diff --git a/PackagesList.cmake b/PackagesList.cmake index 4a44aafd4eb9..08dc43e67ae0 100644 --- a/PackagesList.cmake +++ b/PackagesList.cmake @@ -112,6 +112,7 @@ TRIBITS_REPOSITORY_DEFINE_PACKAGES( Compadre packages/compadre ST STK packages/stk PT # Depends on boost Percept packages/percept PT # Depends on boost + Krino packages/krino PT # Depends on boost SCORECapf_zoltan SCOREC/zoltan ST SCORECapf_stk SCOREC/stk ST SCORECma SCOREC/ma ST diff --git a/packages/belos/CMakeLists.txt b/packages/belos/CMakeLists.txt index 3f82f7e5de80..1e6b02c137dc 100644 --- a/packages/belos/CMakeLists.txt +++ b/packages/belos/CMakeLists.txt @@ -112,6 +112,10 @@ IF (Belos_ENABLE_Tpetra) ADD_SUBDIRECTORY(tpetra) ENDIF() +IF (Belos_ENABLE_KokkosKernels) + ADD_SUBDIRECTORY(kokkos) +ENDIF() + IF (Belos_ENABLE_Xpetra) ADD_SUBDIRECTORY(xpetra) ENDIF() diff --git a/packages/belos/cmake/Belos_config.h.in b/packages/belos/cmake/Belos_config.h.in index 58033a25f870..a0e404b5ff0c 100644 --- a/packages/belos/cmake/Belos_config.h.in +++ b/packages/belos/cmake/Belos_config.h.in @@ -13,6 +13,9 @@ /* Define if want to build with EpetraExt enabled */ #cmakedefine HAVE_BELOS_EPETRAEXT +/* Define if want to build with KokkosKernels enabled */ +#cmakedefine HAVE_BELOS_KOKKOSKERNELS + /* Define if want to build with ML enabled */ #cmakedefine HAVE_BELOS_ML diff --git a/packages/belos/cmake/Dependencies.cmake b/packages/belos/cmake/Dependencies.cmake index 532176ceebff..ea5f6a8a3df6 100644 --- a/packages/belos/cmake/Dependencies.cmake +++ b/packages/belos/cmake/Dependencies.cmake @@ -1,5 +1,5 @@ SET(LIB_REQUIRED_DEP_PACKAGES Teuchos) -SET(LIB_OPTIONAL_DEP_PACKAGES Epetra Tpetra Xpetra Thyra AztecOO Triutils) +SET(LIB_OPTIONAL_DEP_PACKAGES Epetra Tpetra Xpetra Thyra AztecOO Triutils KokkosKernels) SET(TEST_REQUIRED_DEP_PACKAGES) SET(TEST_OPTIONAL_DEP_PACKAGES Galeri Triutils EpetraExt Ifpack ML AztecOO) SET(LIB_REQUIRED_DEP_TPLS) diff --git a/packages/belos/kokkos/CMakeLists.txt b/packages/belos/kokkos/CMakeLists.txt new file mode 100644 index 000000000000..635ad11fded6 --- /dev/null +++ b/packages/belos/kokkos/CMakeLists.txt @@ -0,0 +1,4 @@ +ADD_SUBDIRECTORY(src) + +TRIBITS_ADD_TEST_DIRECTORIES(test) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(example) diff --git a/packages/belos/kokkos/example/BlockCGKokkosExFile.cpp b/packages/belos/kokkos/example/BlockCGKokkosExFile.cpp new file mode 100644 index 000000000000..47f7887ee3b4 --- /dev/null +++ b/packages/belos/kokkos/example/BlockCGKokkosExFile.cpp @@ -0,0 +1,215 @@ +//@HEADER +// ************************************************************************ +// +// Belos: Block Linear Solvers Package +// Copyright 2004 Sandia Corporation +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Michael A. Heroux (maherou@sandia.gov) +// +// ************************************************************************ +//@HEADER +// +// This driver reads a problem from a file, which must be in Matrix Market (*.mtx). +// The problem right-hand side will be generated randomly. +// +// NOTE: No preconditioner is used in this example. +// +#include "BelosConfigDefs.hpp" +#include "BelosLinearProblem.hpp" +#include "BelosBlockCGSolMgr.hpp" +#include "BelosOutputManager.hpp" + +#include "Teuchos_CommandLineProcessor.hpp" +#include "Teuchos_ParameterList.hpp" +#include "Teuchos_StandardCatchMacros.hpp" + +#include "BelosKokkosAdapter.hpp" +#include "KokkosKernels_IOUtils.hpp" +#ifdef HAVE_MPI + #include +#endif + +int main(int argc, char *argv[]) { + +#ifdef HAVE_MPI + MPI_Init(&argc,&argv); +#endif + +bool success = true; + Kokkos::initialize(); + { + + typedef double ST; + typedef int OT; + typedef Kokkos::DefaultExecutionSpace EXSP; + typedef Teuchos::ScalarTraits SCT; + typedef SCT::magnitudeType MT; + typedef Belos::KokkosMultiVec MV; + typedef Belos::KokkosCrsOperator OP; + typedef Belos::MultiVec KMV; + typedef Belos::Operator KOP; + typedef Belos::MultiVecTraits MVT; + typedef Belos::OperatorTraits OPT; + + using Teuchos::ParameterList; + using Teuchos::RCP; + using Teuchos::rcp; + using Teuchos::rcpFromRef; + +bool verbose = true; +try { + int frequency = 25; // frequency of status test output. + int numrhs = 1; // number of right-hand sides to solve for + int maxiters = -1; // maximum number of iterations allowed per linear system + bool expresidual = false; // use explicit residual + std::string filename("bcsstk12.mtx"); // example matrix + MT tol = 1.0e-5; // relative residual tolerance + + Teuchos::CommandLineProcessor cmdp(false,true); + cmdp.setOption("verbose","quiet",&verbose,"Print messages and results."); + cmdp.setOption("expres","impres",&expresidual,"Use explicit residual throughout."); + cmdp.setOption("frequency",&frequency,"Solvers frequency for printing residuals (#iters)."); + cmdp.setOption("filename",&filename,"Filename for test matrix. Acceptable file extensions: *.hb,*.mtx,*.triU,*.triS"); + cmdp.setOption("tol",&tol,"Relative residual tolerance used by Gmres solver."); + cmdp.setOption("num-rhs",&numrhs,"Number of right-hand sides to be solved for."); + cmdp.setOption("max-iters",&maxiters,"Maximum number of iterations per linear system (-1 = adapted to problem/block size)."); + + if (cmdp.parse(argc,argv) != Teuchos::CommandLineProcessor::PARSE_SUCCESSFUL) { + return -1; + } + if (!verbose) + frequency = -1; // reset frequency if test is not verbose + + // Read in a matrix Market file and use it to test the Kokkos Operator. + KokkosSparse::CrsMatrix crsMat = + KokkosKernels::Impl::read_kokkos_crst_matrix>(filename.c_str()); + RCP> A = + rcp(new Belos::KokkosCrsOperator(crsMat)); + OT numRows = crsMat.numRows(); + + Teuchos::RCP X = Teuchos::rcp( new MV(numRows, numrhs) ); + X->MvRandom(); + Teuchos::RCP B = Teuchos::rcp( new MV(numRows, numrhs) ); + OPT::Apply(*A,*X,*B); + X->MvInit(0.0); + + // + // ********Other information used by block solver*********** + // *****************(can be user specified)****************** + // + const int NumGlobalElements = B->GetGlobalLength(); + if (maxiters == -1) + maxiters = NumGlobalElements - 1; // maximum number of iterations to run + + ParameterList belosList; + belosList.set( "Maximum Iterations", maxiters ); // Maximum number of iterations allowed + belosList.set( "Convergence Tolerance", tol ); // Relative convergence tolerance requested + belosList.set( "Explicit Residual Test", expresidual); // use explicit residual + + if (verbose) { + belosList.set( "Verbosity", Belos::Errors + Belos::Warnings + + Belos::StatusTestDetails + Belos::FinalSummary + Belos::TimingDetails); + if (frequency > 0) + belosList.set( "Output Frequency", frequency ); + } + else + belosList.set( "Verbosity", Belos::Errors + Belos::Warnings ); + + // + // Construct an unpreconditioned linear problem instance. + // + Belos::LinearProblem problem( A, X, B ); + bool set = problem.setProblem(); + if (set == false) { + std::cout << std::endl << "ERROR: Belos::LinearProblem failed to set up correctly!" << std::endl; + return -1; + } + // + // ******************************************************************* + // **************Start the block CG iteration************************* + // ******************************************************************* + // + // Create an iterative solver manager. + RCP< Belos::SolverManager > newSolver + = rcp( new Belos::BlockCGSolMgr(rcpFromRef(problem), rcpFromRef(belosList)) ); + + // + // **********Print out information about problem******************* + // + std::cout << std::endl << std::endl; + std::cout << "Dimension of matrix: " << NumGlobalElements << std::endl; + std::cout << "Number of right-hand sides: " << numrhs << std::endl; + std::cout << "Max number of Gmres iterations: " << maxiters << std::endl; + std::cout << "Relative residual tolerance: " << tol << std::endl; + std::cout << std::endl; + // + // Perform solve + // + Belos::ReturnType ret; + ret = newSolver->solve(); + + // + // Compute actual residuals. + // + bool badRes = false; + std::vector actual_resids( numrhs ); + std::vector rhs_norm( numrhs ); + MV resid(numRows, numrhs); + OPT::Apply( *A, *X, resid ); + MVT::MvAddMv( -1.0, resid, 1.0, *B, resid ); + MVT::MvNorm( resid, actual_resids ); + MVT::MvNorm( *B, rhs_norm ); + std::cout<< "---------- Actual Residuals (normalized) ----------"< tol) badRes = true; + } + + if (ret!=Belos::Converged || badRes) { + success = false; + std::cout << std::endl << "ERROR: Belos did not converge!" << std::endl; + } else { + success = true; + std::cout << std::endl << "SUCCESS: Belos converged!" << std::endl; + } + + } + TEUCHOS_STANDARD_CATCH_STATEMENTS(verbose, std::cerr, success); + } + Kokkos::finalize(); +#ifdef HAVE_MPI + MPI_Finalize(); +#endif + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/packages/belos/kokkos/example/BlockGmresKokkosExFile.cpp b/packages/belos/kokkos/example/BlockGmresKokkosExFile.cpp new file mode 100644 index 000000000000..0b7a969128a5 --- /dev/null +++ b/packages/belos/kokkos/example/BlockGmresKokkosExFile.cpp @@ -0,0 +1,223 @@ +//@HEADER +// ************************************************************************ +// +// Belos: Block Linear Solvers Package +// Copyright 2004 Sandia Corporation +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Michael A. Heroux (maherou@sandia.gov) +// +// ************************************************************************ +//@HEADER +// +// This driver reads a problem from a file, which must be in Matrix Market (*.mtx). +// The problem right-hand side will be generated randomly. +// +// NOTE: No preconditioner is used in this example. +// +#include "BelosConfigDefs.hpp" +#include "BelosLinearProblem.hpp" +#include "BelosBlockGmresSolMgr.hpp" +#include "BelosOutputManager.hpp" + +#include "Teuchos_CommandLineProcessor.hpp" +#include "Teuchos_ParameterList.hpp" +#include "Teuchos_StandardCatchMacros.hpp" + +#include "BelosKokkosAdapter.hpp" +#include "KokkosKernels_IOUtils.hpp" +#ifdef HAVE_MPI + #include +#endif + +int main(int argc, char *argv[]) { + +#ifdef HAVE_MPI + MPI_Init(&argc,&argv); +#endif + +bool success = true; + Kokkos::initialize(); + { + + typedef double ST; + typedef int OT; + typedef Kokkos::DefaultExecutionSpace EXSP; + typedef Teuchos::ScalarTraits SCT; + typedef SCT::magnitudeType MT; + typedef Belos::KokkosMultiVec MV; + typedef Belos::KokkosCrsOperator OP; + typedef Belos::MultiVec KMV; + typedef Belos::Operator KOP; + typedef Belos::MultiVecTraits MVT; + typedef Belos::OperatorTraits OPT; + + using Teuchos::ParameterList; + using Teuchos::RCP; + using Teuchos::rcp; + using Teuchos::rcpFromRef; + +bool verbose = true; +try { + int frequency = 25; // frequency of status test output. + int numrhs = 1; // number of right-hand sides to solve for + int maxiters = -1; // maximum number of iterations allowed per linear system + int maxsubspace = 50; // maximum number of blocks the solver can use for the subspace + int maxrestarts = 35; // number of restarts allowed + bool expresidual = false; // use explicit residual + std::string filename("bcsstk12.mtx"); // example matrix + std::string rhsfile(""); + MT tol = 1.0e-5; // relative residual tolerance + + Teuchos::CommandLineProcessor cmdp(false,true); + cmdp.setOption("verbose","quiet",&verbose,"Print messages and results."); + cmdp.setOption("expres","impres",&expresidual,"Use explicit residual throughout."); + cmdp.setOption("frequency",&frequency,"Solvers frequency for printing residuals (#iters)."); + cmdp.setOption("filename",&filename,"Filename for test matrix. Acceptable file extensions: *.hb,*.mtx,*.triU,*.triS"); + cmdp.setOption("rhsfile",&rhsfile,"Filename for right-hand side. (*.mtx file) "); + cmdp.setOption("tol",&tol,"Relative residual tolerance used by Gmres solver."); + cmdp.setOption("num-rhs",&numrhs,"Number of right-hand sides to be solved for."); + cmdp.setOption("max-iters",&maxiters,"Maximum number of iterations per linear system (-1 = adapted to problem/block size)."); + cmdp.setOption("max-subspace",&maxsubspace,"Maximum number of blocks the solver can use for the subspace."); + cmdp.setOption("max-restarts",&maxrestarts,"Maximum number of restarts allowed for GMRES solver."); + + if (cmdp.parse(argc,argv) != Teuchos::CommandLineProcessor::PARSE_SUCCESSFUL) { + return -1; + } + if (!verbose) + frequency = -1; // reset frequency if test is not verbose + + // Read in a matrix Market file and use it to test the Kokkos Operator. + KokkosSparse::CrsMatrix crsMat = + KokkosKernels::Impl::read_kokkos_crst_matrix>(filename.c_str()); + RCP> A = + rcp(new Belos::KokkosCrsOperator(crsMat)); + OT numRows = crsMat.numRows(); + + Teuchos::RCP X = Teuchos::rcp( new MV(numRows, numrhs) ); + X->MvRandom(); + Teuchos::RCP B = Teuchos::rcp( new MV(numRows, numrhs) ); + OPT::Apply(*A,*X,*B); + X->MvInit(0.0); + + // + // ********Other information used by block solver*********** + // *****************(can be user specified)****************** + // + const int NumGlobalElements = B->GetGlobalLength(); + if (maxiters == -1) + maxiters = NumGlobalElements - 1; // maximum number of iterations to run + + ParameterList belosList; + belosList.set( "Maximum Iterations", maxiters ); // Maximum number of iterations allowed + belosList.set( "Convergence Tolerance", tol ); // Relative convergence tolerance requested + belosList.set( "Num Blocks", maxsubspace); // Maximum number of blocks in Krylov factorization + belosList.set( "Maximum Restarts", maxrestarts ); // Maximum number of restarts allowed + belosList.set( "Explicit Residual Test", expresidual); // use explicit residual + + if (verbose) { + belosList.set( "Verbosity", Belos::Errors + Belos::Warnings + + Belos::StatusTestDetails + Belos::FinalSummary + Belos::TimingDetails); + if (frequency > 0) + belosList.set( "Output Frequency", frequency ); + } + else + belosList.set( "Verbosity", Belos::Errors + Belos::Warnings ); + + // + // Construct an unpreconditioned linear problem instance. + // + Belos::LinearProblem problem( A, X, B ); + bool set = problem.setProblem(); + if (set == false) { + std::cout << std::endl << "ERROR: Belos::LinearProblem failed to set up correctly!" << std::endl; + return -1; + } + // + // ******************************************************************* + // **************Start the block Gmres iteration************************* + // ******************************************************************* + // + // Create an iterative solver manager. + RCP< Belos::SolverManager > newSolver + = rcp( new Belos::BlockGmresSolMgr(rcpFromRef(problem), rcpFromRef(belosList)) ); + + // + // **********Print out information about problem******************* + // + std::cout << std::endl << std::endl; + std::cout << "Dimension of matrix: " << NumGlobalElements << std::endl; + std::cout << "Number of right-hand sides: " << numrhs << std::endl; + std::cout << "Max number of Gmres iterations: " << maxiters << std::endl; + std::cout << "Relative residual tolerance: " << tol << std::endl; + std::cout << std::endl; + // + // Perform solve + // + Belos::ReturnType ret; + ret = newSolver->solve(); + + // + // Compute actual residuals. + // + bool badRes = false; + std::vector actual_resids( numrhs ); + std::vector rhs_norm( numrhs ); + MV resid(numRows, numrhs); + OPT::Apply( *A, *X, resid ); + MVT::MvAddMv( -1.0, resid, 1.0, *B, resid ); + MVT::MvNorm( resid, actual_resids ); + MVT::MvNorm( *B, rhs_norm ); + std::cout<< "---------- Actual Residuals (normalized) ----------"< tol) badRes = true; + } + + if (ret!=Belos::Converged || badRes) { + success = false; + std::cout << std::endl << "ERROR: Belos did not converge!" << std::endl; + } else { + success = true; + std::cout << std::endl << "SUCCESS: Belos converged!" << std::endl; + } + + } + TEUCHOS_STANDARD_CATCH_STATEMENTS(verbose, std::cerr, success); + } + Kokkos::finalize(); +#ifdef HAVE_MPI + MPI_Finalize(); +#endif + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/packages/belos/kokkos/example/CMakeLists.txt b/packages/belos/kokkos/example/CMakeLists.txt new file mode 100644 index 000000000000..b493959ea9af --- /dev/null +++ b/packages/belos/kokkos/example/CMakeLists.txt @@ -0,0 +1,23 @@ +INCLUDE_DIRECTORIES(${Belos_SOURCE_DIR}/kokkos/src) + +TRIBITS_ADD_EXECUTABLE_AND_TEST( + Kokkos_BlockGmresEx + SOURCES BlockGmresKokkosExFile.cpp + ARGS "--verbose" + "--verbose --num-rhs=4" + COMM serial mpi + ) + +TRIBITS_ADD_EXECUTABLE_AND_TEST( + Kokkos_BlockCGEx + SOURCES BlockCGKokkosExFile.cpp + ARGS "--verbose" + "--verbose --num-rhs=4" + COMM serial mpi + ) + +TRIBITS_COPY_FILES_TO_BINARY_DIR(CopyExampleMtxFiles + SOURCE_DIR ${Belos_SOURCE_DIR}/kokkos/example + SOURCE_FILES bcsstk12.mtx + EXEDEPS Kokkos_BlockCGEx Kokkos_BlockGmresEx + ) diff --git a/packages/belos/kokkos/example/bcsstk12.mtx b/packages/belos/kokkos/example/bcsstk12.mtx new file mode 100644 index 000000000000..d734671ce1d3 --- /dev/null +++ b/packages/belos/kokkos/example/bcsstk12.mtx @@ -0,0 +1,17874 @@ +%%MatrixMarket matrix coordinate real symmetric +%------------------------------------------------------------------------------- +% UF Sparse Matrix Collection, Tim Davis +% http://www.cise.ufl.edu/research/sparse/matrices/HB/bcsstk12 +% name: HB/bcsstk12 +% [SYMMETRIC STIFFNESS MATRIX, ORE CAR (CONSISTENT MASSES)] +% id: 34 +% date: 1982 +% author: J. Lewis +% ed: I. Duff, R. Grimes, J. Lewis +% fields: title A name id kind notes date author ed +% kind: duplicate structural problem +%------------------------------------------------------------------------------- +% notes: +% duplicate of HB/bcsstk11 +%------------------------------------------------------------------------------- +1473 1473 17857 +1 1 1011851.60912 +2 1 4110093.43272 +3 1 -4.47034835815e-8 +4 1 -311975.890718 +5 1 -1184718.57235 +6 1 9.31322574615e-9 +52 1 22134.9913963 +53 1 90320.9940529 +54 1 -18909.0650259 +55 1 -144050.092155 +56 1 -547025.666412 +57 1 358351.461509 +2 2 16788621.6609 +3 2 -1.9371509552e-7 +4 2 -1184718.57235 +5 2 -4498931.28739 +6 2 2.98023223877e-8 +52 2 90320.9940529 +53 2 370626.696215 +54 2 4631.25243447 +55 2 -547025.666412 +56 2 -2077312.65726 +57 2 1360828.33484 +3 3 18240145.4814 +4 3 1.02445483208e-8 +5 3 3.35276126862e-8 +6 3 758227.34113 +52 3 18909.0650259 +53 3 -4631.25243457 +54 3 -5293544.01962 +55 3 358351.461509 +56 3 1360828.33484 +57 3 -2284273.33935 +4 4 2350262.38096 +5 4 4600302.12491 +6 4 -2.421438694e-8 +7 4 2.98023223877e-8 +8 4 -1.19209289551e-7 +10 4 -413671.243673 +11 4 -1334423.36669 +12 4 1.58324837685e-8 +52 4 -144050.092155 +53 4 -547025.666412 +54 4 -358351.461509 +55 4 -474420.971735 +56 4 131724.797327 +57 4 -23329.9193232 +58 4 800256.904552 +59 4 8988441.97885 +61 4 -193836.559394 +62 4 -625279.223852 +63 4 416676.259817 +5 5 17048556.8253 +6 5 -8.19563865662e-8 +7 5 5.96046447754e-8 +10 5 -1334423.36669 +11 5 -4304591.50545 +12 5 4.842877388e-8 +52 5 -547025.666412 +53 5 -2077312.65726 +54 5 -1360828.33484 +55 5 131724.797327 +56 5 -28174.6046034 +57 5 6684.54707694 +58 5 -6040684.68178 +59 5 -800256.904552 +61 5 -625279.223852 +62 5 -2017029.75436 +63 5 1344116.96715 +6 6 36176122.0894 +10 6 1.30385160446e-8 +11 6 3.72529029846e-8 +12 6 830431.306934 +52 6 -358351.461509 +53 6 -1360828.33484 +54 6 -2284273.33935 +55 6 23329.9193232 +56 6 -6684.54707697 +57 6 -14301137.6219 +61 6 416676.259817 +62 6 1344116.96715 +63 6 -2296979.40748 +7 7 305878308.174 +8 7 66094123.115 +55 7 -800256.904552 +56 7 6040684.68178 +58 7 64525494.457 +59 7 -4237812.99363 +8 8 549336918.275 +55 8 -8988441.97885 +56 8 800256.904552 +58 8 -4237812.99363 +59 8 48915452.1011 +9 9 715555.555556 +60 9 -357777.777778 +10 10 1816739.3315 +11 10 5341279.87864 +12 10 -3.53902578354e-8 +13 10 -544032.43723 +14 10 -1483724.82881 +15 10 2.23517417908e-8 +55 10 -193836.559394 +56 10 -625279.223852 +57 10 -416676.259817 +61 10 24667.0078013 +62 10 73754.1287474 +63 10 -27106.375452 +64 10 -260348.273704 +65 10 -710040.746465 +66 10 484442.198447 +11 11 15814018.062 +12 11 -1.11758708954e-7 +13 11 -1483724.82881 +14 11 -4046522.26039 +15 11 5.96046447754e-8 +55 11 -625279.223852 +56 11 -2017029.75436 +57 11 -1344116.96715 +61 11 73754.1287474 +62 11 222052.369365 +63 11 9164.38855485 +64 11 -710040.746465 +65 11 -1936474.76309 +66 11 1321205.99576 +12 12 18448886.5844 +13 12 2.14204192162e-8 +14 12 5.58793544769e-8 +15 12 931107.693647 +55 12 -416676.259817 +56 12 -1344116.96715 +57 12 -2296979.40748 +61 12 27106.375452 +62 12 -9164.3885549 +63 12 -5492991.14412 +64 12 484442.198447 +65 12 1321205.99576 +66 12 -2315242.24089 +13 13 2558539.62181 +14 13 6138307.60949 +15 13 -5.21540641785e-8 +16 13 -759692.612592 +17 13 -1651505.67955 +18 13 2.79396772385e-8 +61 13 -260348.273704 +62 13 -710040.746465 +63 13 -484442.198447 +64 13 12227.6197723 +65 13 33038.3525623 +66 13 -41457.2556898 +67 13 -379286.632065 +68 13 -824536.156663 +69 13 588085.337671 +14 14 14915778.6682 +15 14 -1.26659870148e-7 +16 14 -1651505.67955 +17 14 -3590229.73815 +18 14 6.14672899246e-8 +61 14 -710040.746465 +62 14 -1936474.76309 +63 14 -1321205.99576 +64 14 33038.3525623 +65 14 89431.3340662 +66 14 17103.8438088 +67 14 -824536.156663 +68 14 -1792469.90579 +69 14 1278446.38624 +15 15 18669379.7869 +16 15 2.88709998131e-8 +17 15 6.14672899246e-8 +18 15 1124555.53479 +61 15 -484442.198447 +62 15 -1321205.99576 +63 15 -2315242.24089 +64 15 41457.2556898 +65 15 -17103.8438089 +66 15 -5695176.55864 +67 15 588085.337671 +68 15 1278446.38624 +69 15 -2352102.70813 +16 16 3880798.16119 +17 16 7110485.96967 +18 16 -4.47034835815e-8 +19 16 -1076372.38828 +20 16 -1736084.49722 +21 16 2.60770320892e-8 +64 16 -379286.632065 +65 16 -824536.156663 +66 16 -588085.337671 +67 16 -52167.0393845 +68 16 -83826.4031562 +69 16 -61373.985991 +70 16 -590912.90871 +71 16 -953085.336629 +72 16 741520.302649 +17 17 13318817.2723 +18 17 -8.94069671631e-8 +19 17 -1736084.49722 +20 17 -2800136.28584 +21 17 4.28408384323e-8 +64 17 -824536.156663 +65 17 -1792469.90579 +66 17 -1278446.38624 +67 17 -83826.4031562 +68 17 -134521.304433 +69 17 32978.3592395 +70 17 -953085.336629 +71 17 -1537234.41392 +72 17 1196000.48814 +18 18 19146980.3477 +19 18 1.86264514923e-8 +20 18 3.35276126862e-8 +21 18 1520483.39551 +64 18 -588085.337671 +65 18 -1278446.38624 +66 18 -2352102.70813 +67 18 61373.985991 +68 18 -32978.3592395 +69 18 -6109264.54972 +70 18 741520.302649 +71 18 1196000.48814 +72 18 -2434642.38116 +19 19 6516876.42304 +20 19 8531914.96891 +21 19 -2.23517417908e-8 +22 19 -2626121.17726 +23 19 -2984228.61052 +24 19 9.31322574615e-9 +67 19 -590912.90871 +68 19 -953085.336629 +69 19 -741520.302649 +70 19 222027.67781 +71 19 227177.812693 +72 19 -75251.5247453 +73 19 -1038306.19785 +74 19 -1179893.40665 +75 19 929649.114512 +20 20 11512089.4984 +21 20 -3.72529029846e-8 +22 20 -2984228.61052 +23 20 -3391168.87559 +24 20 1.11758708954e-8 +67 20 -953085.336629 +68 20 -1537234.41392 +69 20 -1196000.48814 +70 20 227177.812693 +71 20 217630.207531 +72 20 55832.4159336 +73 20 -1179893.40665 +74 20 -1340787.9621 +75 20 1056419.44831 +21 21 18361731.7736 +22 21 1.86264514923e-9 +23 21 1.86264514923e-9 +24 21 -125380.553907 +67 21 -741520.302649 +68 21 -1196000.48814 +69 21 -2434642.38116 +70 21 75251.5247453 +71 21 -55832.4159336 +72 21 -5287984.36193 +73 21 929649.114512 +74 21 1056419.44831 +75 21 -2155790.5645 +22 22 11197028.9194 +23 22 7624235.1212 +24 22 -1.49011611938e-8 +25 22 -1154846.00933 +26 22 -476225.158488 +27 22 9.31322574615e-9 +70 22 -1038306.19785 +71 22 -1179893.40665 +72 22 -929649.114512 +73 22 -908773.635179 +74 22 -175831.894858 +75 22 -148519.569575 +76 22 -1760951.03338 +77 22 -726165.37459 +78 22 1300948.03845 +23 23 6560950.4012 +24 23 -7.45058059692e-9 +25 23 -476225.158488 +26 23 -196381.508655 +27 23 3.72529029846e-9 +70 23 -1179893.40665 +71 23 -1340787.9621 +72 23 -1056419.44831 +73 23 -175831.894858 +74 23 153537.59263 +75 23 207978.412157 +76 23 -726165.37459 +77 23 -299449.639006 +78 23 536473.417918 +24 24 21281133.3311 +25 24 3.72529029846e-9 +26 24 9.31322574615e-10 +27 24 4075797.18655 +70 24 -929649.114512 +71 24 -1056419.44831 +72 24 -2155790.5645 +73 24 148519.569575 +74 24 -207978.412157 +75 24 -7295491.64648 +76 24 1300948.03845 +77 24 536473.417918 +78 24 -3164492.7709 +25 25 14087608.2601 +26 25 -2.23517417908e-8 +27 25 -7.45058059692e-9 +28 25 -1154846.00933 +29 25 476225.158488 +30 25 9.31322574615e-9 +73 25 -1760951.03338 +74 25 -726165.37459 +75 25 -1300948.03845 +76 25 -2367056.05396 +77 25 1.11758708954e-8 +78 25 -1.86264514923e-9 +79 25 -1760951.03338 +80 25 726165.37459 +81 25 1300948.03845 +26 26 2395597.11087 +28 26 476225.158488 +29 26 -196381.508655 +30 26 -3.72529029846e-9 +73 26 -726165.37459 +74 26 -299449.639006 +75 26 -536473.417918 +76 26 9.31322574615e-9 +77 26 -402517.768767 +78 26 429178.734334 +79 26 726165.37459 +80 26 -299449.639006 +81 26 -536473.417918 +27 27 25315942.1547 +28 27 3.72529029846e-9 +29 27 -9.31322574615e-10 +30 27 4075797.18655 +73 27 -1300948.03845 +74 27 -536473.417918 +75 27 -3164492.7709 +76 27 1.86264514923e-9 +77 27 -429178.734334 +78 27 -10404782.7221 +79 27 1300948.03845 +80 27 -536473.417918 +81 27 -3164492.7709 +28 28 11197028.9194 +29 28 -7624235.1212 +30 28 1.49011611938e-8 +31 28 -2626121.17726 +32 28 2984228.61052 +33 28 -1.11758708954e-8 +76 28 -1760951.03338 +77 28 726165.37459 +78 28 -1300948.03845 +79 28 -908773.635179 +80 28 175831.894858 +81 28 148519.569575 +82 28 -1038306.19785 +83 28 1179893.40665 +84 28 929649.114512 +29 29 6560950.4012 +30 29 -2.23517417908e-8 +31 29 2984228.61052 +32 29 -3391168.87559 +33 29 1.11758708954e-8 +76 29 726165.37459 +77 29 -299449.639006 +78 29 536473.417918 +79 29 175831.894858 +80 29 153537.59263 +81 29 207978.412157 +82 29 1179893.40665 +83 29 -1340787.9621 +84 29 -1056419.44831 +30 30 21281133.3311 +31 30 -1.49011611938e-8 +32 30 1.67638063431e-8 +33 30 -125380.553907 +76 30 -1300948.03845 +77 30 536473.417918 +78 30 -3164492.7709 +79 30 -148519.569575 +80 30 -207978.412157 +81 30 -7295491.64648 +82 30 929649.114512 +83 30 -1056419.44831 +84 30 -2155790.5645 +31 31 6516876.42304 +32 31 -8531914.96891 +33 31 2.60770320892e-8 +34 31 -1076372.38828 +35 31 1736084.49722 +36 31 -1.30385160446e-8 +79 31 -1038306.19785 +80 31 1179893.40665 +81 31 -929649.114512 +82 31 222027.67781 +83 31 -227177.812693 +84 31 75251.5247453 +85 31 -590912.90871 +86 31 953085.336629 +87 31 741520.302649 +32 32 11512089.4984 +33 32 -3.72529029846e-8 +34 32 1736084.49722 +35 32 -2800136.28584 +36 32 2.04890966415e-8 +79 32 1179893.40665 +80 32 -1340787.9621 +81 32 1056419.44831 +82 32 -227177.812693 +83 32 217630.207531 +84 32 55832.4159336 +85 32 953085.336629 +86 32 -1537234.41392 +87 32 -1196000.48814 +33 33 18361731.7736 +34 33 -1.67638063431e-8 +35 33 2.79396772385e-8 +36 33 1520483.39551 +79 33 -929649.114512 +80 33 1056419.44831 +81 33 -2155790.5645 +82 33 -75251.5247453 +83 33 -55832.4159336 +84 33 -5287984.36193 +85 33 741520.302649 +86 33 -1196000.48814 +87 33 -2434642.38116 +34 34 3880798.16119 +35 34 -7110485.96967 +36 34 2.60770320892e-8 +37 34 -759692.612592 +38 34 1651505.67955 +39 34 -1.67638063431e-8 +82 34 -590912.90871 +83 34 953085.336629 +84 34 -741520.302649 +85 34 -52167.0393845 +86 34 83826.4031562 +87 34 61373.985991 +88 34 -379286.632065 +89 34 824536.156663 +90 34 588085.337671 +35 35 13318817.2723 +36 35 -4.47034835815e-8 +37 35 1651505.67955 +38 35 -3590229.73815 +39 35 3.72529029846e-8 +82 35 953085.336629 +83 35 -1537234.41392 +84 35 1196000.48814 +85 35 83826.4031562 +86 35 -134521.304433 +87 35 32978.3592395 +88 35 824536.156663 +89 35 -1792469.90579 +90 35 -1278446.38624 +36 36 19146980.3477 +37 36 -2.14204192162e-8 +38 36 4.842877388e-8 +39 36 1124555.53479 +82 36 -741520.302649 +83 36 1196000.48814 +84 36 -2434642.38116 +85 36 -61373.985991 +86 36 -32978.3592395 +87 36 -6109264.54972 +88 36 588085.337671 +89 36 -1278446.38624 +90 36 -2352102.70813 +37 37 2558539.62181 +38 37 -6138307.60949 +39 37 4.28408384323e-8 +40 37 -544032.43723 +41 37 1483724.82881 +42 37 -2.421438694e-8 +85 37 -379286.632065 +86 37 824536.156663 +87 37 -588085.337671 +88 37 12227.6197723 +89 37 -33038.3525623 +90 37 41457.2556898 +91 37 -260348.273704 +92 37 710040.746465 +93 37 484442.198447 +38 38 14915778.6682 +39 38 -1.19209289551e-7 +40 38 1483724.82881 +41 38 -4046522.26039 +42 38 7.07805156708e-8 +85 38 824536.156663 +86 38 -1792469.90579 +87 38 1278446.38624 +88 38 -33038.3525623 +89 38 89431.3340662 +90 38 17103.8438088 +91 38 710040.746465 +92 38 -1936474.76309 +93 38 -1321205.99576 +39 39 18669379.7869 +40 39 -2.60770320892e-8 +41 39 7.45058059692e-8 +42 39 931107.693647 +85 39 -588085.337671 +86 39 1278446.38624 +87 39 -2352102.70813 +88 39 -41457.2556898 +89 39 -17103.8438089 +90 39 -5695176.55864 +91 39 484442.198447 +92 39 -1321205.99576 +93 39 -2315242.24089 +40 40 1816739.3315 +41 40 -5341279.87864 +42 40 3.91155481339e-8 +43 40 -413671.243673 +44 40 1334423.36669 +45 40 -8.38190317154e-9 +88 40 -260348.273704 +89 40 710040.746465 +90 40 -484442.198447 +91 40 24667.0078013 +92 40 -73754.1287475 +93 40 27106.375452 +94 40 -193836.559394 +95 40 625279.223852 +96 40 416676.259817 +41 41 15814018.062 +42 41 -1.19209289551e-7 +43 41 1334423.36669 +44 41 -4304591.50545 +45 41 2.98023223877e-8 +88 41 710040.746465 +89 41 -1936474.76309 +90 41 1321205.99576 +91 41 -73754.1287475 +92 41 222052.369365 +93 41 9164.38855486 +94 41 625279.223852 +95 41 -2017029.75436 +96 41 -1344116.96715 +42 42 18448886.5844 +43 42 -1.11758708954e-8 +44 42 3.72529029846e-8 +45 42 830431.306934 +88 42 -484442.198447 +89 42 1321205.99576 +90 42 -2315242.24089 +91 42 -27106.375452 +92 42 -9164.38855491 +93 42 -5492991.14412 +94 42 416676.259817 +95 42 -1344116.96715 +96 42 -2296979.40748 +43 43 2350262.78049 +44 43 -4600302.86075 +45 43 2.04890966415e-8 +46 43 -7.45058059692e-9 +49 43 -311975.890718 +50 43 1184718.57235 +51 43 -1.11758708954e-8 +91 43 -193836.559394 +92 43 625279.223852 +93 43 -416676.259817 +94 43 -474421.1715 +95 43 -131724.429407 +96 43 23329.9193232 +97 43 -800250.281991 +98 43 8988445.57462 +100 43 -144050.092155 +101 43 547025.666412 +102 43 358351.461509 +44 44 17048556.4258 +45 44 -7.45058059692e-8 +46 44 5.96046447754e-8 +47 44 7.45058059692e-9 +49 44 1184718.57235 +50 44 -4498931.28739 +51 44 3.72529029846e-8 +91 44 625279.223852 +92 44 -2017029.75436 +93 44 1344116.96715 +94 44 -131724.429407 +95 44 -28174.4048386 +96 44 6684.54707695 +97 44 -6040681.08601 +98 44 800250.281991 +100 44 547025.666412 +101 44 -2077312.65726 +102 44 -1360828.33484 +45 45 36176122.0894 +49 45 -1.39698386192e-8 +50 45 4.842877388e-8 +51 45 758227.34113 +91 45 -416676.259817 +92 45 1344116.96715 +93 45 -2296979.40748 +94 45 -23329.9193232 +95 45 -6684.54707698 +96 45 -14301137.6219 +100 45 358351.461509 +101 45 -1360828.33484 +102 45 -2284273.33935 +46 46 305878011.196 +47 46 -66093576.1502 +94 46 800250.281991 +95 46 6040681.08601 +97 46 64525513.4986 +98 46 4237777.92342 +47 47 549337215.254 +94 47 -8988445.57462 +95 47 -800250.281991 +97 47 4237777.92342 +98 47 48915433.0595 +48 48 715555.555556 +99 48 -357777.777778 +49 49 1011851.60912 +50 49 -4110093.43272 +51 49 5.96046447754e-8 +94 49 -144050.092155 +95 49 547025.666412 +96 49 -358351.461509 +100 49 22134.9913963 +101 49 -90320.9940529 +102 49 18909.0650259 +50 50 16788621.6609 +51 50 -2.45869159698e-7 +94 50 547025.666412 +95 50 -2077312.65726 +96 50 1360828.33484 +100 50 -90320.9940529 +101 50 370626.696215 +102 50 4631.25243447 +51 51 18240145.4814 +94 51 -358351.461509 +95 51 1360828.33484 +96 51 -2284273.33935 +100 51 -18909.0650259 +101 51 -4631.25243458 +102 51 -5293544.01962 +52 52 1011851.60912 +53 52 4110093.43272 +54 52 -4.47034835815e-8 +55 52 -311975.890718 +56 52 -1184718.57235 +57 52 1.02445483208e-8 +103 52 22134.9913963 +104 52 90320.9940529 +105 52 -18909.0650259 +106 52 -144050.092155 +107 52 -547025.666412 +108 52 358351.461509 +53 53 16788621.6609 +54 53 -1.9371509552e-7 +55 53 -1184718.57235 +56 53 -4498931.28739 +57 53 3.35276126862e-8 +103 53 90320.9940529 +104 53 370626.696215 +105 53 4631.25243447 +106 53 -547025.666412 +107 53 -2077312.65726 +108 53 1360828.33484 +54 54 18240145.4814 +55 54 9.31322574615e-9 +56 54 2.98023223877e-8 +57 54 758227.34113 +103 54 18909.0650259 +104 54 -4631.25243457 +105 54 -5293544.01962 +106 54 358351.461509 +107 54 1360828.33484 +108 54 -2284273.33935 +55 55 2350262.38096 +56 55 4600302.12491 +57 55 -2.60770320892e-8 +58 55 2.98023223877e-8 +59 55 -1.19209289551e-7 +61 55 -413671.243673 +62 55 -1334423.36669 +63 55 1.58324837685e-8 +103 55 -144050.092155 +104 55 -547025.666412 +105 55 -358351.461509 +106 55 -474420.971735 +107 55 131724.797327 +108 55 -23329.9193232 +109 55 800256.904552 +110 55 8988441.97885 +112 55 -193836.559394 +113 55 -625279.223852 +114 55 416676.259817 +56 56 17048556.8253 +57 56 -8.94069671631e-8 +58 56 5.96046447754e-8 +61 56 -1334423.36669 +62 56 -4304591.50545 +63 56 4.842877388e-8 +103 56 -547025.666412 +104 56 -2077312.65726 +105 56 -1360828.33484 +106 56 131724.797327 +107 56 -28174.6046033 +108 56 6684.54707694 +109 56 -6040684.68178 +110 56 -800256.904552 +112 56 -625279.223852 +113 56 -2017029.75436 +114 56 1344116.96715 +57 57 36176122.0894 +61 57 1.30385160446e-8 +62 57 3.72529029846e-8 +63 57 830431.306934 +103 57 -358351.461509 +104 57 -1360828.33484 +105 57 -2284273.33935 +106 57 23329.9193232 +107 57 -6684.54707697 +108 57 -14301137.6219 +112 57 416676.259817 +113 57 1344116.96715 +114 57 -2296979.40748 +58 58 305878308.174 +59 58 66094123.115 +106 58 -800256.904552 +107 58 6040684.68178 +109 58 64525494.457 +110 58 -4237812.99363 +59 59 549336918.275 +106 59 -8988441.97885 +107 59 800256.904552 +109 59 -4237812.99363 +110 59 48915452.1011 +60 60 715555.555556 +111 60 -357777.777778 +61 61 1816739.3315 +62 61 5341279.87864 +63 61 -3.53902578354e-8 +64 61 -544032.43723 +65 61 -1483724.82881 +66 61 2.23517417908e-8 +106 61 -193836.559394 +107 61 -625279.223852 +108 61 -416676.259817 +112 61 24667.0078013 +113 61 73754.1287475 +114 61 -27106.375452 +115 61 -260348.273704 +116 61 -710040.746465 +117 61 484442.198447 +62 62 15814018.062 +63 62 -1.11758708954e-7 +64 62 -1483724.82881 +65 62 -4046522.26039 +66 62 5.96046447754e-8 +106 62 -625279.223852 +107 62 -2017029.75436 +108 62 -1344116.96715 +112 62 73754.1287475 +113 62 222052.369365 +114 62 9164.38855485 +115 62 -710040.746465 +116 62 -1936474.76309 +117 62 1321205.99576 +63 63 18448886.5844 +64 63 1.86264514923e-8 +65 63 4.842877388e-8 +66 63 931107.693647 +106 63 -416676.259817 +107 63 -1344116.96715 +108 63 -2296979.40748 +112 63 27106.375452 +113 63 -9164.38855491 +114 63 -5492991.14412 +115 63 484442.198447 +116 63 1321205.99576 +117 63 -2315242.24089 +64 64 2558539.62181 +65 64 6138307.60949 +66 64 -4.842877388e-8 +67 64 -759692.612592 +68 64 -1651505.67955 +69 64 2.98023223877e-8 +112 64 -260348.273704 +113 64 -710040.746465 +114 64 -484442.198447 +115 64 12227.6197723 +116 64 33038.3525623 +117 64 -41457.2556898 +118 64 -379286.632065 +119 64 -824536.156663 +120 64 588085.337671 +65 65 14915778.6682 +66 65 -1.19209289551e-7 +67 65 -1651505.67955 +68 65 -3590229.73815 +69 65 6.33299350738e-8 +112 65 -710040.746465 +113 65 -1936474.76309 +114 65 -1321205.99576 +115 65 33038.3525623 +116 65 89431.3340662 +117 65 17103.8438088 +118 65 -824536.156663 +119 65 -1792469.90579 +120 65 1278446.38624 +66 66 18669379.7869 +67 66 2.70083546639e-8 +68 66 5.77419996262e-8 +69 66 1124555.53479 +112 66 -484442.198447 +113 66 -1321205.99576 +114 66 -2315242.24089 +115 66 41457.2556898 +116 66 -17103.8438089 +117 66 -5695176.55864 +118 66 588085.337671 +119 66 1278446.38624 +120 66 -2352102.70813 +67 67 3880798.16119 +68 67 7110485.96967 +69 67 -4.09781932831e-8 +70 67 -1076372.38828 +71 67 -1736084.49722 +72 67 2.23517417908e-8 +115 67 -379286.632065 +116 67 -824536.156663 +117 67 -588085.337671 +118 67 -52167.0393845 +119 67 -83826.4031562 +120 67 -61373.985991 +121 67 -590912.90871 +122 67 -953085.336629 +123 67 741520.302649 +68 68 13318817.2723 +69 68 -8.19563865662e-8 +70 68 -1736084.49722 +71 68 -2800136.28584 +72 68 3.72529029846e-8 +115 68 -824536.156663 +116 68 -1792469.90579 +117 68 -1278446.38624 +118 68 -83826.4031562 +119 68 -134521.304433 +120 68 32978.3592395 +121 68 -953085.336629 +122 68 -1537234.41392 +123 68 1196000.48814 +69 69 19146980.3477 +70 69 1.67638063431e-8 +71 69 2.98023223877e-8 +72 69 1520483.39551 +115 69 -588085.337671 +116 69 -1278446.38624 +117 69 -2352102.70813 +118 69 61373.985991 +119 69 -32978.3592395 +120 69 -6109264.54972 +121 69 741520.302649 +122 69 1196000.48814 +123 69 -2434642.38116 +70 70 6516876.42304 +71 70 8531914.96891 +72 70 -2.60770320892e-8 +73 70 -2626121.17726 +74 70 -2984228.61052 +75 70 1.11758708954e-8 +118 70 -590912.90871 +119 70 -953085.336629 +120 70 -741520.302649 +121 70 222027.67781 +122 70 227177.812693 +123 70 -75251.5247453 +124 70 -1038306.19785 +125 70 -1179893.40665 +126 70 929649.114512 +71 71 11512089.4984 +72 71 -3.72529029846e-8 +73 71 -2984228.61052 +74 71 -3391168.87559 +75 71 1.49011611938e-8 +118 71 -953085.336629 +119 71 -1537234.41392 +120 71 -1196000.48814 +121 71 227177.812693 +122 71 217630.207531 +123 71 55832.4159336 +124 71 -1179893.40665 +125 71 -1340787.9621 +126 71 1056419.44831 +72 72 18361731.7736 +73 72 1.86264514923e-9 +74 72 1.86264514923e-9 +75 72 -125380.553907 +118 72 -741520.302649 +119 72 -1196000.48814 +120 72 -2434642.38116 +121 72 75251.5247453 +122 72 -55832.4159336 +123 72 -5287984.36193 +124 72 929649.114512 +125 72 1056419.44831 +126 72 -2155790.5645 +73 73 11197028.9194 +74 73 7624235.1212 +76 73 -1154846.00933 +77 73 -476225.158488 +121 73 -1038306.19785 +122 73 -1179893.40665 +123 73 -929649.114512 +124 73 -908773.635179 +125 73 -175831.894858 +126 73 -148519.569575 +127 73 -1760951.03338 +128 73 -726165.37459 +129 73 1300948.03845 +74 74 6560950.4012 +75 74 -7.45058059692e-9 +76 74 -476225.158488 +77 74 -196381.508655 +121 74 -1179893.40665 +122 74 -1340787.9621 +123 74 -1056419.44831 +124 74 -175831.894858 +125 74 153537.59263 +126 74 207978.412157 +127 74 -726165.37459 +128 74 -299449.639006 +129 74 536473.417918 +75 75 21281133.3311 +76 75 -7.45058059692e-9 +77 75 -2.79396772385e-9 +78 75 4075797.18655 +121 75 -929649.114512 +122 75 -1056419.44831 +123 75 -2155790.5645 +124 75 148519.569575 +125 75 -207978.412157 +126 75 -7295491.64648 +127 75 1300948.03845 +128 75 536473.417918 +129 75 -3164492.7709 +76 76 14087608.2601 +77 76 -7.45058059692e-9 +78 76 7.45058059692e-9 +79 76 -1154846.00933 +80 76 476225.158488 +124 76 -1760951.03338 +125 76 -726165.37459 +126 76 -1300948.03845 +127 76 -2367056.05396 +128 76 1.11758708954e-8 +129 76 1.86264514923e-9 +130 76 -1760951.03338 +131 76 726165.37459 +132 76 1300948.03845 +77 77 2395597.11087 +78 77 7.45058059692e-9 +79 77 476225.158488 +80 77 -196381.508655 +124 77 -726165.37459 +125 77 -299449.639006 +126 77 -536473.417918 +127 77 9.31322574615e-9 +128 77 -402517.768767 +129 77 429178.734334 +130 77 726165.37459 +131 77 -299449.639006 +132 77 -536473.417918 +78 78 25315942.1547 +79 78 -7.45058059692e-9 +80 78 2.79396772385e-9 +81 78 4075797.18655 +124 78 -1300948.03845 +125 78 -536473.417918 +126 78 -3164492.7709 +128 78 -429178.734334 +129 78 -10404782.7221 +130 78 1300948.03845 +131 78 -536473.417918 +132 78 -3164492.7709 +79 79 11197028.9194 +80 79 -7624235.1212 +81 79 3.72529029846e-9 +82 79 -2626121.17726 +83 79 2984228.61052 +84 79 -5.58793544769e-9 +127 79 -1760951.03338 +128 79 726165.37459 +129 79 -1300948.03845 +130 79 -908773.635179 +131 79 175831.894858 +132 79 148519.569575 +133 79 -1038306.19785 +134 79 1179893.40665 +135 79 929649.114512 +80 80 6560950.4012 +82 80 2984228.61052 +83 80 -3391168.87559 +84 80 5.58793544769e-9 +127 80 726165.37459 +128 80 -299449.639006 +129 80 536473.417918 +130 80 175831.894858 +131 80 153537.59263 +132 80 207978.412157 +133 80 1179893.40665 +134 80 -1340787.9621 +135 80 -1056419.44831 +81 81 21281133.3311 +82 81 -1.30385160446e-8 +83 81 1.30385160446e-8 +84 81 -125380.553907 +127 81 -1300948.03845 +128 81 536473.417918 +129 81 -3164492.7709 +130 81 -148519.569575 +131 81 -207978.412157 +132 81 -7295491.64648 +133 81 929649.114512 +134 81 -1056419.44831 +135 81 -2155790.5645 +82 82 6516876.42304 +83 82 -8531914.96891 +84 82 2.60770320892e-8 +85 82 -1076372.38828 +86 82 1736084.49722 +87 82 -2.04890966415e-8 +130 82 -1038306.19785 +131 82 1179893.40665 +132 82 -929649.114512 +133 82 222027.67781 +134 82 -227177.812693 +135 82 75251.5247453 +136 82 -590912.90871 +137 82 953085.336629 +138 82 741520.302649 +83 83 11512089.4984 +84 83 -3.72529029846e-8 +85 83 1736084.49722 +86 83 -2800136.28584 +87 83 3.35276126862e-8 +130 83 1179893.40665 +131 83 -1340787.9621 +132 83 1056419.44831 +133 83 -227177.812693 +134 83 217630.207531 +135 83 55832.4159336 +136 83 953085.336629 +137 83 -1537234.41392 +138 83 -1196000.48814 +84 84 18361731.7736 +85 84 -2.23517417908e-8 +86 84 3.53902578354e-8 +87 84 1520483.39551 +130 84 -929649.114512 +131 84 1056419.44831 +132 84 -2155790.5645 +133 84 -75251.5247453 +134 84 -55832.4159336 +135 84 -5287984.36193 +136 84 741520.302649 +137 84 -1196000.48814 +138 84 -2434642.38116 +85 85 3880798.16119 +86 85 -7110485.96967 +87 85 3.72529029846e-8 +88 85 -759692.612592 +89 85 1651505.67955 +90 85 -1.86264514923e-8 +133 85 -590912.90871 +134 85 953085.336629 +135 85 -741520.302649 +136 85 -52167.0393845 +137 85 83826.4031562 +138 85 61373.985991 +139 85 -379286.632065 +140 85 824536.156663 +141 85 588085.337671 +86 86 13318817.2723 +87 86 -6.70552253723e-8 +88 86 1651505.67955 +89 86 -3590229.73815 +90 86 4.09781932831e-8 +133 86 953085.336629 +134 86 -1537234.41392 +135 86 1196000.48814 +136 86 83826.4031562 +137 86 -134521.304433 +138 86 32978.3592395 +139 86 824536.156663 +140 86 -1792469.90579 +141 86 -1278446.38624 +87 87 19146980.3477 +88 87 -2.51457095146e-8 +89 87 5.40167093277e-8 +90 87 1124555.53479 +133 87 -741520.302649 +134 87 1196000.48814 +135 87 -2434642.38116 +136 87 -61373.985991 +137 87 -32978.3592395 +138 87 -6109264.54972 +139 87 588085.337671 +140 87 -1278446.38624 +141 87 -2352102.70813 +88 88 2558539.62181 +89 88 -6138307.60949 +90 88 4.09781932831e-8 +91 88 -544032.43723 +92 88 1483724.82881 +93 88 -2.32830643654e-8 +136 88 -379286.632065 +137 88 824536.156663 +138 88 -588085.337671 +139 88 12227.6197723 +140 88 -33038.3525623 +141 88 41457.2556898 +142 88 -260348.273704 +143 88 710040.746465 +144 88 484442.198447 +89 89 14915778.6682 +90 89 -1.04308128357e-7 +91 89 1483724.82881 +92 89 -4046522.26039 +93 89 6.70552253723e-8 +136 89 824536.156663 +137 89 -1792469.90579 +138 89 1278446.38624 +139 89 -33038.3525623 +140 89 89431.3340663 +141 89 17103.8438088 +142 89 710040.746465 +143 89 -1936474.76309 +144 89 -1321205.99576 +90 90 18669379.7869 +91 90 -2.79396772385e-8 +92 90 8.19563865662e-8 +93 90 931107.693647 +136 90 -588085.337671 +137 90 1278446.38624 +138 90 -2352102.70813 +139 90 -41457.2556898 +140 90 -17103.8438089 +141 90 -5695176.55864 +142 90 484442.198447 +143 90 -1321205.99576 +144 90 -2315242.24089 +91 91 1816739.3315 +92 91 -5341279.87864 +93 91 3.53902578354e-8 +94 91 -413671.243673 +95 91 1334423.36669 +96 91 -7.45058059692e-9 +139 91 -260348.273704 +140 91 710040.746465 +141 91 -484442.198447 +142 91 24667.0078013 +143 91 -73754.1287475 +144 91 27106.375452 +145 91 -193836.559394 +146 91 625279.223852 +147 91 416676.259817 +92 92 15814018.062 +93 92 -1.04308128357e-7 +94 92 1334423.36669 +95 92 -4304591.50545 +96 92 2.60770320892e-8 +139 92 710040.746465 +140 92 -1936474.76309 +141 92 1321205.99576 +142 92 -73754.1287475 +143 92 222052.369365 +144 92 9164.38855485 +145 92 625279.223852 +146 92 -2017029.75436 +147 92 -1344116.96715 +93 93 18448886.5844 +94 93 -1.210719347e-8 +95 93 4.09781932831e-8 +96 93 830431.306934 +139 93 -484442.198447 +140 93 1321205.99576 +141 93 -2315242.24089 +142 93 -27106.375452 +143 93 -9164.3885549 +144 93 -5492991.14412 +145 93 416676.259817 +146 93 -1344116.96715 +147 93 -2296979.40748 +94 94 2350262.78049 +95 94 -4600302.86075 +96 94 2.04890966415e-8 +97 94 -7.45058059692e-9 +100 94 -311975.890718 +101 94 1184718.57235 +102 94 -1.02445483208e-8 +142 94 -193836.559394 +143 94 625279.223852 +144 94 -416676.259817 +145 94 -474421.1715 +146 94 -131724.429407 +147 94 23329.9193232 +148 94 -800250.281991 +149 94 8988445.57462 +151 94 -144050.092155 +152 94 547025.666412 +153 94 358351.461509 +95 95 17048556.4258 +96 95 -7.45058059692e-8 +97 95 5.96046447754e-8 +98 95 7.45058059692e-9 +100 95 1184718.57235 +101 95 -4498931.28739 +102 95 3.35276126862e-8 +142 95 625279.223852 +143 95 -2017029.75436 +144 95 1344116.96715 +145 95 -131724.429407 +146 95 -28174.4048385 +147 95 6684.54707694 +148 95 -6040681.08601 +149 95 800250.281991 +151 95 547025.666412 +152 95 -2077312.65726 +153 95 -1360828.33484 +96 96 36176122.0894 +100 96 -1.30385160446e-8 +101 96 4.47034835815e-8 +102 96 758227.34113 +142 96 -416676.259817 +143 96 1344116.96715 +144 96 -2296979.40748 +145 96 -23329.9193232 +146 96 -6684.54707698 +147 96 -14301137.6219 +151 96 358351.461509 +152 96 -1360828.33484 +153 96 -2284273.33935 +97 97 305878011.196 +98 97 -66093576.1502 +145 97 800250.281991 +146 97 6040681.08601 +148 97 64525513.4986 +149 97 4237777.92342 +98 98 549337215.254 +145 98 -8988445.57462 +146 98 -800250.281991 +148 98 4237777.92342 +149 98 48915433.0595 +99 99 715555.555556 +150 99 -357777.777778 +100 100 1011851.60912 +101 100 -4110093.43272 +102 100 6.33299350738e-8 +145 100 -144050.092155 +146 100 547025.666412 +147 100 -358351.461509 +151 100 22134.9913963 +152 100 -90320.994053 +153 100 18909.0650259 +101 101 16788621.6609 +102 101 -2.60770320892e-7 +145 101 547025.666412 +146 101 -2077312.65726 +147 101 1360828.33484 +151 101 -90320.994053 +152 101 370626.696215 +153 101 4631.25243446 +102 102 18240145.4814 +145 102 -358351.461509 +146 102 1360828.33484 +147 102 -2284273.33935 +151 102 -18909.0650259 +152 102 -4631.25243459 +153 102 -5293544.01962 +103 103 1011851.60912 +104 103 4110093.43272 +105 103 -4.28408384323e-8 +106 103 -311975.890718 +107 103 -1184718.57235 +108 103 1.11758708954e-8 +154 103 22134.9913963 +155 103 90320.9940529 +156 103 -18909.0650259 +157 103 -144050.092155 +158 103 -547025.666412 +159 103 358351.461509 +104 104 16788621.6609 +105 104 -1.86264514923e-7 +106 104 -1184718.57235 +107 104 -4498931.28739 +108 104 3.72529029846e-8 +154 104 90320.9940529 +155 104 370626.696215 +156 104 4631.25243447 +157 104 -547025.666412 +158 104 -2077312.65726 +159 104 1360828.33484 +105 105 18240145.4814 +106 105 1.11758708954e-8 +107 105 3.72529029846e-8 +108 105 758227.34113 +154 105 18909.0650259 +155 105 -4631.25243456 +156 105 -5293544.01962 +157 105 358351.461509 +158 105 1360828.33484 +159 105 -2284273.33935 +106 106 2350262.38096 +107 106 4600302.12491 +108 106 -2.60770320892e-8 +109 106 2.98023223877e-8 +110 106 -1.19209289551e-7 +112 106 -413671.243673 +113 106 -1334423.36669 +114 106 1.58324837685e-8 +154 106 -144050.092155 +155 106 -547025.666412 +156 106 -358351.461509 +157 106 -474420.971735 +158 106 131724.797327 +159 106 -23329.9193232 +160 106 800256.904552 +161 106 8988441.97885 +163 106 -193836.559394 +164 106 -625279.223852 +165 106 416676.259817 +107 107 17048556.8253 +108 107 -8.94069671631e-8 +109 107 5.96046447754e-8 +112 107 -1334423.36669 +113 107 -4304591.50545 +114 107 4.842877388e-8 +154 107 -547025.666412 +155 107 -2077312.65726 +156 107 -1360828.33484 +157 107 131724.797327 +158 107 -28174.6046034 +159 107 6684.54707693 +160 107 -6040684.68178 +161 107 -800256.904552 +163 107 -625279.223852 +164 107 -2017029.75436 +165 107 1344116.96715 +108 108 36176122.0894 +112 108 1.30385160446e-8 +113 108 3.72529029846e-8 +114 108 830431.306934 +154 108 -358351.461509 +155 108 -1360828.33484 +156 108 -2284273.33935 +157 108 23329.9193232 +158 108 -6684.54707697 +159 108 -14301137.6219 +163 108 416676.259817 +164 108 1344116.96715 +165 108 -2296979.40748 +109 109 305878308.174 +110 109 66094123.115 +157 109 -800256.904552 +158 109 6040684.68178 +160 109 64525494.457 +161 109 -4237812.99363 +110 110 549336918.275 +157 110 -8988441.97885 +158 110 800256.904552 +160 110 -4237812.99363 +161 110 48915452.1011 +111 111 715555.555556 +162 111 -357777.777778 +112 112 1816739.3315 +113 112 5341279.87864 +114 112 -3.72529029846e-8 +115 112 -544032.43723 +116 112 -1483724.82881 +117 112 2.23517417908e-8 +157 112 -193836.559394 +158 112 -625279.223852 +159 112 -416676.259817 +163 112 24667.0078012 +164 112 73754.1287474 +165 112 -27106.375452 +166 112 -260348.273704 +167 112 -710040.746465 +168 112 484442.198447 +113 113 15814018.062 +114 113 -1.19209289551e-7 +115 113 -1483724.82881 +116 113 -4046522.26039 +117 113 5.96046447754e-8 +157 113 -625279.223852 +158 113 -2017029.75436 +159 113 -1344116.96715 +163 113 73754.1287474 +164 113 222052.369365 +165 113 9164.38855485 +166 113 -710040.746465 +167 113 -1936474.76309 +168 113 1321205.99576 +114 114 18448886.5844 +115 114 2.32830643654e-8 +116 114 6.33299350738e-8 +117 114 931107.693647 +157 114 -416676.259817 +158 114 -1344116.96715 +159 114 -2296979.40748 +163 114 27106.375452 +164 114 -9164.3885549 +165 114 -5492991.14412 +166 114 484442.198447 +167 114 1321205.99576 +168 114 -2315242.24089 +115 115 2558539.62181 +116 115 6138307.60949 +117 115 -5.58793544769e-8 +118 115 -759692.612592 +119 115 -1651505.67955 +120 115 2.70083546639e-8 +163 115 -260348.273704 +164 115 -710040.746465 +165 115 -484442.198447 +166 115 12227.6197723 +167 115 33038.3525623 +168 115 -41457.2556898 +169 115 -379286.632065 +170 115 -824536.156663 +171 115 588085.337671 +116 116 14915778.6682 +117 116 -1.34110450745e-7 +118 116 -1651505.67955 +119 116 -3590229.73815 +120 116 5.77419996262e-8 +163 116 -710040.746465 +164 116 -1936474.76309 +165 116 -1321205.99576 +166 116 33038.3525623 +167 116 89431.3340662 +168 116 17103.8438088 +169 116 -824536.156663 +170 116 -1792469.90579 +171 116 1278446.38624 +117 117 18669379.7869 +118 117 2.51457095146e-8 +119 117 5.40167093277e-8 +120 117 1124555.53479 +163 117 -484442.198447 +164 117 -1321205.99576 +165 117 -2315242.24089 +166 117 41457.2556898 +167 117 -17103.8438089 +168 117 -5695176.55864 +169 117 588085.337671 +170 117 1278446.38624 +171 117 -2352102.70813 +118 118 3880798.16119 +119 118 7110485.96967 +120 118 -4.842877388e-8 +121 118 -1076372.38828 +122 118 -1736084.49722 +123 118 2.98023223877e-8 +166 118 -379286.632065 +167 118 -824536.156663 +168 118 -588085.337671 +169 118 -52167.0393845 +170 118 -83826.4031562 +171 118 -61373.985991 +172 118 -590912.90871 +173 118 -953085.336629 +174 118 741520.302649 +119 119 13318817.2723 +120 119 -9.685754776e-8 +121 119 -1736084.49722 +122 119 -2800136.28584 +123 119 4.842877388e-8 +166 119 -824536.156663 +167 119 -1792469.90579 +168 119 -1278446.38624 +169 119 -83826.4031562 +170 119 -134521.304433 +171 119 32978.3592395 +172 119 -953085.336629 +173 119 -1537234.41392 +174 119 1196000.48814 +120 120 19146980.3477 +121 120 2.421438694e-8 +122 120 3.91155481339e-8 +123 120 1520483.39551 +166 120 -588085.337671 +167 120 -1278446.38624 +168 120 -2352102.70813 +169 120 61373.985991 +170 120 -32978.3592395 +171 120 -6109264.54972 +172 120 741520.302649 +173 120 1196000.48814 +174 120 -2434642.38116 +121 121 6516876.42304 +122 121 8531914.96891 +123 121 -2.23517417908e-8 +124 121 -2626121.17726 +125 121 -2984228.61052 +126 121 9.31322574615e-9 +169 121 -590912.90871 +170 121 -953085.336629 +171 121 -741520.302649 +172 121 222027.67781 +173 121 227177.812693 +174 121 -75251.5247453 +175 121 -1038306.19785 +176 121 -1179893.40665 +177 121 929649.114512 +122 122 11512089.4984 +123 122 -3.72529029846e-8 +124 122 -2984228.61052 +125 122 -3391168.87559 +126 122 1.11758708954e-8 +169 122 -953085.336629 +170 122 -1537234.41392 +171 122 -1196000.48814 +172 122 227177.812693 +173 122 217630.207531 +174 122 55832.4159336 +175 122 -1179893.40665 +176 122 -1340787.9621 +177 122 1056419.44831 +123 123 18361731.7736 +124 123 3.72529029846e-9 +125 123 5.58793544769e-9 +126 123 -125380.553907 +169 123 -741520.302649 +170 123 -1196000.48814 +171 123 -2434642.38116 +172 123 75251.5247453 +173 123 -55832.4159336 +174 123 -5287984.36193 +175 123 929649.114512 +176 123 1056419.44831 +177 123 -2155790.5645 +124 124 11197028.9194 +125 124 7624235.1212 +126 124 -2.23517417908e-8 +127 124 -1154846.00933 +128 124 -476225.158488 +129 124 5.58793544769e-9 +172 124 -1038306.19785 +173 124 -1179893.40665 +174 124 -929649.114512 +175 124 -908773.635179 +176 124 -175831.894858 +177 124 -148519.569575 +178 124 -1760951.03338 +179 124 -726165.37459 +180 124 1300948.03845 +125 125 6560950.4012 +126 125 -1.49011611938e-8 +127 125 -476225.158488 +128 125 -196381.508655 +129 125 2.79396772385e-9 +172 125 -1179893.40665 +173 125 -1340787.9621 +174 125 -1056419.44831 +175 125 -175831.894858 +176 125 153537.59263 +177 125 207978.412157 +178 125 -726165.37459 +179 125 -299449.639006 +180 125 536473.417918 +126 126 21281133.3311 +127 126 3.72529029846e-9 +128 126 1.86264514923e-9 +129 126 4075797.18655 +172 126 -929649.114512 +173 126 -1056419.44831 +174 126 -2155790.5645 +175 126 148519.569575 +176 126 -207978.412157 +177 126 -7295491.64648 +178 126 1300948.03845 +179 126 536473.417918 +180 126 -3164492.7709 +127 127 14087608.2601 +128 127 7.45058059692e-9 +129 127 -7.45058059692e-9 +130 127 -1154846.00933 +131 127 476225.158488 +132 127 5.58793544769e-9 +175 127 -1760951.03338 +176 127 -726165.37459 +177 127 -1300948.03845 +178 127 -2367056.05396 +181 127 -1760951.03338 +182 127 726165.37459 +183 127 1300948.03845 +128 128 2395597.11087 +129 128 7.45058059692e-9 +130 128 476225.158488 +131 128 -196381.508655 +132 128 -2.79396772385e-9 +175 128 -726165.37459 +176 128 -299449.639006 +177 128 -536473.417918 +179 128 -402517.768767 +180 128 429178.734334 +181 128 726165.37459 +182 128 -299449.639006 +183 128 -536473.417918 +129 129 25315942.1547 +130 129 3.72529029846e-9 +131 129 -1.86264514923e-9 +132 129 4075797.18655 +175 129 -1300948.03845 +176 129 -536473.417918 +177 129 -3164492.7709 +178 129 5.58793544769e-9 +179 129 -429178.734334 +180 129 -10404782.7221 +181 129 1300948.03845 +182 129 -536473.417918 +183 129 -3164492.7709 +130 130 11197028.9194 +131 130 -7624235.1212 +132 130 1.49011611938e-8 +133 130 -2626121.17726 +134 130 2984228.61052 +135 130 -9.31322574615e-9 +178 130 -1760951.03338 +179 130 726165.37459 +180 130 -1300948.03845 +181 130 -908773.635179 +182 130 175831.894858 +183 130 148519.569575 +184 130 -1038306.19785 +185 130 1179893.40665 +186 130 929649.114512 +131 131 6560950.4012 +132 131 -7.45058059692e-9 +133 131 2984228.61052 +134 131 -3391168.87559 +135 131 9.31322574615e-9 +178 131 726165.37459 +179 131 -299449.639006 +180 131 536473.417918 +181 131 175831.894858 +182 131 153537.59263 +183 131 207978.412157 +184 131 1179893.40665 +185 131 -1340787.9621 +186 131 -1056419.44831 +132 132 21281133.3311 +133 132 -1.49011611938e-8 +134 132 1.67638063431e-8 +135 132 -125380.553907 +178 132 -1300948.03845 +179 132 536473.417918 +180 132 -3164492.7709 +181 132 -148519.569575 +182 132 -207978.412157 +183 132 -7295491.64648 +184 132 929649.114512 +185 132 -1056419.44831 +186 132 -2155790.5645 +133 133 6516876.42304 +134 133 -8531914.96891 +135 133 1.86264514923e-8 +136 133 -1076372.38828 +137 133 1736084.49722 +138 133 -1.30385160446e-8 +181 133 -1038306.19785 +182 133 1179893.40665 +183 133 -929649.114512 +184 133 222027.67781 +185 133 -227177.812693 +186 133 75251.5247453 +187 133 -590912.90871 +188 133 953085.336629 +189 133 741520.302649 +134 134 11512089.4984 +135 134 -2.98023223877e-8 +136 134 1736084.49722 +137 134 -2800136.28584 +138 134 2.04890966415e-8 +181 134 1179893.40665 +182 134 -1340787.9621 +183 134 1056419.44831 +184 134 -227177.812693 +185 134 217630.207531 +186 134 55832.4159336 +187 134 953085.336629 +188 134 -1537234.41392 +189 134 -1196000.48814 +135 135 18361731.7736 +136 135 -1.67638063431e-8 +137 135 2.79396772385e-8 +138 135 1520483.39551 +181 135 -929649.114512 +182 135 1056419.44831 +183 135 -2155790.5645 +184 135 -75251.5247453 +185 135 -55832.4159336 +186 135 -5287984.36193 +187 135 741520.302649 +188 135 -1196000.48814 +189 135 -2434642.38116 +136 136 3880798.16119 +137 136 -7110485.96967 +138 136 3.35276126862e-8 +139 136 -759692.612592 +140 136 1651505.67955 +141 136 -2.04890966415e-8 +184 136 -590912.90871 +185 136 953085.336629 +186 136 -741520.302649 +187 136 -52167.0393845 +188 136 83826.4031562 +189 136 61373.985991 +190 136 -379286.632065 +191 136 824536.156663 +192 136 588085.337671 +137 137 13318817.2723 +138 137 -5.96046447754e-8 +139 137 1651505.67955 +140 137 -3590229.73815 +141 137 4.47034835815e-8 +184 137 953085.336629 +185 137 -1537234.41392 +186 137 1196000.48814 +187 137 83826.4031562 +188 137 -134521.304433 +189 137 32978.3592395 +190 137 824536.156663 +191 137 -1792469.90579 +192 137 -1278446.38624 +138 138 19146980.3477 +139 138 -2.70083546639e-8 +140 138 5.77419996262e-8 +141 138 1124555.53479 +184 138 -741520.302649 +185 138 1196000.48814 +186 138 -2434642.38116 +187 138 -61373.985991 +188 138 -32978.3592395 +189 138 -6109264.54972 +190 138 588085.337671 +191 138 -1278446.38624 +192 138 -2352102.70813 +139 139 2558539.62181 +140 139 -6138307.60949 +141 139 4.28408384323e-8 +142 139 -544032.43723 +143 139 1483724.82881 +144 139 -2.51457095146e-8 +187 139 -379286.632065 +188 139 824536.156663 +189 139 -588085.337671 +190 139 12227.6197723 +191 139 -33038.3525623 +192 139 41457.2556898 +193 139 -260348.273704 +194 139 710040.746465 +195 139 484442.198447 +140 140 14915778.6682 +141 140 -1.19209289551e-7 +142 140 1483724.82881 +143 140 -4046522.26039 +144 140 7.07805156708e-8 +187 140 824536.156663 +188 140 -1792469.90579 +189 140 1278446.38624 +190 140 -33038.3525623 +191 140 89431.3340662 +192 140 17103.8438088 +193 140 710040.746465 +194 140 -1936474.76309 +195 140 -1321205.99576 +141 141 18669379.7869 +142 141 -2.60770320892e-8 +143 141 7.45058059692e-8 +144 141 931107.693647 +187 141 -588085.337671 +188 141 1278446.38624 +189 141 -2352102.70813 +190 141 -41457.2556898 +191 141 -17103.8438089 +192 141 -5695176.55864 +193 141 484442.198447 +194 141 -1321205.99576 +195 141 -2315242.24089 +142 142 1816739.3315 +143 142 -5341279.87864 +144 142 3.53902578354e-8 +145 142 -413671.243673 +146 142 1334423.36669 +147 142 -7.45058059692e-9 +190 142 -260348.273704 +191 142 710040.746465 +192 142 -484442.198447 +193 142 24667.0078013 +194 142 -73754.1287474 +195 142 27106.375452 +196 142 -193836.559394 +197 142 625279.223852 +198 142 416676.259817 +143 143 15814018.062 +144 143 -1.04308128357e-7 +145 143 1334423.36669 +146 143 -4304591.50545 +147 143 2.60770320892e-8 +190 143 710040.746465 +191 143 -1936474.76309 +192 143 1321205.99576 +193 143 -73754.1287474 +194 143 222052.369365 +195 143 9164.38855485 +196 143 625279.223852 +197 143 -2017029.75436 +198 143 -1344116.96715 +144 144 18448886.5844 +145 144 -9.31322574615e-9 +146 144 2.98023223877e-8 +147 144 830431.306934 +190 144 -484442.198447 +191 144 1321205.99576 +192 144 -2315242.24089 +193 144 -27106.375452 +194 144 -9164.3885549 +195 144 -5492991.14412 +196 144 416676.259817 +197 144 -1344116.96715 +198 144 -2296979.40748 +145 145 2350262.78049 +146 145 -4600302.86075 +147 145 1.86264514923e-8 +148 145 -7.45058059692e-9 +151 145 -311975.890718 +152 145 1184718.57235 +153 145 -1.11758708954e-8 +193 145 -193836.559394 +194 145 625279.223852 +195 145 -416676.259817 +196 145 -474421.1715 +197 145 -131724.429407 +198 145 23329.9193232 +199 145 -800250.281991 +200 145 8988445.57462 +202 145 -144050.092155 +203 145 547025.666412 +204 145 358351.461509 +146 146 17048556.4258 +147 146 -6.70552253723e-8 +148 146 5.96046447754e-8 +149 146 7.45058059692e-9 +151 146 1184718.57235 +152 146 -4498931.28739 +153 146 3.72529029846e-8 +193 146 625279.223852 +194 146 -2017029.75436 +195 146 1344116.96715 +196 146 -131724.429407 +197 146 -28174.4048386 +198 146 6684.54707694 +199 146 -6040681.08601 +200 146 800250.281991 +202 146 547025.666412 +203 146 -2077312.65726 +204 146 -1360828.33484 +147 147 36176122.0894 +151 147 -1.39698386192e-8 +152 147 4.842877388e-8 +153 147 758227.34113 +193 147 -416676.259817 +194 147 1344116.96715 +195 147 -2296979.40748 +196 147 -23329.9193232 +197 147 -6684.54707698 +198 147 -14301137.6219 +202 147 358351.461509 +203 147 -1360828.33484 +204 147 -2284273.33935 +148 148 305878011.196 +149 148 -66093576.1502 +196 148 800250.281991 +197 148 6040681.08601 +199 148 64525513.4986 +200 148 4237777.92342 +149 149 549337215.254 +196 149 -8988445.57462 +197 149 -800250.281991 +199 149 4237777.92342 +200 149 48915433.0595 +150 150 715555.555556 +201 150 -357777.777778 +151 151 1011851.60912 +152 151 -4110093.43272 +153 151 6.51925802231e-8 +196 151 -144050.092155 +197 151 547025.666412 +198 151 -358351.461509 +202 151 22134.9913963 +203 151 -90320.9940529 +204 151 18909.0650259 +152 152 16788621.6609 +153 152 -2.68220901489e-7 +196 152 547025.666412 +197 152 -2077312.65726 +198 152 1360828.33484 +202 152 -90320.9940529 +203 152 370626.696215 +204 152 4631.25243446 +153 153 18240145.4814 +196 153 -358351.461509 +197 153 1360828.33484 +198 153 -2284273.33935 +202 153 -18909.0650259 +203 153 -4631.25243458 +204 153 -5293544.01962 +154 154 1011851.60912 +155 154 4110093.43272 +156 154 -4.842877388e-8 +157 154 -311975.890718 +158 154 -1184718.57235 +159 154 1.02445483208e-8 +205 154 22134.9913963 +206 154 90320.994053 +207 154 -18909.0650259 +208 154 -144050.092155 +209 154 -547025.666412 +210 154 358351.461509 +155 155 16788621.6609 +156 155 -2.08616256714e-7 +157 155 -1184718.57235 +158 155 -4498931.28739 +159 155 3.35276126862e-8 +205 155 90320.994053 +206 155 370626.696215 +207 155 4631.25243448 +208 155 -547025.666412 +209 155 -2077312.65726 +210 155 1360828.33484 +156 156 18240145.4814 +157 156 9.31322574615e-9 +158 156 2.98023223877e-8 +159 156 758227.34113 +205 156 18909.0650259 +206 156 -4631.25243456 +207 156 -5293544.01962 +208 156 358351.461509 +209 156 1360828.33484 +210 156 -2284273.33935 +157 157 2350262.38096 +158 157 4600302.12491 +159 157 -2.98023223877e-8 +160 157 2.98023223877e-8 +161 157 -1.19209289551e-7 +163 157 -413671.243673 +164 157 -1334423.36669 +165 157 1.67638063431e-8 +205 157 -144050.092155 +206 157 -547025.666412 +207 157 -358351.461509 +208 157 -474420.971735 +209 157 131724.797327 +210 157 -23329.9193232 +211 157 800256.904552 +212 157 8988441.97885 +214 157 -193836.559394 +215 157 -625279.223852 +216 157 416676.259817 +158 158 17048556.8253 +159 158 -9.685754776e-8 +160 158 5.96046447754e-8 +163 158 -1334423.36669 +164 158 -4304591.50545 +165 158 5.21540641785e-8 +205 158 -547025.666412 +206 158 -2077312.65726 +207 158 -1360828.33484 +208 158 131724.797327 +209 158 -28174.6046033 +210 158 6684.54707694 +211 158 -6040684.68178 +212 158 -800256.904552 +214 158 -625279.223852 +215 158 -2017029.75436 +216 158 1344116.96715 +159 159 36176122.0894 +163 159 1.39698386192e-8 +164 159 4.09781932831e-8 +165 159 830431.306934 +205 159 -358351.461509 +206 159 -1360828.33484 +207 159 -2284273.33935 +208 159 23329.9193232 +209 159 -6684.54707697 +210 159 -14301137.6219 +214 159 416676.259817 +215 159 1344116.96715 +216 159 -2296979.40748 +160 160 305878308.174 +161 160 66094123.115 +208 160 -800256.904552 +209 160 6040684.68178 +211 160 64525494.457 +212 160 -4237812.99363 +161 161 549336918.275 +208 161 -8988441.97885 +209 161 800256.904552 +211 161 -4237812.99363 +212 161 48915452.1011 +162 162 715555.555556 +213 162 -357777.777778 +163 163 1816739.3315 +164 163 5341279.87864 +165 163 -3.91155481339e-8 +166 163 -544032.43723 +167 163 -1483724.82881 +168 163 2.51457095146e-8 +208 163 -193836.559394 +209 163 -625279.223852 +210 163 -416676.259817 +214 163 24667.0078013 +215 163 73754.1287475 +216 163 -27106.375452 +217 163 -260348.273704 +218 163 -710040.746465 +219 163 484442.198447 +164 164 15814018.062 +165 164 -1.26659870148e-7 +166 164 -1483724.82881 +167 164 -4046522.26039 +168 164 6.70552253723e-8 +208 164 -625279.223852 +209 164 -2017029.75436 +210 164 -1344116.96715 +214 164 73754.1287475 +215 164 222052.369365 +216 164 9164.38855485 +217 164 -710040.746465 +218 164 -1936474.76309 +219 164 1321205.99576 +165 165 18448886.5844 +166 165 2.23517417908e-8 +167 165 5.96046447754e-8 +168 165 931107.693647 +208 165 -416676.259817 +209 165 -1344116.96715 +210 165 -2296979.40748 +214 165 27106.375452 +215 165 -9164.3885549 +216 165 -5492991.14412 +217 165 484442.198447 +218 165 1321205.99576 +219 165 -2315242.24089 +166 166 2558539.62181 +167 166 6138307.60949 +168 166 -5.21540641785e-8 +169 166 -759692.612592 +170 166 -1651505.67955 +171 166 3.16649675369e-8 +214 166 -260348.273704 +215 166 -710040.746465 +216 166 -484442.198447 +217 166 12227.6197723 +218 166 33038.3525623 +219 166 -41457.2556898 +220 166 -379286.632065 +221 166 -824536.156663 +222 166 588085.337671 +167 167 14915778.6682 +168 167 -1.26659870148e-7 +169 167 -1651505.67955 +170 167 -3590229.73815 +171 167 6.70552253723e-8 +214 167 -710040.746465 +215 167 -1936474.76309 +216 167 -1321205.99576 +217 167 33038.3525623 +218 167 89431.3340663 +219 167 17103.8438088 +220 167 -824536.156663 +221 167 -1792469.90579 +222 167 1278446.38624 +168 168 18669379.7869 +169 168 2.88709998131e-8 +170 168 6.14672899246e-8 +171 168 1124555.53479 +214 168 -484442.198447 +215 168 -1321205.99576 +216 168 -2315242.24089 +217 168 41457.2556898 +218 168 -17103.8438089 +219 168 -5695176.55864 +220 168 588085.337671 +221 168 1278446.38624 +222 168 -2352102.70813 +169 169 3880798.16119 +170 169 7110485.96967 +171 169 -4.09781932831e-8 +172 169 -1076372.38828 +173 169 -1736084.49722 +174 169 2.421438694e-8 +217 169 -379286.632065 +218 169 -824536.156663 +219 169 -588085.337671 +220 169 -52167.0393845 +221 169 -83826.4031561 +222 169 -61373.985991 +223 169 -590912.90871 +224 169 -953085.336629 +225 169 741520.302649 +170 170 13318817.2723 +171 170 -8.19563865662e-8 +172 170 -1736084.49722 +173 170 -2800136.28584 +174 170 3.91155481339e-8 +217 170 -824536.156663 +218 170 -1792469.90579 +219 170 -1278446.38624 +220 170 -83826.4031561 +221 170 -134521.304432 +222 170 32978.3592395 +223 170 -953085.336629 +224 170 -1537234.41392 +225 170 1196000.48814 +171 171 19146980.3477 +172 171 1.86264514923e-8 +173 171 3.35276126862e-8 +174 171 1520483.39551 +217 171 -588085.337671 +218 171 -1278446.38624 +219 171 -2352102.70813 +220 171 61373.985991 +221 171 -32978.3592395 +222 171 -6109264.54972 +223 171 741520.302649 +224 171 1196000.48814 +225 171 -2434642.38116 +172 172 6516876.42304 +173 172 8531914.96891 +174 172 -1.86264514923e-8 +175 172 -2626121.17726 +176 172 -2984228.61052 +177 172 5.58793544769e-9 +220 172 -590912.90871 +221 172 -953085.336629 +222 172 -741520.302649 +223 172 222027.67781 +224 172 227177.812693 +225 172 -75251.5247453 +226 172 -1038306.19785 +227 172 -1179893.40665 +228 172 929649.114512 +173 173 11512089.4984 +174 173 -2.98023223877e-8 +175 173 -2984228.61052 +176 173 -3391168.87559 +177 173 9.31322574615e-9 +220 173 -953085.336629 +221 173 -1537234.41392 +222 173 -1196000.48814 +223 173 227177.812693 +224 173 217630.207531 +225 173 55832.4159336 +226 173 -1179893.40665 +227 173 -1340787.9621 +228 173 1056419.44831 +174 174 18361731.7736 +175 174 1.86264514923e-9 +176 174 3.72529029846e-9 +177 174 -125380.553907 +220 174 -741520.302649 +221 174 -1196000.48814 +222 174 -2434642.38116 +223 174 75251.5247453 +224 174 -55832.4159336 +225 174 -5287984.36193 +226 174 929649.114512 +227 174 1056419.44831 +228 174 -2155790.5645 +175 175 11197028.9194 +176 175 7624235.1212 +177 175 -2.98023223877e-8 +178 175 -1154846.00933 +179 175 -476225.158488 +180 175 1.67638063431e-8 +223 175 -1038306.19785 +224 175 -1179893.40665 +225 175 -929649.114512 +226 175 -908773.635179 +227 175 -175831.894858 +228 175 -148519.569575 +229 175 -1760951.03338 +230 175 -726165.37459 +231 175 1300948.03845 +176 176 6560950.4012 +177 176 -1.49011611938e-8 +178 176 -476225.158488 +179 176 -196381.508655 +180 176 7.45058059692e-9 +223 176 -1179893.40665 +224 176 -1340787.9621 +225 176 -1056419.44831 +226 176 -175831.894858 +227 176 153537.59263 +228 176 207978.412157 +229 176 -726165.37459 +230 176 -299449.639006 +231 176 536473.417918 +177 177 21281133.3311 +180 177 4075797.18655 +223 177 -929649.114512 +224 177 -1056419.44831 +225 177 -2155790.5645 +226 177 148519.569575 +227 177 -207978.412157 +228 177 -7295491.64648 +229 177 1300948.03845 +230 177 536473.417918 +231 177 -3164492.7709 +178 178 14087608.2601 +179 178 7.45058059692e-9 +180 178 -7.45058059692e-9 +181 178 -1154846.00933 +182 178 476225.158488 +183 178 1.67638063431e-8 +226 178 -1760951.03338 +227 178 -726165.37459 +228 178 -1300948.03845 +229 178 -2367056.05396 +230 178 7.45058059692e-9 +231 178 -9.31322574615e-9 +232 178 -1760951.03338 +233 178 726165.37459 +234 178 1300948.03845 +179 179 2395597.11087 +180 179 1.49011611938e-8 +181 179 476225.158488 +182 179 -196381.508655 +183 179 -7.45058059692e-9 +226 179 -726165.37459 +227 179 -299449.639006 +228 179 -536473.417918 +229 179 1.11758708954e-8 +230 179 -402517.768767 +231 179 429178.734334 +232 179 726165.37459 +233 179 -299449.639006 +234 179 -536473.417918 +180 180 25315942.1547 +183 180 4075797.18655 +226 180 -1300948.03845 +227 180 -536473.417918 +228 180 -3164492.7709 +229 180 5.58793544769e-9 +230 180 -429178.734334 +231 180 -10404782.7221 +232 180 1300948.03845 +233 180 -536473.417918 +234 180 -3164492.7709 +181 181 11197028.9194 +182 181 -7624235.1212 +183 181 3.35276126862e-8 +184 181 -2626121.17726 +185 181 2984228.61052 +186 181 -9.31322574615e-9 +229 181 -1760951.03338 +230 181 726165.37459 +231 181 -1300948.03845 +232 181 -908773.635179 +233 181 175831.894858 +234 181 148519.569575 +235 181 -1038306.19785 +236 181 1179893.40665 +237 181 929649.114512 +182 182 6560950.4012 +183 182 -2.23517417908e-8 +184 182 2984228.61052 +185 182 -3391168.87559 +186 182 9.31322574615e-9 +229 182 726165.37459 +230 182 -299449.639006 +231 182 536473.417918 +232 182 175831.894858 +233 182 153537.59263 +234 182 207978.412157 +235 182 1179893.40665 +236 182 -1340787.9621 +237 182 -1056419.44831 +183 183 21281133.3311 +184 183 -1.30385160446e-8 +185 183 1.49011611938e-8 +186 183 -125380.553907 +229 183 -1300948.03845 +230 183 536473.417918 +231 183 -3164492.7709 +232 183 -148519.569575 +233 183 -207978.412157 +234 183 -7295491.64648 +235 183 929649.114512 +236 183 -1056419.44831 +237 183 -2155790.5645 +184 184 6516876.42304 +185 184 -8531914.96891 +186 184 1.86264514923e-8 +187 184 -1076372.38828 +188 184 1736084.49722 +189 184 -1.30385160446e-8 +232 184 -1038306.19785 +233 184 1179893.40665 +234 184 -929649.114512 +235 184 222027.67781 +236 184 -227177.812693 +237 184 75251.5247453 +238 184 -590912.90871 +239 184 953085.336629 +240 184 741520.302649 +185 185 11512089.4984 +186 185 -2.23517417908e-8 +187 185 1736084.49722 +188 185 -2800136.28584 +189 185 2.04890966415e-8 +232 185 1179893.40665 +233 185 -1340787.9621 +234 185 1056419.44831 +235 185 -227177.812693 +236 185 217630.207531 +237 185 55832.4159336 +238 185 953085.336629 +239 185 -1537234.41392 +240 185 -1196000.48814 +186 186 18361731.7736 +187 186 -1.67638063431e-8 +188 186 2.60770320892e-8 +189 186 1520483.39551 +232 186 -929649.114512 +233 186 1056419.44831 +234 186 -2155790.5645 +235 186 -75251.5247453 +236 186 -55832.4159336 +237 186 -5287984.36193 +238 186 741520.302649 +239 186 -1196000.48814 +240 186 -2434642.38116 +187 187 3880798.16119 +188 187 -7110485.96967 +189 187 3.72529029846e-8 +190 187 -759692.612592 +191 187 1651505.67955 +192 187 -1.67638063431e-8 +235 187 -590912.90871 +236 187 953085.336629 +237 187 -741520.302649 +238 187 -52167.0393845 +239 187 83826.4031561 +240 187 61373.985991 +241 187 -379286.632065 +242 187 824536.156663 +243 187 588085.337671 +188 188 13318817.2723 +189 188 -6.70552253723e-8 +190 188 1651505.67955 +191 188 -3590229.73815 +192 188 3.72529029846e-8 +235 188 953085.336629 +236 188 -1537234.41392 +237 188 1196000.48814 +238 188 83826.4031561 +239 188 -134521.304432 +240 188 32978.3592395 +241 188 824536.156663 +242 188 -1792469.90579 +243 188 -1278446.38624 +189 189 19146980.3477 +190 189 -2.32830643654e-8 +191 189 5.02914190292e-8 +192 189 1124555.53479 +235 189 -741520.302649 +236 189 1196000.48814 +237 189 -2434642.38116 +238 189 -61373.985991 +239 189 -32978.3592395 +240 189 -6109264.54972 +241 189 588085.337671 +242 189 -1278446.38624 +243 189 -2352102.70813 +190 190 2558539.62181 +191 190 -6138307.60949 +192 190 4.28408384323e-8 +193 190 -544032.43723 +194 190 1483724.82881 +195 190 -2.421438694e-8 +238 190 -379286.632065 +239 190 824536.156663 +240 190 -588085.337671 +241 190 12227.6197724 +242 190 -33038.3525623 +243 190 41457.2556898 +244 190 -260348.273704 +245 190 710040.746465 +246 190 484442.198447 +191 191 14915778.6682 +192 191 -1.11758708954e-7 +193 191 1483724.82881 +194 191 -4046522.26039 +195 191 6.70552253723e-8 +238 191 824536.156663 +239 191 -1792469.90579 +240 191 1278446.38624 +241 191 -33038.3525623 +242 191 89431.3340663 +243 191 17103.8438088 +244 191 710040.746465 +245 191 -1936474.76309 +246 191 -1321205.99576 +192 192 18669379.7869 +193 192 -2.70083546639e-8 +194 192 7.82310962677e-8 +195 192 931107.693647 +238 192 -588085.337671 +239 192 1278446.38624 +240 192 -2352102.70813 +241 192 -41457.2556898 +242 192 -17103.8438089 +243 192 -5695176.55864 +244 192 484442.198447 +245 192 -1321205.99576 +246 192 -2315242.24089 +193 193 1816739.3315 +194 193 -5341279.87864 +195 193 3.53902578354e-8 +196 193 -413671.243673 +197 193 1334423.36669 +198 193 -7.45058059692e-9 +241 193 -260348.273704 +242 193 710040.746465 +243 193 -484442.198447 +244 193 24667.0078013 +245 193 -73754.1287475 +246 193 27106.375452 +247 193 -193836.559394 +248 193 625279.223852 +249 193 416676.259817 +194 194 15814018.062 +195 194 -1.04308128357e-7 +196 194 1334423.36669 +197 194 -4304591.50545 +198 194 2.60770320892e-8 +241 194 710040.746465 +242 194 -1936474.76309 +243 194 1321205.99576 +244 194 -73754.1287475 +245 194 222052.369365 +246 194 9164.38855485 +247 194 625279.223852 +248 194 -2017029.75436 +249 194 -1344116.96715 +195 195 18448886.5844 +196 195 -1.11758708954e-8 +197 195 3.72529029846e-8 +198 195 830431.306934 +241 195 -484442.198447 +242 195 1321205.99576 +243 195 -2315242.24089 +244 195 -27106.375452 +245 195 -9164.3885549 +246 195 -5492991.14412 +247 195 416676.259817 +248 195 -1344116.96715 +249 195 -2296979.40748 +196 196 2350262.78049 +197 196 -4600302.86075 +198 196 1.86264514923e-8 +199 196 -7.45058059692e-9 +202 196 -311975.890718 +203 196 1184718.57235 +204 196 -9.31322574615e-9 +244 196 -193836.559394 +245 196 625279.223852 +246 196 -416676.259817 +247 196 -474421.1715 +248 196 -131724.429407 +249 196 23329.9193232 +250 196 -800250.281991 +251 196 8988445.57462 +253 196 -144050.092155 +254 196 547025.666412 +255 196 358351.461509 +197 197 17048556.4258 +198 197 -6.70552253723e-8 +199 197 5.96046447754e-8 +200 197 7.45058059692e-9 +202 197 1184718.57235 +203 197 -4498931.28739 +204 197 2.98023223877e-8 +244 197 625279.223852 +245 197 -2017029.75436 +246 197 1344116.96715 +247 197 -131724.429407 +248 197 -28174.4048385 +249 197 6684.54707694 +250 197 -6040681.08601 +251 197 800250.281991 +253 197 547025.666412 +254 197 -2077312.65726 +255 197 -1360828.33484 +198 198 36176122.0894 +202 198 -1.11758708954e-8 +203 198 3.72529029846e-8 +204 198 758227.34113 +244 198 -416676.259817 +245 198 1344116.96715 +246 198 -2296979.40748 +247 198 -23329.9193232 +248 198 -6684.54707698 +249 198 -14301137.6219 +253 198 358351.461509 +254 198 -1360828.33484 +255 198 -2284273.33935 +199 199 305878011.196 +200 199 -66093576.1502 +247 199 800250.281991 +248 199 6040681.08601 +250 199 64525513.4986 +251 199 4237777.92342 +200 200 549337215.254 +247 200 -8988445.57462 +248 200 -800250.281991 +250 200 4237777.92342 +251 200 48915433.0595 +201 201 715555.555556 +252 201 -357777.777778 +202 202 1011851.60912 +203 202 -4110093.43272 +204 202 6.33299350738e-8 +247 202 -144050.092155 +248 202 547025.666412 +249 202 -358351.461509 +253 202 22134.9913963 +254 202 -90320.994053 +255 202 18909.0650259 +203 203 16788621.6609 +204 203 -2.53319740295e-7 +247 203 547025.666412 +248 203 -2077312.65726 +249 203 1360828.33484 +253 203 -90320.994053 +254 203 370626.696215 +255 203 4631.25243446 +204 204 18240145.4814 +247 204 -358351.461509 +248 204 1360828.33484 +249 204 -2284273.33935 +253 204 -18909.0650259 +254 204 -4631.25243459 +255 204 -5293544.01962 +205 205 1011851.60912 +206 205 4110093.43272 +207 205 -4.47034835815e-8 +208 205 -311975.890718 +209 205 -1184718.57235 +210 205 1.11758708954e-8 +256 205 22134.9913963 +257 205 90320.9940529 +258 205 -18909.0650259 +259 205 -144050.092155 +260 205 -547025.666412 +261 205 358351.461509 +206 206 16788621.6609 +207 206 -1.9371509552e-7 +208 206 -1184718.57235 +209 206 -4498931.28739 +210 206 3.72529029846e-8 +256 206 90320.9940529 +257 206 370626.696215 +258 206 4631.25243447 +259 206 -547025.666412 +260 206 -2077312.65726 +261 206 1360828.33484 +207 207 18240145.4814 +208 207 1.11758708954e-8 +209 207 3.72529029846e-8 +210 207 758227.34113 +256 207 18909.0650259 +257 207 -4631.25243456 +258 207 -5293544.01962 +259 207 358351.461509 +260 207 1360828.33484 +261 207 -2284273.33935 +208 208 2350262.38096 +209 208 4600302.12491 +210 208 -2.98023223877e-8 +211 208 2.98023223877e-8 +212 208 -1.19209289551e-7 +214 208 -413671.243673 +215 208 -1334423.36669 +216 208 1.58324837685e-8 +256 208 -144050.092155 +257 208 -547025.666412 +258 208 -358351.461509 +259 208 -474420.971735 +260 208 131724.797327 +261 208 -23329.9193232 +262 208 800256.904552 +263 208 8988441.97885 +265 208 -193836.559394 +266 208 -625279.223852 +267 208 416676.259817 +209 209 17048556.8253 +210 209 -9.685754776e-8 +211 209 5.96046447754e-8 +214 209 -1334423.36669 +215 209 -4304591.50545 +216 209 4.842877388e-8 +256 209 -547025.666412 +257 209 -2077312.65726 +258 209 -1360828.33484 +259 209 131724.797327 +260 209 -28174.6046033 +261 209 6684.54707694 +262 209 -6040684.68178 +263 209 -800256.904552 +265 209 -625279.223852 +266 209 -2017029.75436 +267 209 1344116.96715 +210 210 36176122.0894 +214 210 1.30385160446e-8 +215 210 3.72529029846e-8 +216 210 830431.306934 +256 210 -358351.461509 +257 210 -1360828.33484 +258 210 -2284273.33935 +259 210 23329.9193232 +260 210 -6684.54707697 +261 210 -14301137.6219 +265 210 416676.259817 +266 210 1344116.96715 +267 210 -2296979.40748 +211 211 305878308.174 +212 211 66094123.115 +259 211 -800256.904552 +260 211 6040684.68178 +262 211 64525494.457 +263 211 -4237812.99363 +212 212 549336918.275 +259 212 -8988441.97885 +260 212 800256.904552 +262 212 -4237812.99363 +263 212 48915452.1011 +213 213 715555.555556 +264 213 -357777.777778 +214 214 1816739.3315 +215 214 5341279.87864 +216 214 -3.72529029846e-8 +217 214 -544032.43723 +218 214 -1483724.82881 +219 214 2.51457095146e-8 +259 214 -193836.559394 +260 214 -625279.223852 +261 214 -416676.259817 +265 214 24667.0078013 +266 214 73754.1287475 +267 214 -27106.375452 +268 214 -260348.273704 +269 214 -710040.746465 +270 214 484442.198447 +215 215 15814018.062 +216 215 -1.19209289551e-7 +217 215 -1483724.82881 +218 215 -4046522.26039 +219 215 6.70552253723e-8 +259 215 -625279.223852 +260 215 -2017029.75436 +261 215 -1344116.96715 +265 215 73754.1287475 +266 215 222052.369365 +267 215 9164.38855485 +268 215 -710040.746465 +269 215 -1936474.76309 +270 215 1321205.99576 +216 216 18448886.5844 +217 216 2.60770320892e-8 +218 216 6.70552253723e-8 +219 216 931107.693647 +259 216 -416676.259817 +260 216 -1344116.96715 +261 216 -2296979.40748 +265 216 27106.375452 +266 216 -9164.38855491 +267 216 -5492991.14412 +268 216 484442.198447 +269 216 1321205.99576 +270 216 -2315242.24089 +217 217 2558539.62181 +218 217 6138307.60949 +219 217 -5.58793544769e-8 +220 217 -759692.612592 +221 217 -1651505.67955 +222 217 2.79396772385e-8 +265 217 -260348.273704 +266 217 -710040.746465 +267 217 -484442.198447 +268 217 12227.6197723 +269 217 33038.3525623 +270 217 -41457.2556898 +271 217 -379286.632065 +272 217 -824536.156663 +273 217 588085.337671 +218 218 14915778.6682 +219 218 -1.41561031342e-7 +220 218 -1651505.67955 +221 218 -3590229.73815 +222 218 6.14672899246e-8 +265 218 -710040.746465 +266 218 -1936474.76309 +267 218 -1321205.99576 +268 218 33038.3525623 +269 218 89431.3340663 +270 218 17103.8438088 +271 218 -824536.156663 +272 218 -1792469.90579 +273 218 1278446.38624 +219 219 18669379.7869 +220 219 2.70083546639e-8 +221 219 5.77419996262e-8 +222 219 1124555.53479 +265 219 -484442.198447 +266 219 -1321205.99576 +267 219 -2315242.24089 +268 219 41457.2556898 +269 219 -17103.8438089 +270 219 -5695176.55864 +271 219 588085.337671 +272 219 1278446.38624 +273 219 -2352102.70813 +220 220 3880798.16119 +221 220 7110485.96967 +222 220 -4.09781932831e-8 +223 220 -1076372.38828 +224 220 -1736084.49722 +225 220 2.421438694e-8 +268 220 -379286.632065 +269 220 -824536.156663 +270 220 -588085.337671 +271 220 -52167.0393845 +272 220 -83826.4031562 +273 220 -61373.985991 +274 220 -590912.90871 +275 220 -953085.336629 +276 220 741520.302649 +221 221 13318817.2723 +222 221 -8.19563865662e-8 +223 221 -1736084.49722 +224 221 -2800136.28584 +225 221 3.91155481339e-8 +268 221 -824536.156663 +269 221 -1792469.90579 +270 221 -1278446.38624 +271 221 -83826.4031562 +272 221 -134521.304433 +273 221 32978.3592395 +274 221 -953085.336629 +275 221 -1537234.41392 +276 221 1196000.48814 +222 222 19146980.3477 +223 222 1.49011611938e-8 +224 222 2.79396772385e-8 +225 222 1520483.39551 +268 222 -588085.337671 +269 222 -1278446.38624 +270 222 -2352102.70813 +271 222 61373.985991 +272 222 -32978.3592395 +273 222 -6109264.54972 +274 222 741520.302649 +275 222 1196000.48814 +276 222 -2434642.38116 +223 223 6516876.42304 +224 223 8531914.96891 +225 223 -1.49011611938e-8 +226 223 -2626121.17726 +227 223 -2984228.61052 +228 223 1.30385160446e-8 +271 223 -590912.90871 +272 223 -953085.336629 +273 223 -741520.302649 +274 223 222027.67781 +275 223 227177.812693 +276 223 -75251.5247453 +277 223 -1038306.19785 +278 223 -1179893.40665 +279 223 929649.114512 +224 224 11512089.4984 +225 224 -1.49011611938e-8 +226 224 -2984228.61052 +227 224 -3391168.87559 +228 224 1.30385160446e-8 +271 224 -953085.336629 +272 224 -1537234.41392 +273 224 -1196000.48814 +274 224 227177.812693 +275 224 217630.207531 +276 224 55832.4159336 +277 224 -1179893.40665 +278 224 -1340787.9621 +279 224 1056419.44831 +225 225 18361731.7736 +226 225 3.72529029846e-9 +227 225 5.58793544769e-9 +228 225 -125380.553907 +271 225 -741520.302649 +272 225 -1196000.48814 +273 225 -2434642.38116 +274 225 75251.5247453 +275 225 -55832.4159336 +276 225 -5287984.36193 +277 225 929649.114512 +278 225 1056419.44831 +279 225 -2155790.5645 +226 226 11197028.9194 +227 226 7624235.1212 +228 226 2.23517417908e-8 +229 226 -1154846.00933 +230 226 -476225.158488 +231 226 -3.72529029846e-9 +274 226 -1038306.19785 +275 226 -1179893.40665 +276 226 -929649.114512 +277 226 -908773.635179 +278 226 -175831.894858 +279 226 -148519.569575 +280 226 -1760951.03338 +281 226 -726165.37459 +282 226 1300948.03845 +227 227 6560950.4012 +228 227 7.45058059692e-9 +229 227 -476225.158488 +230 227 -196381.508655 +231 227 -1.86264514923e-9 +274 227 -1179893.40665 +275 227 -1340787.9621 +276 227 -1056419.44831 +277 227 -175831.894858 +278 227 153537.59263 +279 227 207978.412157 +280 227 -726165.37459 +281 227 -299449.639006 +282 227 536473.417918 +228 228 21281133.3311 +229 228 -3.72529029846e-9 +230 228 -1.86264514923e-9 +231 228 4075797.18655 +274 228 -929649.114512 +275 228 -1056419.44831 +276 228 -2155790.5645 +277 228 148519.569575 +278 228 -207978.412157 +279 228 -7295491.64648 +280 228 1300948.03845 +281 228 536473.417918 +282 228 -3164492.7709 +229 229 14087608.2601 +230 229 -7.45058059692e-9 +231 229 7.45058059692e-9 +232 229 -1154846.00933 +233 229 476225.158488 +234 229 -3.72529029846e-9 +277 229 -1760951.03338 +278 229 -726165.37459 +279 229 -1300948.03845 +280 229 -2367056.05396 +281 229 1.11758708954e-8 +282 229 1.86264514923e-9 +283 229 -1760951.03338 +284 229 726165.37459 +285 229 1300948.03845 +230 230 2395597.11087 +231 230 -7.45058059692e-9 +232 230 476225.158488 +233 230 -196381.508655 +234 230 1.86264514923e-9 +277 230 -726165.37459 +278 230 -299449.639006 +279 230 -536473.417918 +280 230 9.31322574615e-9 +281 230 -402517.768767 +282 230 429178.734334 +283 230 726165.37459 +284 230 -299449.639006 +285 230 -536473.417918 +231 231 25315942.1547 +232 231 -3.72529029846e-9 +233 231 1.86264514923e-9 +234 231 4075797.18655 +277 231 -1300948.03845 +278 231 -536473.417918 +279 231 -3164492.7709 +281 231 -429178.734334 +282 231 -10404782.7221 +283 231 1300948.03845 +284 231 -536473.417918 +285 231 -3164492.7709 +232 232 11197028.9194 +233 232 -7624235.1212 +234 232 -1.49011611938e-8 +235 232 -2626121.17726 +236 232 2984228.61052 +237 232 -5.58793544769e-9 +280 232 -1760951.03338 +281 232 726165.37459 +282 232 -1300948.03845 +283 232 -908773.635179 +284 232 175831.894858 +285 232 148519.569575 +286 232 -1038306.19785 +287 232 1179893.40665 +288 232 929649.114512 +233 233 6560950.4012 +234 233 7.45058059692e-9 +235 233 2984228.61052 +236 233 -3391168.87559 +237 233 5.58793544769e-9 +280 233 726165.37459 +281 233 -299449.639006 +282 233 536473.417918 +283 233 175831.894858 +284 233 153537.59263 +285 233 207978.412157 +286 233 1179893.40665 +287 233 -1340787.9621 +288 233 -1056419.44831 +234 234 21281133.3311 +235 234 -1.49011611938e-8 +236 234 1.67638063431e-8 +237 234 -125380.553907 +280 234 -1300948.03845 +281 234 536473.417918 +282 234 -3164492.7709 +283 234 -148519.569575 +284 234 -207978.412157 +285 234 -7295491.64648 +286 234 929649.114512 +287 234 -1056419.44831 +288 234 -2155790.5645 +235 235 6516876.42304 +236 235 -8531914.96891 +237 235 3.72529029846e-8 +238 235 -1076372.38828 +239 235 1736084.49722 +240 235 -1.30385160446e-8 +283 235 -1038306.19785 +284 235 1179893.40665 +285 235 -929649.114512 +286 235 222027.67781 +287 235 -227177.812693 +288 235 75251.5247453 +289 235 -590912.90871 +290 235 953085.336629 +291 235 741520.302649 +236 236 11512089.4984 +237 236 -4.47034835815e-8 +238 236 1736084.49722 +239 236 -2800136.28584 +240 236 2.04890966415e-8 +283 236 1179893.40665 +284 236 -1340787.9621 +285 236 1056419.44831 +286 236 -227177.812693 +287 236 217630.207531 +288 236 55832.4159336 +289 236 953085.336629 +290 236 -1537234.41392 +291 236 -1196000.48814 +237 237 18361731.7736 +238 237 -2.23517417908e-8 +239 237 3.72529029846e-8 +240 237 1520483.39551 +283 237 -929649.114512 +284 237 1056419.44831 +285 237 -2155790.5645 +286 237 -75251.5247453 +287 237 -55832.4159336 +288 237 -5287984.36193 +289 237 741520.302649 +290 237 -1196000.48814 +291 237 -2434642.38116 +238 238 3880798.16119 +239 238 -7110485.96967 +240 238 3.72529029846e-8 +241 238 -759692.612592 +242 238 1651505.67955 +243 238 -1.86264514923e-8 +286 238 -590912.90871 +287 238 953085.336629 +288 238 -741520.302649 +289 238 -52167.0393845 +290 238 83826.4031562 +291 238 61373.985991 +292 238 -379286.632065 +293 238 824536.156663 +294 238 588085.337671 +239 239 13318817.2723 +240 239 -6.70552253723e-8 +241 239 1651505.67955 +242 239 -3590229.73815 +243 239 4.09781932831e-8 +286 239 953085.336629 +287 239 -1537234.41392 +288 239 1196000.48814 +289 239 83826.4031562 +290 239 -134521.304432 +291 239 32978.3592395 +292 239 824536.156663 +293 239 -1792469.90579 +294 239 -1278446.38624 +240 240 19146980.3477 +241 240 -2.51457095146e-8 +242 240 5.40167093277e-8 +243 240 1124555.53479 +286 240 -741520.302649 +287 240 1196000.48814 +288 240 -2434642.38116 +289 240 -61373.985991 +290 240 -32978.3592395 +291 240 -6109264.54972 +292 240 588085.337671 +293 240 -1278446.38624 +294 240 -2352102.70813 +241 241 2558539.62181 +242 241 -6138307.60949 +243 241 4.28408384323e-8 +244 241 -544032.43723 +245 241 1483724.82881 +246 241 -2.23517417908e-8 +289 241 -379286.632065 +290 241 824536.156663 +291 241 -588085.337671 +292 241 12227.6197723 +293 241 -33038.3525623 +294 241 41457.2556898 +295 241 -260348.273704 +296 241 710040.746465 +297 241 484442.198447 +242 242 14915778.6682 +243 242 -1.11758708954e-7 +244 242 1483724.82881 +245 242 -4046522.26039 +246 242 6.33299350738e-8 +289 242 824536.156663 +290 242 -1792469.90579 +291 242 1278446.38624 +292 242 -33038.3525623 +293 242 89431.3340663 +294 242 17103.8438088 +295 242 710040.746465 +296 242 -1936474.76309 +297 242 -1321205.99576 +243 243 18669379.7869 +244 243 -2.60770320892e-8 +245 243 7.45058059692e-8 +246 243 931107.693647 +289 243 -588085.337671 +290 243 1278446.38624 +291 243 -2352102.70813 +292 243 -41457.2556898 +293 243 -17103.8438089 +294 243 -5695176.55864 +295 243 484442.198447 +296 243 -1321205.99576 +297 243 -2315242.24089 +244 244 1816739.3315 +245 244 -5341279.87864 +246 244 3.35276126862e-8 +247 244 -413671.243673 +248 244 1334423.36669 +249 244 -6.51925802231e-9 +292 244 -260348.273704 +293 244 710040.746465 +294 244 -484442.198447 +295 244 24667.0078013 +296 244 -73754.1287475 +297 244 27106.375452 +298 244 -193836.559394 +299 244 625279.223852 +300 244 416676.259817 +245 245 15814018.062 +246 245 -9.685754776e-8 +247 245 1334423.36669 +248 245 -4304591.50545 +249 245 2.23517417908e-8 +292 245 710040.746465 +293 245 -1936474.76309 +294 245 1321205.99576 +295 245 -73754.1287475 +296 245 222052.369365 +297 245 9164.38855486 +298 245 625279.223852 +299 245 -2017029.75436 +300 245 -1344116.96715 +246 246 18448886.5844 +247 246 -9.31322574615e-9 +248 246 2.98023223877e-8 +249 246 830431.306934 +292 246 -484442.198447 +293 246 1321205.99576 +294 246 -2315242.24089 +295 246 -27106.375452 +296 246 -9164.38855491 +297 246 -5492991.14412 +298 246 416676.259817 +299 246 -1344116.96715 +300 246 -2296979.40748 +247 247 2350262.78049 +248 247 -4600302.86075 +249 247 1.86264514923e-8 +250 247 -7.45058059692e-9 +253 247 -311975.890718 +254 247 1184718.57235 +255 247 -1.02445483208e-8 +295 247 -193836.559394 +296 247 625279.223852 +297 247 -416676.259817 +298 247 -474421.1715 +299 247 -131724.429407 +300 247 23329.9193232 +301 247 -800250.281991 +302 247 8988445.57462 +304 247 -144050.092155 +305 247 547025.666412 +306 247 358351.461509 +248 248 17048556.4258 +249 248 -6.70552253723e-8 +250 248 5.96046447754e-8 +251 248 7.45058059692e-9 +253 248 1184718.57235 +254 248 -4498931.28739 +255 248 3.72529029846e-8 +295 248 625279.223852 +296 248 -2017029.75436 +297 248 1344116.96715 +298 248 -131724.429407 +299 248 -28174.4048384 +300 248 6684.54707695 +301 248 -6040681.08601 +302 248 800250.281991 +304 248 547025.666412 +305 248 -2077312.65726 +306 248 -1360828.33484 +249 249 36176122.0894 +253 249 -1.210719347e-8 +254 249 4.09781932831e-8 +255 249 758227.34113 +295 249 -416676.259817 +296 249 1344116.96715 +297 249 -2296979.40748 +298 249 -23329.9193232 +299 249 -6684.54707698 +300 249 -14301137.6219 +304 249 358351.461509 +305 249 -1360828.33484 +306 249 -2284273.33935 +250 250 305878011.196 +251 250 -66093576.1502 +298 250 800250.281991 +299 250 6040681.08601 +301 250 64525513.4986 +302 250 4237777.92342 +251 251 549337215.254 +298 251 -8988445.57462 +299 251 -800250.281991 +301 251 4237777.92342 +302 251 48915433.0595 +252 252 715555.555556 +303 252 -357777.777778 +253 253 1011851.60912 +254 253 -4110093.43272 +255 253 6.33299350738e-8 +298 253 -144050.092155 +299 253 547025.666412 +300 253 -358351.461509 +304 253 22134.9913963 +305 253 -90320.994053 +306 253 18909.0650259 +254 254 16788621.6609 +255 254 -2.60770320892e-7 +298 254 547025.666412 +299 254 -2077312.65726 +300 254 1360828.33484 +304 254 -90320.994053 +305 254 370626.696215 +306 254 4631.25243446 +255 255 18240145.4814 +298 255 -358351.461509 +299 255 1360828.33484 +300 255 -2284273.33935 +304 255 -18909.0650259 +305 255 -4631.25243459 +306 255 -5293544.01962 +256 256 983097.517609 +257 256 3993191.05725 +258 256 -3.72529029846e-8 +259 256 -268071.177508 +260 256 -1017991.81332 +261 256 1.39698386192e-8 +257 257 16310675.5472 +258 257 -1.56462192535e-7 +259 257 -1017991.81332 +260 257 -3865791.69616 +261 257 4.842877388e-8 +258 258 19175351.0344 +259 258 7.45058059692e-9 +260 258 2.60770320892e-8 +261 258 1324183.34296 +259 259 2528206.57902 +260 259 4472508.77878 +261 259 -1.86264514923e-8 +262 259 -161794.959894 +263 259 1643725.47145 +265 259 -354732.167298 +266 259 -1144297.31386 +267 259 1.76951289177e-8 +307 259 -183325.791023 +308 259 -591373.519429 +309 259 416676.259817 +260 260 16817023.5501 +261 260 -6.70552253723e-8 +262 260 -2239699.42799 +263 260 161794.959894 +265 260 -1144297.31386 +266 260 -3691281.65763 +267 260 5.21540641785e-8 +307 260 -591373.519429 +308 260 -1907656.51429 +309 260 1344116.96715 +261 261 39001825.1235 +265 261 1.39698386192e-8 +266 261 4.09781932831e-8 +267 261 1397983.15475 +307 261 416676.259817 +308 261 1344116.96715 +309 261 -2538358.87127 +262 262 329627273.449 +263 262 65098710.708 +263 263 569419268.28 +264 264 790573.476703 +265 265 1768961.88645 +266 265 5200497.8108 +267 265 -3.91155481339e-8 +268 265 -465133.879389 +269 265 -1268546.94379 +270 265 2.32830643654e-8 +307 265 -89281.9039061 +308 265 -261158.775213 +309 265 -27106.375452 +310 265 -246970.319543 +311 265 -673555.416935 +312 265 484442.198447 +266 266 15396260.6937 +267 266 -1.26659870148e-7 +268 266 -1268546.94379 +269 266 -3459673.48306 +270 266 6.70552253723e-8 +307 266 -261158.775213 +308 266 -769227.571739 +309 266 9164.38855484 +310 266 -673555.416935 +311 266 -1836969.31891 +312 266 1321205.99576 +267 267 19430253.1852 +268 267 1.67638063431e-8 +269 267 4.09781932831e-8 +270 267 1501024.18544 +307 267 27106.375452 +308 267 -9164.38855489 +309 267 -7121142.78387 +310 267 484442.198447 +311 267 1321205.99576 +312 267 -2564546.07772 +268 268 2497002.01247 +269 268 5989724.5569 +270 268 -4.47034835815e-8 +271 268 -645500.420471 +272 268 -1403261.78363 +273 268 2.98023223877e-8 +307 268 -246970.319543 +308 268 -673555.416935 +309 268 -484442.198447 +310 268 -150094.32553 +311 268 -356091.902114 +312 268 -41457.2556898 +313 268 -361895.781536 +314 268 -786729.959862 +315 268 588085.337671 +269 269 14552393.0113 +270 269 -9.685754776e-8 +271 269 -1403261.78363 +272 269 -3050569.09485 +273 269 6.33299350738e-8 +307 269 -673555.416935 +308 269 -1836969.31891 +309 269 -1321205.99576 +310 269 -356091.902114 +311 269 -855385.258208 +312 269 17103.8438088 +313 269 -786729.959862 +314 269 -1710282.52144 +315 269 1278446.38624 +270 270 19697338.3132 +271 270 2.51457095146e-8 +272 270 5.40167093277e-8 +273 270 1699467.28443 +307 270 -484442.198447 +308 270 -1321205.99576 +309 270 -2564546.07772 +310 270 41457.2556898 +311 270 -17103.8438089 +312 270 -7353984.06298 +313 270 588085.337671 +314 270 1278446.38624 +315 270 -2616778.13471 +271 271 3805513.9161 +272 271 6969546.89216 +273 271 -4.09781932831e-8 +274 271 -900913.14915 +275 271 -1453085.72444 +276 271 2.23517417908e-8 +310 271 -361895.781536 +311 271 -786729.959862 +312 271 -588085.337671 +313 271 -304176.348108 +314 271 -544599.533137 +315 271 -61373.985991 +316 271 -570661.636677 +317 271 -920421.994641 +318 271 741520.302649 +272 272 13049076.8844 +273 272 -8.19563865662e-8 +274 272 -1453085.72444 +275 272 -2343686.65232 +276 272 3.72529029846e-8 +310 272 -786729.959862 +311 272 -1710282.52144 +312 272 -1278446.38624 +313 272 -544599.533137 +314 272 -995761.387375 +315 272 32978.3592395 +316 272 -920421.994641 +317 272 -1484551.60426 +318 272 1196000.48814 +273 273 20269767.9526 +274 273 2.23517417908e-8 +275 273 3.53902578354e-8 +276 273 2107431.22341 +310 273 -588085.337671 +311 273 -1278446.38624 +312 273 -2616778.13471 +313 273 61373.985991 +314 273 -32978.3592395 +315 273 -7832517.92941 +316 273 741520.302649 +317 273 1196000.48814 +318 273 -2731360.75727 +274 274 6311393.43185 +275 274 8279110.50413 +276 274 -3.35276126862e-8 +277 274 -2301041.15788 +278 274 -2614819.49759 +279 274 2.23517417908e-8 +313 274 -570661.636677 +314 274 -920421.994641 +315 274 -741520.302649 +316 274 -175770.085153 +317 274 -298827.840692 +318 274 -75251.5247453 +319 274 -955815.974241 +320 274 -1086154.51618 +321 274 929649.114512 +275 275 11193680.9464 +276 275 -5.21540641785e-8 +277 275 -2614819.49759 +278 275 -2971385.79271 +279 275 2.60770320892e-8 +313 275 -920421.994641 +314 275 -1484551.60426 +315 275 -1196000.48814 +316 275 -298827.840692 +317 275 -499398.232929 +318 275 55832.4159336 +319 275 -1086154.51618 +320 275 -1234266.49566 +321 275 1056419.44831 +276 276 19292741.7183 +277 276 1.67638063431e-8 +278 276 1.86264514923e-8 +279 276 427942.721782 +313 276 -741520.302649 +314 276 -1196000.48814 +315 276 -2731360.75727 +316 276 75251.5247453 +317 276 -55832.4159336 +318 276 -6893760.43768 +319 276 929649.114512 +320 276 1056419.44831 +321 276 -2324577.161 +277 277 11173430.2083 +278 277 7495059.08719 +279 277 -5.21540641785e-8 +280 277 -678836.974673 +281 277 -279932.773061 +282 277 9.31322574615e-9 +316 277 -955815.974241 +317 277 -1086154.51618 +318 277 -929649.114512 +319 277 -1698063.33368 +320 277 -676945.37624 +321 277 -148519.569575 +322 277 -1831641.90145 +323 277 -755316.248019 +324 277 1300948.03845 +278 278 6371949.42588 +279 278 -1.86264514923e-8 +280 278 -279932.773061 +281 278 -115436.195077 +282 278 2.79396772385e-9 +316 278 -1086154.51618 +317 278 -1234266.49566 +318 278 -1056419.44831 +319 278 -676945.37624 +320 278 -252690.316209 +321 278 207978.412157 +322 278 -755316.248019 +323 278 -311470.61774 +324 278 536473.417918 +279 279 22658086.7563 +280 279 -1.86264514923e-9 +282 279 4790763.62708 +316 279 -929649.114512 +317 279 -1056419.44831 +318 279 -2324577.161 +319 279 148519.569575 +320 279 -207978.412157 +321 279 -9252258.07493 +322 279 1300948.03845 +323 279 536473.417918 +324 279 -3684182.88731 +280 280 14370371.7322 +281 280 7.45058059692e-9 +282 280 -7.45058059692e-9 +283 280 -678836.974673 +284 280 279932.773061 +285 280 9.31322574615e-9 +319 280 -1831641.90145 +320 280 -755316.248019 +321 280 -1300948.03845 +322 280 -3460455.85927 +323 280 -3.72529029846e-9 +324 280 -5.58793544769e-9 +325 280 -1831641.90145 +326 280 755316.248019 +327 280 1300948.03845 +281 281 2443681.02578 +282 281 1.11758708954e-8 +283 281 279932.773061 +284 281 -115436.195077 +285 281 -2.79396772385e-9 +319 281 -755316.248019 +320 281 -311470.61774 +321 281 -536473.417918 +322 281 -3.72529029846e-9 +323 281 -588450.353367 +324 281 429178.734334 +325 281 755316.248019 +326 281 -311470.61774 +327 281 -536473.417918 +282 282 27394702.6193 +283 282 -1.86264514923e-9 +285 282 4790763.62708 +319 282 -1300948.03845 +320 282 -536473.417918 +321 282 -3684182.88731 +322 282 3.72529029846e-9 +323 282 -429178.734334 +324 282 -12874095.835 +325 282 1300948.03845 +326 282 -536473.417918 +327 282 -3684182.88731 +283 283 11173430.2083 +284 283 -7495059.08719 +285 283 2.98023223877e-8 +286 283 -2301041.15788 +287 283 2614819.49759 +288 283 -5.58793544769e-9 +322 283 -1831641.90145 +323 283 755316.248019 +324 283 -1300948.03845 +325 283 -1698063.33368 +326 283 676945.37624 +327 283 148519.569575 +328 283 -955815.974241 +329 283 1086154.51618 +330 283 929649.114512 +284 284 6371949.42588 +286 284 2614819.49759 +287 284 -2971385.79271 +288 284 3.72529029846e-9 +322 284 755316.248019 +323 284 -311470.61774 +324 284 536473.417918 +325 284 676945.37624 +326 284 -252690.316209 +327 284 207978.412157 +328 284 1086154.51618 +329 284 -1234266.49566 +330 284 -1056419.44831 +285 285 22658086.7563 +286 285 -9.31322574615e-9 +287 285 1.30385160446e-8 +288 285 427942.721782 +322 285 -1300948.03845 +323 285 536473.417918 +324 285 -3684182.88731 +325 285 -148519.569575 +326 285 -207978.412157 +327 285 -9252258.07493 +328 285 929649.114512 +329 285 -1056419.44831 +330 285 -2324577.161 +286 286 6311393.43185 +287 286 -8279110.50413 +288 286 2.98023223877e-8 +289 286 -900913.14915 +290 286 1453085.72444 +291 286 -1.67638063431e-8 +325 286 -955815.974241 +326 286 1086154.51618 +327 286 -929649.114512 +328 286 -175770.085153 +329 286 298827.840692 +330 286 75251.5247453 +331 286 -570661.636677 +332 286 920421.994641 +333 286 741520.302649 +287 287 11193680.9464 +288 287 -4.47034835815e-8 +289 287 1453085.72444 +290 287 -2343686.65232 +291 287 2.60770320892e-8 +325 287 1086154.51618 +326 287 -1234266.49566 +327 287 1056419.44831 +328 287 298827.840692 +329 287 -499398.232929 +330 287 55832.4159336 +331 287 920421.994641 +332 287 -1484551.60426 +333 287 -1196000.48814 +288 288 19292741.7183 +289 288 -1.67638063431e-8 +290 288 2.79396772385e-8 +291 288 2107431.22341 +325 288 -929649.114512 +326 288 1056419.44831 +327 288 -2324577.161 +328 288 -75251.5247453 +329 288 -55832.4159336 +330 288 -6893760.43768 +331 288 741520.302649 +332 288 -1196000.48814 +333 288 -2731360.75727 +289 289 3805513.9161 +290 289 -6969546.89216 +291 289 2.98023223877e-8 +292 289 -645500.420471 +293 289 1403261.78363 +294 289 -2.04890966415e-8 +328 289 -570661.636677 +329 289 920421.994641 +330 289 -741520.302649 +331 289 -304176.348108 +332 289 544599.533137 +333 289 61373.985991 +334 289 -361895.781536 +335 289 786729.959862 +336 289 588085.337671 +290 290 13049076.8844 +291 290 -5.96046447754e-8 +292 290 1403261.78363 +293 290 -3050569.09485 +294 290 4.47034835815e-8 +328 290 920421.994641 +329 290 -1484551.60426 +330 290 1196000.48814 +331 290 544599.533137 +332 290 -995761.387374 +333 290 32978.3592395 +334 290 786729.959862 +335 290 -1710282.52144 +336 290 -1278446.38624 +291 291 20269767.9526 +292 291 -2.51457095146e-8 +293 291 5.40167093277e-8 +294 291 1699467.28443 +328 291 -741520.302649 +329 291 1196000.48814 +330 291 -2731360.75727 +331 291 -61373.985991 +332 291 -32978.3592395 +333 291 -7832517.9294 +334 291 588085.337671 +335 291 -1278446.38624 +336 291 -2616778.13471 +292 292 2497002.01247 +293 292 -5989724.5569 +294 292 3.53902578354e-8 +295 292 -465133.879389 +296 292 1268546.94379 +297 292 -1.95577740669e-8 +331 292 -361895.781536 +332 292 786729.959862 +333 292 -588085.337671 +334 292 -150094.32553 +335 292 356091.902114 +336 292 41457.2556898 +337 292 -246970.319543 +338 292 673555.416935 +339 292 484442.198447 +293 293 14552393.0113 +294 293 -8.19563865662e-8 +295 293 1268546.94379 +296 293 -3459673.48306 +297 293 4.842877388e-8 +331 293 786729.959862 +332 293 -1710282.52144 +333 293 1278446.38624 +334 293 356091.902114 +335 293 -855385.258208 +336 293 17103.8438089 +337 293 673555.416935 +338 293 -1836969.31891 +339 293 -1321205.99576 +294 294 19697338.3132 +295 294 -2.98023223877e-8 +296 294 8.94069671631e-8 +297 294 1501024.18544 +331 294 -588085.337671 +332 294 1278446.38624 +333 294 -2616778.13471 +334 294 -41457.2556898 +335 294 -17103.8438089 +336 294 -7353984.06298 +337 294 484442.198447 +338 294 -1321205.99576 +339 294 -2564546.07772 +295 295 1768961.88645 +296 295 -5200497.8108 +297 295 4.28408384323e-8 +298 295 -354732.167298 +299 295 1144297.31386 +300 295 -7.45058059692e-9 +334 295 -246970.319543 +335 295 673555.416935 +336 295 -484442.198447 +337 295 -89281.9039061 +338 295 261158.775213 +339 295 27106.375452 +296 296 15396260.6937 +297 296 -1.26659870148e-7 +298 296 1144297.31386 +299 296 -3691281.65763 +300 296 2.60770320892e-8 +334 296 673555.416935 +335 296 -1836969.31891 +336 296 1321205.99576 +337 296 261158.775213 +338 296 -769227.571739 +339 296 9164.38855485 +297 297 19430253.1852 +298 297 -1.02445483208e-8 +299 297 3.35276126862e-8 +300 297 1397983.15475 +334 297 -484442.198447 +335 297 1321205.99576 +336 297 -2564546.07772 +337 297 -27106.375452 +338 297 -9164.38855489 +339 297 -7121142.78387 +298 298 2528206.97157 +299 298 -4472509.50178 +300 298 1.30385160446e-8 +301 298 161793.620953 +302 298 1643724.74446 +304 298 -268071.177508 +305 298 1017991.81332 +306 298 -7.45058059692e-9 +337 298 -183325.791023 +338 298 591373.519429 +339 298 -416676.259817 +299 299 16817023.1576 +300 299 -4.47034835815e-8 +301 299 -2239700.15498 +302 299 -161793.620953 +304 299 1017991.81332 +305 299 -3865791.69616 +306 299 2.98023223877e-8 +337 299 591373.519429 +338 299 -1907656.51429 +339 299 1344116.96715 +300 300 39001825.1235 +304 300 -1.58324837685e-8 +305 300 5.58793544769e-8 +306 300 1324183.34296 +337 300 -416676.259817 +338 300 1344116.96715 +339 300 -2538358.87127 +301 301 329626980.943 +302 301 -65098171.9808 +302 302 569419560.785 +303 303 790573.476703 +304 304 983097.517609 +305 304 -3993191.05725 +306 304 5.21540641785e-8 +305 305 16310675.5472 +306 305 -2.16066837311e-7 +306 306 19175351.0344 +307 307 1902223.18075 +308 307 5590205.99024 +309 307 -2.421438694e-8 +310 307 -77037.3094958 +311 307 -210101.75317 +312 307 1.86264514923e-8 +340 307 -718178.246541 +341 307 -2108958.17516 +342 307 -27106.375452 +343 307 -300633.375282 +344 307 -819909.205315 +345 307 484442.198447 +308 308 16543837.5893 +309 308 -8.19563865662e-8 +310 308 -210101.75317 +311 308 -573004.781374 +312 308 4.842877388e-8 +340 308 -2108958.17516 +341 308 -6236511.30779 +342 308 9164.38855485 +343 308 -819909.205315 +344 308 -2236116.0145 +345 308 1321205.99576 +309 309 30501291.4656 +310 309 2.421438694e-8 +311 309 6.70552253723e-8 +312 309 5508023.65258 +340 309 27106.375452 +341 309 -9164.3885549 +342 309 -19001911.4947 +343 309 484442.198447 +344 309 1321205.99576 +345 309 -5113778.6239 +310 310 2722764.08058 +311 310 6525095.96362 +312 310 -4.842877388e-8 +313 310 -76937.6348729 +314 310 -167255.727985 +315 310 2.32830643654e-8 +340 310 -300633.375282 +341 310 -819909.205315 +342 310 -484442.198447 +343 310 -1057312.76972 +344 310 -2529098.59693 +345 310 -41457.2556898 +346 310 -451882.564601 +347 310 -982353.401306 +348 310 588085.337671 +311 311 15837837.4467 +312 311 -1.26659870148e-7 +313 311 -167255.727985 +314 311 -363599.408662 +315 311 5.21540641785e-8 +340 311 -819909.205315 +341 311 -2236116.0145 +342 311 -1321205.99576 +343 311 -2529098.59693 +344 311 -6126929.2712 +345 311 17103.8438088 +346 311 -982353.401306 +347 311 -2135550.8724 +348 311 1278446.38624 +312 312 31131706.3634 +313 312 3.16649675369e-8 +314 312 6.89178705216e-8 +315 312 5787467.22392 +340 312 -484442.198447 +341 312 -1321205.99576 +342 312 -5113778.6239 +343 312 41457.2556898 +344 312 -17103.8438089 +345 312 -19507359.9875 +346 312 588085.337671 +347 312 1278446.38624 +348 312 -5270750.35301 +313 313 4267595.81742 +314 313 7796294.34309 +315 313 -5.21540641785e-8 +316 313 -4790.05038338 +317 313 -7725.88771512 +318 313 2.04890966415e-8 +343 313 -451882.564601 +344 313 -982353.401306 +345 313 -588085.337671 +346 313 -1747893.8743 +347 313 -3178566.02079 +348 313 -61373.985991 +349 313 -749357.926948 +350 313 -1208641.81766 +351 313 741520.302649 +314 314 14559614.5621 +315 314 -9.685754776e-8 +316 314 -7725.88771512 +317 314 -12461.109218 +318 314 2.98023223877e-8 +343 314 -982353.401306 +344 314 -2135550.8724 +345 314 -1278446.38624 +346 314 -3178566.02079 +347 314 -5907985.37222 +348 314 32978.3592395 +349 314 -1208641.81766 +350 314 -1949422.28655 +351 314 1196000.48814 +315 315 32450763.4987 +316 315 1.11758708954e-8 +317 315 2.421438694e-8 +318 315 6373223.13441 +343 315 -588085.337671 +344 315 -1278446.38624 +345 315 -5270750.35301 +346 315 61373.985991 +347 315 -32978.3592395 +348 315 -20553554.1703 +349 315 741520.302649 +350 315 1196000.48814 +351 315 -5606492.51233 +316 316 6563466.92161 +317 316 8716568.29938 +318 316 -2.23517417908e-8 +319 316 -797223.130676 +320 316 -905935.375768 +321 316 1.86264514923e-8 +346 316 -749357.926948 +347 316 -1208641.81766 +348 316 -741520.302649 +349 316 -2303950.19297 +350 316 -3145795.04337 +351 316 -75251.5247453 +352 316 -1005897.92455 +353 316 -1143065.82336 +354 316 929649.114512 +317 317 11934357.6383 +318 317 -3.72529029846e-8 +319 317 -905935.375768 +320 317 -1029472.01792 +321 317 2.04890966415e-8 +346 317 -1208641.81766 +347 317 -1949422.28655 +348 317 -1196000.48814 +349 317 -3145795.04337 +350 317 -4425847.45616 +351 317 55832.4159336 +352 317 -1143065.82336 +353 317 -1298938.43563 +354 317 1056419.44831 +318 318 30008520.3637 +319 318 1.30385160446e-8 +320 318 1.86264514923e-8 +321 318 4063389.39899 +346 318 -741520.302649 +347 318 -1196000.48814 +348 318 -5606492.51233 +349 318 75251.5247453 +350 318 -55832.4159336 +351 318 -18547112.2702 +352 318 929649.114512 +353 318 1056419.44831 +354 318 -4341829.75862 +319 319 13789595.5493 +320 319 8526963.46189 +321 319 -1.49011611938e-8 +322 319 2189391.67462 +323 319 902841.927677 +324 319 3.72529029846e-9 +349 319 -1005897.92455 +350 319 -1143065.82336 +351 319 -929649.114512 +352 319 -6588902.98151 +353 319 -3583442.90451 +354 319 -148519.569575 +355 319 -3101441.97781 +356 319 -1278945.14549 +357 319 1300948.03845 +320 320 6744151.21474 +321 320 -2.23517417908e-8 +322 320 902841.927677 +323 320 372305.949557 +324 320 2.79396772385e-9 +349 320 -1143065.82336 +350 320 -1298938.43563 +351 320 -1056419.44831 +352 320 -3583442.90451 +353 320 -2462219.22114 +354 320 207978.412157 +355 320 -1278945.14549 +356 320 -527400.059996 +357 320 536473.417918 +321 321 37051709.8254 +322 321 1.86264514923e-9 +324 321 10537577.5004 +349 321 -929649.114512 +350 321 -1056419.44831 +351 321 -4341829.75862 +352 321 148519.569575 +353 321 -207978.412157 +354 321 -23874563.7281 +355 321 1300948.03845 +356 321 536473.417918 +357 321 -8175265.11488 +322 322 19732335.5073 +323 322 -1.49011611938e-8 +324 322 -7.45058059692e-9 +325 322 2189391.67462 +326 322 -902841.927677 +327 322 3.72529029846e-9 +352 322 -3101441.97781 +353 322 -1278945.14549 +354 322 -1300948.03845 +355 322 -10784495.2388 +356 322 1.49011611938e-8 +358 322 -3101441.97781 +359 322 1278945.14549 +360 322 1300948.03845 +323 323 3355482.70929 +325 323 -902841.927677 +326 323 372305.949557 +327 323 -2.79396772385e-9 +352 323 -1278945.14549 +353 323 -527400.059996 +354 323 -536473.417918 +355 323 1.49011611938e-8 +356 323 -1833902.89956 +357 323 429178.734334 +358 323 1278945.14549 +359 323 -527400.059996 +360 323 -536473.417918 +324 324 47437791.9854 +325 324 1.86264514923e-9 +327 324 10537577.5004 +352 324 -1300948.03845 +353 324 -536473.417918 +354 324 -8175265.11488 +355 324 5.58793544769e-9 +356 324 -429178.734334 +357 324 -31919955.1469 +358 324 1300948.03845 +359 324 -536473.417918 +360 324 -8175265.11488 +325 325 13789595.5493 +326 325 -8526963.46189 +327 325 3.72529029846e-9 +328 325 -797223.130676 +329 325 905935.375768 +330 325 -1.11758708954e-8 +355 325 -3101441.97781 +356 325 1278945.14549 +357 325 -1300948.03845 +358 325 -6588902.98151 +359 325 3583442.90451 +360 325 148519.569575 +361 325 -1005897.92455 +362 325 1143065.82336 +363 325 929649.114512 +326 326 6744151.21474 +327 326 -2.23517417908e-8 +328 326 905935.375768 +329 326 -1029472.01792 +330 326 1.30385160446e-8 +355 326 1278945.14549 +356 326 -527400.059996 +357 326 536473.417918 +358 326 3583442.90451 +359 326 -2462219.22114 +360 326 207978.412157 +361 326 1143065.82336 +362 326 -1298938.43563 +363 326 -1056419.44831 +327 327 37051709.8254 +328 327 -2.23517417908e-8 +329 327 2.23517417908e-8 +330 327 4063389.39899 +355 327 -1300948.03845 +356 327 536473.417918 +357 327 -8175265.11488 +358 327 -148519.569575 +359 327 -207978.412157 +360 327 -23874563.7281 +361 327 929649.114512 +362 327 -1056419.44831 +363 327 -4341829.75862 +328 328 6563466.92161 +329 328 -8716568.29938 +330 328 1.11758708954e-8 +331 328 -4790.05038338 +332 328 7725.88771513 +333 328 -1.49011611938e-8 +358 328 -1005897.92455 +359 328 1143065.82336 +360 328 -929649.114512 +361 328 -2303950.19297 +362 328 3145795.04337 +363 328 75251.5247453 +364 328 -749357.926948 +365 328 1208641.81766 +366 328 741520.302649 +329 329 11934357.6383 +330 329 -1.49011611938e-8 +331 329 7725.88771514 +332 329 -12461.109218 +333 329 2.60770320892e-8 +358 329 1143065.82336 +359 329 -1298938.43563 +360 329 1056419.44831 +361 329 3145795.04337 +362 329 -4425847.45616 +363 329 55832.4159336 +364 329 1208641.81766 +365 329 -1949422.28655 +366 329 -1196000.48814 +330 330 30008520.3637 +331 330 -1.67638063431e-8 +332 330 2.421438694e-8 +333 330 6373223.13441 +358 330 -929649.114512 +359 330 1056419.44831 +360 330 -4341829.75862 +361 330 -75251.5247453 +362 330 -55832.4159336 +363 330 -18547112.2702 +364 330 741520.302649 +365 330 -1196000.48814 +366 330 -5606492.51233 +331 331 4267595.81742 +332 331 -7796294.34309 +333 331 4.09781932831e-8 +334 331 -76937.6348729 +335 331 167255.727985 +336 331 -1.95577740669e-8 +361 331 -749357.926948 +362 331 1208641.81766 +363 331 -741520.302649 +364 331 -1747893.8743 +365 331 3178566.02079 +366 331 61373.985991 +367 331 -451882.564601 +368 331 982353.401306 +369 331 588085.337671 +332 332 14559614.5621 +333 332 -8.19563865662e-8 +334 332 167255.727985 +335 332 -363599.408662 +336 332 4.28408384323e-8 +361 332 1208641.81766 +362 332 -1949422.28655 +363 332 1196000.48814 +364 332 3178566.02079 +365 332 -5907985.37222 +366 332 32978.3592395 +367 332 982353.401306 +368 332 -2135550.8724 +369 332 -1278446.38624 +333 333 32450763.4987 +334 333 -1.76951289177e-8 +335 333 3.72529029846e-8 +336 333 5787467.22392 +361 333 -741520.302649 +362 333 1196000.48814 +363 333 -5606492.51233 +364 333 -61373.985991 +365 333 -32978.3592395 +366 333 -20553554.1703 +367 333 588085.337671 +368 333 -1278446.38624 +369 333 -5270750.35301 +334 334 2722764.08058 +335 334 -6525095.96362 +336 334 3.91155481339e-8 +337 334 -77037.3094958 +338 334 210101.75317 +339 334 -2.51457095146e-8 +364 334 -451882.564601 +365 334 982353.401306 +366 334 -588085.337671 +367 334 -1057312.76972 +368 334 2529098.59693 +369 334 41457.2556898 +370 334 -300633.375282 +371 334 819909.205315 +372 334 484442.198447 +335 335 15837837.4467 +336 335 -1.04308128357e-7 +337 335 210101.75317 +338 335 -573004.781374 +339 335 7.45058059692e-8 +364 335 982353.401306 +365 335 -2135550.8724 +366 335 1278446.38624 +367 335 2529098.59693 +368 335 -6126929.2712 +369 335 17103.8438089 +370 335 819909.205315 +371 335 -2236116.0145 +372 335 -1321205.99576 +336 336 31131706.3633 +337 336 -2.32830643654e-8 +338 336 6.33299350738e-8 +339 336 5508023.65258 +364 336 -588085.337671 +365 336 1278446.38624 +366 336 -5270750.35301 +367 336 -41457.2556898 +368 336 -17103.8438089 +369 336 -19507359.9875 +370 336 484442.198447 +371 336 -1321205.99576 +372 336 -5113778.6239 +337 337 1902223.18075 +338 337 -5590205.99024 +339 337 2.60770320892e-8 +367 337 -300633.375282 +368 337 819909.205315 +369 337 -484442.198447 +370 337 -718178.246541 +371 337 2108958.17516 +372 337 27106.375452 +338 338 16543837.5893 +339 338 -7.45058059692e-8 +367 338 819909.205315 +368 338 -2236116.0145 +369 338 1321205.99576 +370 338 2108958.17516 +371 338 -6236511.30779 +372 338 9164.38855486 +339 339 30501291.4656 +367 339 -484442.198447 +368 339 1321205.99576 +369 339 -5113778.6239 +370 339 -27106.375452 +371 339 -9164.38855491 +372 339 -19001911.4947 +340 340 1902223.18075 +341 340 5590205.99024 +342 340 -4.842877388e-8 +343 340 -77037.3094958 +344 340 -210101.75317 +345 340 2.70083546639e-8 +376 340 -183325.791023 +377 340 -591373.519429 +378 340 -416676.259817 +382 340 -89281.9039061 +383 340 -261158.775213 +384 340 -27106.375452 +385 340 -246970.319543 +386 340 -673555.416935 +387 340 484442.198447 +341 341 16543837.5893 +342 341 -1.41561031342e-7 +343 341 -210101.75317 +344 341 -573004.781374 +345 341 7.45058059692e-8 +376 341 -591373.519429 +377 341 -1907656.51429 +378 341 -1344116.96715 +382 341 -261158.775213 +383 341 -769227.571739 +384 341 9164.38855484 +385 341 -673555.416935 +386 341 -1836969.31891 +387 341 1321205.99576 +342 342 30501291.4656 +343 342 1.58324837685e-8 +344 342 4.09781932831e-8 +345 342 5508023.65258 +376 342 -416676.259817 +377 342 -1344116.96715 +378 342 -2538358.87127 +382 342 27106.375452 +383 342 -9164.38855489 +384 342 -7121142.78386 +385 342 484442.198447 +386 342 1321205.99576 +387 342 -2564546.07772 +343 343 2722764.08058 +344 343 6525095.96362 +345 343 -5.21540641785e-8 +346 343 -76937.6348729 +347 343 -167255.727985 +348 343 3.35276126862e-8 +382 343 -246970.319543 +383 343 -673555.416935 +384 343 -484442.198447 +385 343 -150094.325529 +386 343 -356091.902114 +387 343 -41457.2556898 +388 343 -361895.781536 +389 343 -786729.959862 +390 343 588085.337671 +344 344 15837837.4467 +345 344 -1.11758708954e-7 +346 344 -167255.727985 +347 344 -363599.408662 +348 344 7.264316082e-8 +382 344 -673555.416935 +383 344 -1836969.31891 +384 344 -1321205.99576 +385 344 -356091.902114 +386 344 -855385.258208 +387 344 17103.8438088 +388 344 -786729.959862 +389 344 -1710282.52144 +390 344 1278446.38624 +345 345 31131706.3633 +346 345 1.86264514923e-8 +347 345 4.09781932831e-8 +348 345 5787467.22392 +382 345 -484442.198447 +383 345 -1321205.99576 +384 345 -2564546.07772 +385 345 41457.2556898 +386 345 -17103.8438089 +387 345 -7353984.06298 +388 345 588085.337671 +389 345 1278446.38624 +390 345 -2616778.13471 +346 346 4267595.81742 +347 346 7796294.34309 +348 346 -2.23517417908e-8 +349 346 -4790.05038347 +350 346 -7725.88771528 +351 346 1.86264514923e-8 +385 346 -361895.781536 +386 346 -786729.959862 +387 346 -588085.337671 +388 346 -304176.348108 +389 346 -544599.533136 +390 346 -61373.985991 +391 346 -570661.636677 +392 346 -920421.994641 +393 346 741520.302649 +347 347 14559614.5621 +348 347 -5.21540641785e-8 +349 347 -7725.88771528 +350 347 -12461.1092182 +351 347 3.16649675369e-8 +385 347 -786729.959862 +386 347 -1710282.52144 +387 347 -1278446.38624 +388 347 -544599.533136 +389 347 -995761.387374 +390 347 32978.3592395 +391 347 -920421.994641 +392 347 -1484551.60426 +393 347 1196000.48814 +348 348 32450763.4987 +349 348 1.67638063431e-8 +350 348 2.421438694e-8 +351 348 6373223.13441 +385 348 -588085.337671 +386 348 -1278446.38624 +387 348 -2616778.13471 +388 348 61373.985991 +389 348 -32978.3592395 +390 348 -7832517.9294 +391 348 741520.302649 +392 348 1196000.48814 +393 348 -2731360.75727 +349 349 6563466.92161 +350 349 8716568.29938 +351 349 -4.47034835815e-8 +352 349 -797223.130676 +353 349 -905935.375768 +354 349 1.86264514923e-8 +388 349 -570661.636677 +389 349 -920421.994641 +390 349 -741520.302649 +391 349 -175770.085153 +392 349 -298827.840692 +393 349 -75251.5247453 +394 349 -955815.974241 +395 349 -1086154.51618 +396 349 929649.114512 +350 350 11934357.6383 +351 350 -5.21540641785e-8 +352 350 -905935.375768 +353 350 -1029472.01792 +354 350 2.421438694e-8 +388 350 -920421.994641 +389 350 -1484551.60426 +390 350 -1196000.48814 +391 350 -298827.840692 +392 350 -499398.232928 +393 350 55832.4159336 +394 350 -1086154.51618 +395 350 -1234266.49566 +396 350 1056419.44831 +351 351 30008520.3637 +352 351 1.30385160446e-8 +353 351 1.49011611938e-8 +354 351 4063389.39899 +388 351 -741520.302649 +389 351 -1196000.48814 +390 351 -2731360.75727 +391 351 75251.5247453 +392 351 -55832.4159336 +393 351 -6893760.43768 +394 351 929649.114512 +395 351 1056419.44831 +396 351 -2324577.161 +352 352 13789595.5493 +353 352 8526963.46189 +354 352 -3.72529029846e-8 +355 352 2189391.67462 +356 352 902841.927677 +357 352 9.31322574615e-9 +391 352 -955815.974241 +392 352 -1086154.51618 +393 352 -929649.114512 +394 352 -1698063.33368 +395 352 -676945.37624 +396 352 -148519.569575 +397 352 -1831641.90145 +398 352 -755316.248019 +399 352 1300948.03845 +353 353 6744151.21474 +354 353 -3.72529029846e-9 +355 353 902841.927677 +356 353 372305.949557 +357 353 2.79396772385e-9 +391 353 -1086154.51618 +392 353 -1234266.49566 +393 353 -1056419.44831 +394 353 -676945.37624 +395 353 -252690.316209 +396 353 207978.412157 +397 353 -755316.248019 +398 353 -311470.61774 +399 353 536473.417918 +354 354 37051709.8254 +355 354 -3.72529029846e-9 +356 354 -9.31322574615e-10 +357 354 10537577.5004 +391 354 -929649.114512 +392 354 -1056419.44831 +393 354 -2324577.161 +394 354 148519.569575 +395 354 -207978.412157 +396 354 -9252258.07493 +397 354 1300948.03845 +398 354 536473.417918 +399 354 -3684182.88731 +355 355 19732335.5073 +357 355 -7.45058059692e-9 +358 355 2189391.67462 +359 355 -902841.927677 +360 355 9.31322574615e-9 +394 355 -1831641.90145 +395 355 -755316.248019 +396 355 -1300948.03845 +397 355 -3460455.85927 +398 355 -7.45058059692e-9 +399 355 -5.58793544769e-9 +400 355 -1831641.90145 +401 355 755316.248019 +402 355 1300948.03845 +356 356 3355482.70929 +357 356 3.72529029846e-9 +358 356 -902841.927677 +359 356 372305.949557 +360 356 -2.79396772385e-9 +394 356 -755316.248019 +395 356 -311470.61774 +396 356 -536473.417918 +397 356 -7.45058059692e-9 +398 356 -588450.353367 +399 356 429178.734334 +400 356 755316.248019 +401 356 -311470.61774 +402 356 -536473.417918 +357 357 47437791.9854 +358 357 -3.72529029846e-9 +359 357 9.31322574615e-10 +360 357 10537577.5004 +394 357 -1300948.03845 +395 357 -536473.417918 +396 357 -3684182.88731 +398 357 -429178.734334 +399 357 -12874095.835 +400 357 1300948.03845 +401 357 -536473.417918 +402 357 -3684182.88731 +358 358 13789595.5493 +359 358 -8526963.46189 +360 358 1.49011611938e-8 +361 358 -797223.130676 +362 358 905935.375768 +363 358 -7.45058059692e-9 +397 358 -1831641.90145 +398 358 755316.248019 +399 358 -1300948.03845 +400 358 -1698063.33368 +401 358 676945.37624 +402 358 148519.569575 +403 358 -955815.974241 +404 358 1086154.51618 +405 358 929649.114512 +359 359 6744151.21474 +360 359 7.45058059692e-9 +361 359 905935.375768 +362 359 -1029472.01792 +363 359 5.58793544769e-9 +397 359 755316.248019 +398 359 -311470.61774 +399 359 536473.417918 +400 359 676945.37624 +401 359 -252690.316209 +402 359 207978.412157 +403 359 1086154.51618 +404 359 -1234266.49566 +405 359 -1056419.44831 +360 360 37051709.8254 +361 360 -9.31322574615e-9 +362 360 9.31322574615e-9 +363 360 4063389.39899 +397 360 -1300948.03845 +398 360 536473.417918 +399 360 -3684182.88731 +400 360 -148519.569575 +401 360 -207978.412157 +402 360 -9252258.07493 +403 360 929649.114512 +404 360 -1056419.44831 +405 360 -2324577.161 +361 361 6563466.92161 +362 361 -8716568.29938 +363 361 2.23517417908e-8 +364 361 -4790.05038348 +365 361 7725.8877153 +366 361 -1.30385160446e-8 +400 361 -955815.974241 +401 361 1086154.51618 +402 361 -929649.114512 +403 361 -175770.085153 +404 361 298827.840692 +405 361 75251.5247453 +406 361 -570661.636677 +407 361 920421.994641 +408 361 741520.302649 +362 362 11934357.6383 +363 362 -2.98023223877e-8 +364 362 7725.88771529 +365 362 -12461.1092182 +366 362 1.86264514923e-8 +400 362 1086154.51618 +401 362 -1234266.49566 +402 362 1056419.44831 +403 362 298827.840692 +404 362 -499398.232928 +405 362 55832.4159336 +406 362 920421.994641 +407 362 -1484551.60426 +408 362 -1196000.48814 +363 363 30008520.3637 +364 363 -1.30385160446e-8 +365 363 2.60770320892e-8 +366 363 6373223.13441 +400 363 -929649.114512 +401 363 1056419.44831 +402 363 -2324577.161 +403 363 -75251.5247453 +404 363 -55832.4159336 +405 363 -6893760.43768 +406 363 741520.302649 +407 363 -1196000.48814 +408 363 -2731360.75727 +364 364 4267595.81742 +365 364 -7796294.34309 +366 364 1.86264514923e-8 +367 364 -76937.6348729 +368 364 167255.727985 +369 364 -1.11758708954e-8 +403 364 -570661.636677 +404 364 920421.994641 +405 364 -741520.302649 +406 364 -304176.348108 +407 364 544599.533136 +408 364 61373.985991 +409 364 -361895.781536 +410 364 786729.959862 +411 364 588085.337671 +365 365 14559614.5621 +366 365 -3.72529029846e-8 +367 365 167255.727985 +368 365 -363599.408662 +369 365 2.421438694e-8 +403 365 920421.994641 +404 365 -1484551.60426 +405 365 1196000.48814 +406 365 544599.533136 +407 365 -995761.387374 +408 365 32978.3592395 +409 365 786729.959862 +410 365 -1710282.52144 +411 365 -1278446.38624 +366 366 32450763.4987 +367 366 -2.23517417908e-8 +368 366 5.02914190292e-8 +369 366 5787467.22392 +403 366 -741520.302649 +404 366 1196000.48814 +405 366 -2731360.75727 +406 366 -61373.985991 +407 366 -32978.3592395 +408 366 -7832517.9294 +409 366 588085.337671 +410 366 -1278446.38624 +411 366 -2616778.13471 +367 367 2722764.08058 +368 367 -6525095.96362 +369 367 4.28408384323e-8 +370 367 -77037.3094958 +371 367 210101.75317 +372 367 -1.86264514923e-8 +406 367 -361895.781536 +407 367 786729.959862 +408 367 -588085.337671 +409 367 -150094.325529 +410 367 356091.902114 +411 367 41457.2556898 +412 367 -246970.319543 +413 367 673555.416935 +414 367 484442.198447 +368 368 15837837.4467 +369 368 -9.685754776e-8 +370 368 210101.75317 +371 368 -573004.781374 +372 368 4.842877388e-8 +406 368 786729.959862 +407 368 -1710282.52144 +408 368 1278446.38624 +409 368 356091.902114 +410 368 -855385.258208 +411 368 17103.8438089 +412 368 673555.416935 +413 368 -1836969.31891 +414 368 -1321205.99576 +369 369 31131706.3633 +370 369 -2.98023223877e-8 +371 369 8.56816768646e-8 +372 369 5508023.65258 +406 369 -588085.337671 +407 369 1278446.38624 +408 369 -2616778.13471 +409 369 -41457.2556898 +410 369 -17103.8438089 +411 369 -7353984.06298 +412 369 484442.198447 +413 369 -1321205.99576 +414 369 -2564546.07772 +370 370 1902223.18075 +371 370 -5590205.99024 +372 370 4.09781932831e-8 +409 370 -246970.319543 +410 370 673555.416935 +411 370 -484442.198447 +412 370 -89281.9039061 +413 370 261158.775213 +414 370 27106.375452 +415 370 -183325.791023 +416 370 591373.519429 +417 370 416676.259817 +371 371 16543837.5893 +372 371 -1.11758708954e-7 +409 371 673555.416935 +410 371 -1836969.31891 +411 371 1321205.99576 +412 371 261158.775213 +413 371 -769227.571739 +414 371 9164.38855485 +415 371 591373.519429 +416 371 -1907656.51429 +417 371 -1344116.96715 +372 372 30501291.4656 +409 372 -484442.198447 +410 372 1321205.99576 +411 372 -2564546.07772 +412 372 -27106.375452 +413 372 -9164.38855489 +414 372 -7121142.78386 +415 372 416676.259817 +416 372 -1344116.96715 +417 372 -2538358.87127 +373 373 983097.51761 +374 373 3993191.05725 +375 373 -4.09781932831e-8 +376 373 -268071.177508 +377 373 -1017991.81332 +378 373 6.51925802231e-9 +424 373 22134.9913963 +425 373 90320.994053 +426 373 -18909.0650259 +427 373 -144050.092155 +428 373 -547025.666412 +429 373 358351.461509 +374 374 16310675.5472 +375 374 -1.71363353729e-7 +376 374 -1017991.81332 +377 374 -3865791.69616 +378 374 2.60770320892e-8 +424 374 90320.994053 +425 374 370626.696215 +426 374 4631.25243447 +427 374 -547025.666412 +428 374 -2077312.65726 +429 374 1360828.33484 +375 375 19175351.0344 +376 375 1.210719347e-8 +377 375 4.09781932831e-8 +378 375 1324183.34296 +424 375 18909.0650259 +425 375 -4631.25243456 +426 375 -5293544.01962 +427 375 358351.461509 +428 375 1360828.33484 +429 375 -2284273.33935 +376 376 2528206.57902 +377 376 4472508.77878 +378 376 -2.60770320892e-8 +379 376 161794.959894 +380 376 -1643725.47145 +382 376 -354732.167298 +383 376 -1144297.31386 +384 376 1.30385160446e-8 +424 376 -144050.092155 +425 376 -547025.666412 +426 376 -358351.461509 +427 376 -474420.971735 +428 376 131724.797328 +429 376 -23329.9193232 +430 376 800256.904552 +431 376 8988441.97885 +433 376 -193836.559394 +434 376 -625279.223852 +435 376 416676.259817 +377 377 16817023.5501 +378 377 -8.94069671631e-8 +379 377 2239699.42799 +380 377 -161794.959894 +382 377 -1144297.31386 +383 377 -3691281.65763 +384 377 4.09781932831e-8 +424 377 -547025.666412 +425 377 -2077312.65726 +426 377 -1360828.33484 +427 377 131724.797328 +428 377 -28174.604603 +429 377 6684.54707693 +430 377 -6040684.68178 +431 377 -800256.904552 +433 377 -625279.223852 +434 377 -2017029.75436 +435 377 1344116.96715 +378 378 39001825.1235 +382 378 1.210719347e-8 +383 378 3.35276126862e-8 +384 378 1397983.15475 +424 378 -358351.461509 +425 378 -1360828.33484 +426 378 -2284273.33935 +427 378 23329.9193232 +428 378 -6684.54707697 +429 378 -14301137.6219 +433 378 416676.259817 +434 378 1344116.96715 +435 378 -2296979.40748 +379 379 329627273.449 +380 379 65098710.708 +427 379 -800256.904552 +428 379 6040684.68178 +430 379 64525494.457 +431 379 -4237812.99363 +380 380 569419268.28 +427 380 -8988441.97885 +428 380 800256.904552 +430 380 -4237812.99363 +431 380 48915452.1011 +381 381 790573.476703 +432 381 -357777.777778 +382 382 1768961.88645 +383 382 5200497.8108 +384 382 -2.98023223877e-8 +385 382 -465133.879389 +386 382 -1268546.94379 +387 382 1.86264514923e-8 +427 382 -193836.559394 +428 382 -625279.223852 +429 382 -416676.259817 +433 382 24667.0078013 +434 382 73754.1287476 +435 382 -27106.375452 +436 382 -260348.273704 +437 382 -710040.746465 +438 382 484442.198447 +383 383 15396260.6937 +384 383 -8.94069671631e-8 +385 383 -1268546.94379 +386 383 -3459673.48306 +387 383 4.47034835815e-8 +427 383 -625279.223852 +428 383 -2017029.75436 +429 383 -1344116.96715 +433 383 73754.1287476 +434 383 222052.369366 +435 383 9164.38855485 +436 383 -710040.746465 +437 383 -1936474.76309 +438 383 1321205.99576 +384 384 19430253.1852 +385 384 2.04890966415e-8 +386 384 5.96046447754e-8 +387 384 1501024.18544 +427 384 -416676.259817 +428 384 -1344116.96715 +429 384 -2296979.40748 +433 384 27106.375452 +434 384 -9164.3885549 +435 384 -5492991.14412 +436 384 484442.198447 +437 384 1321205.99576 +438 384 -2315242.24089 +385 385 2497002.01247 +386 385 5989724.5569 +387 385 -5.21540641785e-8 +388 385 -645500.420471 +389 385 -1403261.78363 +390 385 2.60770320892e-8 +433 385 -260348.273704 +434 385 -710040.746465 +435 385 -484442.198447 +436 385 12227.6197724 +437 385 33038.3525624 +438 385 -41457.2556898 +439 385 -379286.632065 +440 385 -824536.156663 +441 385 588085.337671 +386 386 14552393.0113 +387 386 -1.26659870148e-7 +388 386 -1403261.78363 +389 386 -3050569.09485 +390 386 5.77419996262e-8 +433 386 -710040.746465 +434 386 -1936474.76309 +435 386 -1321205.99576 +436 386 33038.3525624 +437 386 89431.3340665 +438 386 17103.8438088 +439 386 -824536.156663 +440 386 -1792469.90579 +441 386 1278446.38624 +387 387 19697338.3132 +388 387 2.32830643654e-8 +389 387 5.21540641785e-8 +390 387 1699467.28443 +433 387 -484442.198447 +434 387 -1321205.99576 +435 387 -2315242.24089 +436 387 41457.2556898 +437 387 -17103.8438089 +438 387 -5695176.55864 +439 387 588085.337671 +440 387 1278446.38624 +441 387 -2352102.70813 +388 388 3805513.9161 +389 388 6969546.89216 +390 388 -4.47034835815e-8 +391 388 -900913.14915 +392 388 -1453085.72444 +393 388 2.23517417908e-8 +436 388 -379286.632065 +437 388 -824536.156663 +438 388 -588085.337671 +439 388 -52167.0393845 +440 388 -83826.4031561 +441 388 -61373.985991 +442 388 -590912.90871 +443 388 -953085.336629 +444 388 741520.302649 +389 389 13049076.8844 +390 389 -8.94069671631e-8 +391 389 -1453085.72444 +392 389 -2343686.65232 +393 389 3.53902578354e-8 +436 389 -824536.156663 +437 389 -1792469.90579 +438 389 -1278446.38624 +439 389 -83826.4031561 +440 389 -134521.304432 +441 389 32978.3592395 +442 389 -953085.336629 +443 389 -1537234.41392 +444 389 1196000.48814 +390 390 20269767.9526 +391 390 1.67638063431e-8 +392 390 2.98023223877e-8 +393 390 2107431.22341 +436 390 -588085.337671 +437 390 -1278446.38624 +438 390 -2352102.70813 +439 390 61373.985991 +440 390 -32978.3592395 +441 390 -6109264.54972 +442 390 741520.302649 +443 390 1196000.48814 +444 390 -2434642.38116 +391 391 6311393.43185 +392 391 8279110.50413 +393 391 -3.35276126862e-8 +394 391 -2301041.15788 +395 391 -2614819.49759 +396 391 1.67638063431e-8 +439 391 -590912.90871 +440 391 -953085.336629 +441 391 -741520.302649 +442 391 222027.67781 +443 391 227177.812693 +444 391 -75251.5247453 +445 391 -1038306.19785 +446 391 -1179893.40665 +447 391 929649.114512 +392 392 11193680.9464 +393 392 -4.47034835815e-8 +394 392 -2614819.49759 +395 392 -2971385.79271 +396 392 1.67638063431e-8 +439 392 -953085.336629 +440 392 -1537234.41392 +441 392 -1196000.48814 +442 392 227177.812693 +443 392 217630.207531 +444 392 55832.4159336 +445 392 -1179893.40665 +446 392 -1340787.9621 +447 392 1056419.44831 +393 393 19292741.7183 +394 393 1.11758708954e-8 +395 393 1.49011611938e-8 +396 393 427942.721781 +439 393 -741520.302649 +440 393 -1196000.48814 +441 393 -2434642.38116 +442 393 75251.5247453 +443 393 -55832.4159336 +444 393 -5287984.36193 +445 393 929649.114512 +446 393 1056419.44831 +447 393 -2155790.5645 +394 394 11173430.2083 +395 394 7495059.08719 +396 394 -2.23517417908e-8 +397 394 -678836.974673 +398 394 -279932.773061 +399 394 7.45058059692e-9 +442 394 -1038306.19785 +443 394 -1179893.40665 +444 394 -929649.114512 +445 394 -908773.635179 +446 394 -175831.894858 +447 394 -148519.569575 +448 394 -1760951.03338 +449 394 -726165.37459 +450 394 1300948.03845 +395 395 6371949.42588 +396 395 -2.23517417908e-8 +397 395 -279932.773061 +398 395 -115436.195077 +399 395 3.72529029846e-9 +442 395 -1179893.40665 +443 395 -1340787.9621 +444 395 -1056419.44831 +445 395 -175831.894858 +446 395 153537.59263 +447 395 207978.412157 +448 395 -726165.37459 +449 395 -299449.639006 +450 395 536473.417918 +396 396 22658086.7563 +398 396 -9.31322574615e-10 +399 396 4790763.62708 +442 396 -929649.114512 +443 396 -1056419.44831 +444 396 -2155790.5645 +445 396 148519.569575 +446 396 -207978.412157 +447 396 -7295491.64648 +448 396 1300948.03845 +449 396 536473.417918 +450 396 -3164492.7709 +397 397 14370371.7322 +398 397 1.49011611938e-8 +399 397 -7.45058059692e-9 +400 397 -678836.974673 +401 397 279932.773061 +402 397 7.45058059692e-9 +445 397 -1760951.03338 +446 397 -726165.37459 +447 397 -1300948.03845 +448 397 -2367056.05396 +450 397 -5.58793544769e-9 +451 397 -1760951.03338 +452 397 726165.37459 +453 397 1300948.03845 +398 398 2443681.02578 +399 398 1.49011611938e-8 +400 398 279932.773061 +401 398 -115436.195077 +402 398 -3.72529029846e-9 +445 398 -726165.37459 +446 398 -299449.639006 +447 398 -536473.417918 +449 398 -402517.768767 +450 398 429178.734334 +451 398 726165.37459 +452 398 -299449.639006 +453 398 -536473.417918 +399 399 27394702.6193 +401 399 9.31322574615e-10 +402 399 4790763.62708 +445 399 -1300948.03845 +446 399 -536473.417918 +447 399 -3164492.7709 +448 399 3.72529029846e-9 +449 399 -429178.734334 +450 399 -10404782.7221 +451 399 1300948.03845 +452 399 -536473.417918 +453 399 -3164492.7709 +400 400 11173430.2083 +401 400 -7495059.08719 +402 400 1.86264514923e-8 +403 400 -2301041.15788 +404 400 2614819.49759 +405 400 -1.30385160446e-8 +448 400 -1760951.03338 +449 400 726165.37459 +450 400 -1300948.03845 +451 400 -908773.635179 +452 400 175831.894858 +453 400 148519.569575 +454 400 -1038306.19785 +455 400 1179893.40665 +456 400 929649.114512 +401 401 6371949.42588 +402 401 -2.98023223877e-8 +403 401 2614819.49759 +404 401 -2971385.79271 +405 401 1.49011611938e-8 +448 401 726165.37459 +449 401 -299449.639006 +450 401 536473.417918 +451 401 175831.894858 +452 401 153537.59263 +453 401 207978.412157 +454 401 1179893.40665 +455 401 -1340787.9621 +456 401 -1056419.44831 +402 402 22658086.7563 +403 402 -2.04890966415e-8 +404 402 2.04890966415e-8 +405 402 427942.721781 +448 402 -1300948.03845 +449 402 536473.417918 +450 402 -3164492.7709 +451 402 -148519.569575 +452 402 -207978.412157 +453 402 -7295491.64648 +454 402 929649.114512 +455 402 -1056419.44831 +456 402 -2155790.5645 +403 403 6311393.43185 +404 403 -8279110.50413 +405 403 2.98023223877e-8 +406 403 -900913.14915 +407 403 1453085.72444 +408 403 -1.86264514923e-8 +451 403 -1038306.19785 +452 403 1179893.40665 +453 403 -929649.114512 +454 403 222027.67781 +455 403 -227177.812693 +456 403 75251.5247453 +457 403 -590912.90871 +458 403 953085.336629 +459 403 741520.302649 +404 404 11193680.9464 +405 404 -3.72529029846e-8 +406 404 1453085.72444 +407 404 -2343686.65232 +408 404 2.79396772385e-8 +451 404 1179893.40665 +452 404 -1340787.9621 +453 404 1056419.44831 +454 404 -227177.812693 +455 404 217630.207531 +456 404 55832.4159336 +457 404 953085.336629 +458 404 -1537234.41392 +459 404 -1196000.48814 +405 405 19292741.7183 +406 405 -2.04890966415e-8 +407 405 3.35276126862e-8 +408 405 2107431.22341 +451 405 -929649.114512 +452 405 1056419.44831 +453 405 -2155790.5645 +454 405 -75251.5247453 +455 405 -55832.4159336 +456 405 -5287984.36193 +457 405 741520.302649 +458 405 -1196000.48814 +459 405 -2434642.38116 +406 406 3805513.9161 +407 406 -6969546.89216 +408 406 3.72529029846e-8 +409 406 -645500.420471 +410 406 1403261.78363 +411 406 -2.04890966415e-8 +454 406 -590912.90871 +455 406 953085.336629 +456 406 -741520.302649 +457 406 -52167.0393845 +458 406 83826.4031561 +459 406 61373.985991 +460 406 -379286.632065 +461 406 824536.156663 +462 406 588085.337671 +407 407 13049076.8844 +408 407 -6.70552253723e-8 +409 407 1403261.78363 +410 407 -3050569.09485 +411 407 4.47034835815e-8 +454 407 953085.336629 +455 407 -1537234.41392 +456 407 1196000.48814 +457 407 83826.4031561 +458 407 -134521.304432 +459 407 32978.3592395 +460 407 824536.156663 +461 407 -1792469.90579 +462 407 -1278446.38624 +408 408 20269767.9526 +409 408 -2.51457095146e-8 +410 408 5.40167093277e-8 +411 408 1699467.28443 +454 408 -741520.302649 +455 408 1196000.48814 +456 408 -2434642.38116 +457 408 -61373.985991 +458 408 -32978.3592395 +459 408 -6109264.54972 +460 408 588085.337671 +461 408 -1278446.38624 +462 408 -2352102.70813 +409 409 2497002.01247 +410 409 -5989724.5569 +411 409 4.842877388e-8 +412 409 -465133.879389 +413 409 1268546.94379 +414 409 -2.51457095146e-8 +457 409 -379286.632065 +458 409 824536.156663 +459 409 -588085.337671 +460 409 12227.6197724 +461 409 -33038.3525624 +462 409 41457.2556898 +463 409 -260348.273704 +464 409 710040.746465 +465 409 484442.198447 +410 410 14552393.0113 +411 410 -1.19209289551e-7 +412 410 1268546.94379 +413 410 -3459673.48306 +414 410 7.45058059692e-8 +457 410 824536.156663 +458 410 -1792469.90579 +459 410 1278446.38624 +460 410 -33038.3525624 +461 410 89431.3340665 +462 410 17103.8438088 +463 410 710040.746465 +464 410 -1936474.76309 +465 410 -1321205.99576 +411 411 19697338.3132 +412 411 -2.421438694e-8 +413 411 6.33299350738e-8 +414 411 1501024.18544 +457 411 -588085.337671 +458 411 1278446.38624 +459 411 -2352102.70813 +460 411 -41457.2556898 +461 411 -17103.8438089 +462 411 -5695176.55864 +463 411 484442.198447 +464 411 -1321205.99576 +465 411 -2315242.24089 +412 412 1768961.88645 +413 412 -5200497.8108 +414 412 2.98023223877e-8 +415 412 -354732.167298 +416 412 1144297.31386 +417 412 -7.45058059692e-9 +460 412 -260348.273704 +461 412 710040.746465 +462 412 -484442.198447 +463 412 24667.0078013 +464 412 -73754.1287476 +465 412 27106.375452 +466 412 -193836.559394 +467 412 625279.223852 +468 412 416676.259817 +413 413 15396260.6937 +414 413 -8.19563865662e-8 +415 413 1144297.31386 +416 413 -3691281.65763 +417 413 2.60770320892e-8 +460 413 710040.746465 +461 413 -1936474.76309 +462 413 1321205.99576 +463 413 -73754.1287476 +464 413 222052.369366 +465 413 9164.38855486 +466 413 625279.223852 +467 413 -2017029.75436 +468 413 -1344116.96715 +414 414 19430253.1852 +415 414 -9.31322574615e-9 +416 414 2.98023223877e-8 +417 414 1397983.15475 +460 414 -484442.198447 +461 414 1321205.99576 +462 414 -2315242.24089 +463 414 -27106.375452 +464 414 -9164.38855491 +465 414 -5492991.14412 +466 414 416676.259817 +467 414 -1344116.96715 +468 414 -2296979.40748 +415 415 2528206.97157 +416 415 -4472509.50178 +417 415 2.23517417908e-8 +418 415 -161793.620953 +419 415 -1643724.74446 +421 415 -268071.177508 +422 415 1017991.81332 +423 415 -1.210719347e-8 +463 415 -193836.559394 +464 415 625279.223852 +465 415 -416676.259817 +466 415 -474421.1715 +467 415 -131724.429407 +468 415 23329.9193232 +469 415 -800250.281991 +470 415 8988445.57462 +472 415 -144050.092155 +473 415 547025.666412 +474 415 358351.461509 +416 416 16817023.1576 +417 416 -8.19563865662e-8 +418 416 2239700.15498 +419 416 161793.620953 +421 416 1017991.81332 +422 416 -3865791.69616 +423 416 4.09781932831e-8 +463 416 625279.223852 +464 416 -2017029.75436 +465 416 1344116.96715 +466 416 -131724.429407 +467 416 -28174.4048382 +468 416 6684.54707694 +469 416 -6040681.08601 +470 416 800250.281991 +472 416 547025.666412 +473 416 -2077312.65726 +474 416 -1360828.33484 +417 417 39001825.1235 +421 417 -1.11758708954e-8 +422 417 4.09781932831e-8 +423 417 1324183.34296 +463 417 -416676.259817 +464 417 1344116.96715 +465 417 -2296979.40748 +466 417 -23329.9193232 +467 417 -6684.54707698 +468 417 -14301137.6219 +472 417 358351.461509 +473 417 -1360828.33484 +474 417 -2284273.33935 +418 418 329626980.943 +419 418 -65098171.9808 +466 418 800250.281991 +467 418 6040681.08601 +469 418 64525513.4986 +470 418 4237777.92342 +419 419 569419560.785 +466 419 -8988445.57462 +467 419 -800250.281991 +469 419 4237777.92342 +470 419 48915433.0595 +420 420 790573.476703 +471 420 -357777.777778 +421 421 983097.51761 +422 421 -3993191.05725 +423 421 5.40167093277e-8 +466 421 -144050.092155 +467 421 547025.666412 +468 421 -358351.461509 +472 421 22134.9913963 +473 421 -90320.994053 +474 421 18909.0650259 +422 422 16310675.5472 +423 422 -2.30967998505e-7 +466 422 547025.666412 +467 422 -2077312.65726 +468 422 1360828.33484 +472 422 -90320.994053 +473 422 370626.696215 +474 422 4631.25243447 +423 423 19175351.0344 +466 423 -358351.461509 +467 423 1360828.33484 +468 423 -2284273.33935 +472 423 -18909.0650259 +473 423 -4631.25243458 +474 423 -5293544.01962 +424 424 1011851.60912 +425 424 4110093.43272 +426 424 -4.65661287308e-8 +427 424 -311975.890718 +428 424 -1184718.57235 +429 424 9.31322574615e-9 +475 424 22134.9913963 +476 424 90320.994053 +477 424 -18909.0650259 +478 424 -144050.092155 +479 424 -547025.666412 +480 424 358351.461509 +425 425 16788621.6609 +426 425 -2.01165676117e-7 +427 425 -1184718.57235 +428 425 -4498931.28739 +429 425 2.98023223877e-8 +475 425 90320.994053 +476 425 370626.696215 +477 425 4631.25243447 +478 425 -547025.666412 +479 425 -2077312.65726 +480 425 1360828.33484 +426 426 18240145.4814 +427 426 9.31322574615e-9 +428 426 2.98023223877e-8 +429 426 758227.34113 +475 426 18909.0650259 +476 426 -4631.25243456 +477 426 -5293544.01962 +478 426 358351.461509 +479 426 1360828.33484 +480 426 -2284273.33935 +427 427 2350262.38096 +428 427 4600302.12491 +429 427 -2.421438694e-8 +430 427 2.98023223877e-8 +431 427 -1.19209289551e-7 +433 427 -413671.243673 +434 427 -1334423.36669 +435 427 1.58324837685e-8 +475 427 -144050.092155 +476 427 -547025.666412 +477 427 -358351.461509 +478 427 -474420.971735 +479 427 131724.797327 +480 427 -23329.9193232 +481 427 800256.904552 +482 427 8988441.97885 +484 427 -193836.559394 +485 427 -625279.223852 +486 427 416676.259817 +428 428 17048556.8253 +429 428 -8.19563865662e-8 +430 428 5.96046447754e-8 +433 428 -1334423.36669 +434 428 -4304591.50545 +435 428 4.842877388e-8 +475 428 -547025.666412 +476 428 -2077312.65726 +477 428 -1360828.33484 +478 428 131724.797327 +479 428 -28174.6046032 +480 428 6684.54707693 +481 428 -6040684.68178 +482 428 -800256.904552 +484 428 -625279.223852 +485 428 -2017029.75436 +486 428 1344116.96715 +429 429 36176122.0894 +433 429 1.39698386192e-8 +434 429 4.09781932831e-8 +435 429 830431.306934 +475 429 -358351.461509 +476 429 -1360828.33484 +477 429 -2284273.33935 +478 429 23329.9193232 +479 429 -6684.54707697 +480 429 -14301137.6219 +484 429 416676.259817 +485 429 1344116.96715 +486 429 -2296979.40748 +430 430 305878308.174 +431 430 66094123.115 +478 430 -800256.904552 +479 430 6040684.68178 +481 430 64525494.457 +482 430 -4237812.99363 +431 431 549336918.275 +478 431 -8988441.97885 +479 431 800256.904552 +481 431 -4237812.99363 +482 431 48915452.1011 +432 432 715555.555556 +483 432 -357777.777778 +433 433 1816739.3315 +434 433 5341279.87864 +435 433 -3.35276126862e-8 +436 433 -544032.43723 +437 433 -1483724.82881 +438 433 2.23517417908e-8 +478 433 -193836.559394 +479 433 -625279.223852 +480 433 -416676.259817 +484 433 24667.0078013 +485 433 73754.1287475 +486 433 -27106.375452 +487 433 -260348.273704 +488 433 -710040.746465 +489 433 484442.198447 +434 434 15814018.062 +435 434 -1.04308128357e-7 +436 434 -1483724.82881 +437 434 -4046522.26039 +438 434 5.96046447754e-8 +478 434 -625279.223852 +479 434 -2017029.75436 +480 434 -1344116.96715 +484 434 73754.1287475 +485 434 222052.369365 +486 434 9164.38855485 +487 434 -710040.746465 +488 434 -1936474.76309 +489 434 1321205.99576 +435 435 18448886.5844 +436 435 2.421438694e-8 +437 435 6.33299350738e-8 +438 435 931107.693647 +478 435 -416676.259817 +479 435 -1344116.96715 +480 435 -2296979.40748 +484 435 27106.375452 +485 435 -9164.38855491 +486 435 -5492991.14412 +487 435 484442.198447 +488 435 1321205.99576 +489 435 -2315242.24089 +436 436 2558539.62181 +437 436 6138307.60949 +438 436 -5.21540641785e-8 +439 436 -759692.612592 +440 436 -1651505.67955 +441 436 2.60770320892e-8 +484 436 -260348.273704 +485 436 -710040.746465 +486 436 -484442.198447 +487 436 12227.6197724 +488 436 33038.3525624 +489 436 -41457.2556898 +490 436 -379286.632065 +491 436 -824536.156663 +492 436 588085.337671 +437 437 14915778.6682 +438 437 -1.34110450745e-7 +439 437 -1651505.67955 +440 437 -3590229.73815 +441 437 5.77419996262e-8 +484 437 -710040.746465 +485 437 -1936474.76309 +486 437 -1321205.99576 +487 437 33038.3525624 +488 437 89431.3340664 +489 437 17103.8438088 +490 437 -824536.156663 +491 437 -1792469.90579 +492 437 1278446.38624 +438 438 18669379.7869 +439 438 2.32830643654e-8 +440 438 5.21540641785e-8 +441 438 1124555.53479 +484 438 -484442.198447 +485 438 -1321205.99576 +486 438 -2315242.24089 +487 438 41457.2556898 +488 438 -17103.8438089 +489 438 -5695176.55864 +490 438 588085.337671 +491 438 1278446.38624 +492 438 -2352102.70813 +439 439 3880798.16119 +440 439 7110485.96967 +441 439 -4.842877388e-8 +442 439 -1076372.38828 +443 439 -1736084.49722 +444 439 2.421438694e-8 +487 439 -379286.632065 +488 439 -824536.156663 +489 439 -588085.337671 +490 439 -52167.0393845 +491 439 -83826.4031561 +492 439 -61373.985991 +493 439 -590912.90871 +494 439 -953085.336629 +495 439 741520.302649 +440 440 13318817.2723 +441 440 -9.685754776e-8 +442 440 -1736084.49722 +443 440 -2800136.28584 +444 440 3.91155481339e-8 +487 440 -824536.156663 +488 440 -1792469.90579 +489 440 -1278446.38624 +490 440 -83826.4031561 +491 440 -134521.304432 +492 440 32978.3592395 +493 440 -953085.336629 +494 440 -1537234.41392 +495 440 1196000.48814 +441 441 19146980.3477 +442 441 1.67638063431e-8 +443 441 2.98023223877e-8 +444 441 1520483.39551 +487 441 -588085.337671 +488 441 -1278446.38624 +489 441 -2352102.70813 +490 441 61373.985991 +491 441 -32978.3592395 +492 441 -6109264.54972 +493 441 741520.302649 +494 441 1196000.48814 +495 441 -2434642.38116 +442 442 6516876.42304 +443 442 8531914.96891 +444 442 -1.86264514923e-8 +445 442 -2626121.17726 +446 442 -2984228.61052 +447 442 1.30385160446e-8 +490 442 -590912.90871 +491 442 -953085.336629 +492 442 -741520.302649 +493 442 222027.67781 +494 442 227177.812693 +495 442 -75251.5247453 +496 442 -1038306.19785 +497 442 -1179893.40665 +498 442 929649.114512 +443 443 11512089.4984 +444 443 -2.23517417908e-8 +445 443 -2984228.61052 +446 443 -3391168.87559 +447 443 1.30385160446e-8 +490 443 -953085.336629 +491 443 -1537234.41392 +492 443 -1196000.48814 +493 443 227177.812693 +494 443 217630.207531 +495 443 55832.4159336 +496 443 -1179893.40665 +497 443 -1340787.9621 +498 443 1056419.44831 +444 444 18361731.7736 +445 444 7.45058059692e-9 +446 444 9.31322574615e-9 +447 444 -125380.553907 +490 444 -741520.302649 +491 444 -1196000.48814 +492 444 -2434642.38116 +493 444 75251.5247453 +494 444 -55832.4159336 +495 444 -5287984.36193 +496 444 929649.114512 +497 444 1056419.44831 +498 444 -2155790.5645 +445 445 11197028.9194 +446 445 7624235.1212 +447 445 -1.49011611938e-8 +448 445 -1154846.00933 +449 445 -476225.158488 +450 445 7.45058059692e-9 +493 445 -1038306.19785 +494 445 -1179893.40665 +495 445 -929649.114512 +496 445 -908773.635179 +497 445 -175831.894858 +498 445 -148519.569575 +499 445 -1760951.03338 +500 445 -726165.37459 +501 445 1300948.03845 +446 446 6560950.4012 +447 446 -7.45058059692e-9 +448 446 -476225.158488 +449 446 -196381.508655 +450 446 2.79396772385e-9 +493 446 -1179893.40665 +494 446 -1340787.9621 +495 446 -1056419.44831 +496 446 -175831.894858 +497 446 153537.59263 +498 446 207978.412157 +499 446 -726165.37459 +500 446 -299449.639006 +501 446 536473.417918 +447 447 21281133.3311 +450 447 4075797.18655 +493 447 -929649.114512 +494 447 -1056419.44831 +495 447 -2155790.5645 +496 447 148519.569575 +497 447 -207978.412157 +498 447 -7295491.64648 +499 447 1300948.03845 +500 447 536473.417918 +501 447 -3164492.7709 +448 448 14087608.2601 +450 448 -7.45058059692e-9 +451 448 -1154846.00933 +452 448 476225.158488 +453 448 7.45058059692e-9 +496 448 -1760951.03338 +497 448 -726165.37459 +498 448 -1300948.03845 +499 448 -2367056.05396 +501 448 -5.58793544769e-9 +502 448 -1760951.03338 +503 448 726165.37459 +504 448 1300948.03845 +449 449 2395597.11087 +450 449 7.45058059692e-9 +451 449 476225.158488 +452 449 -196381.508655 +453 449 -2.79396772385e-9 +496 449 -726165.37459 +497 449 -299449.639006 +498 449 -536473.417918 +500 449 -402517.768767 +501 449 429178.734334 +502 449 726165.37459 +503 449 -299449.639006 +504 449 -536473.417918 +450 450 25315942.1547 +453 450 4075797.18655 +496 450 -1300948.03845 +497 450 -536473.417918 +498 450 -3164492.7709 +499 450 3.72529029846e-9 +500 450 -429178.734334 +501 450 -10404782.7221 +502 450 1300948.03845 +503 450 -536473.417918 +504 450 -3164492.7709 +451 451 11197028.9194 +452 451 -7624235.1212 +453 451 1.49011611938e-8 +454 451 -2626121.17726 +455 451 2984228.61052 +456 451 -9.31322574615e-9 +499 451 -1760951.03338 +500 451 726165.37459 +501 451 -1300948.03845 +502 451 -908773.635179 +503 451 175831.894858 +504 451 148519.569575 +505 451 -1038306.19785 +506 451 1179893.40665 +507 451 929649.114512 +452 452 6560950.4012 +453 452 -7.45058059692e-9 +454 452 2984228.61052 +455 452 -3391168.87559 +456 452 9.31322574615e-9 +499 452 726165.37459 +500 452 -299449.639006 +501 452 536473.417918 +502 452 175831.894858 +503 452 153537.59263 +504 452 207978.412157 +505 452 1179893.40665 +506 452 -1340787.9621 +507 452 -1056419.44831 +453 453 21281133.3311 +454 453 -1.49011611938e-8 +455 453 1.67638063431e-8 +456 453 -125380.553907 +499 453 -1300948.03845 +500 453 536473.417918 +501 453 -3164492.7709 +502 453 -148519.569575 +503 453 -207978.412157 +504 453 -7295491.64648 +505 453 929649.114512 +506 453 -1056419.44831 +507 453 -2155790.5645 +454 454 6516876.42304 +455 454 -8531914.96891 +456 454 2.23517417908e-8 +457 454 -1076372.38828 +458 454 1736084.49722 +459 454 -1.49011611938e-8 +502 454 -1038306.19785 +503 454 1179893.40665 +504 454 -929649.114512 +505 454 222027.67781 +506 454 -227177.812693 +507 454 75251.5247453 +508 454 -590912.90871 +509 454 953085.336629 +510 454 741520.302649 +455 455 11512089.4984 +456 455 -2.98023223877e-8 +457 455 1736084.49722 +458 455 -2800136.28584 +459 455 2.421438694e-8 +502 455 1179893.40665 +503 455 -1340787.9621 +504 455 1056419.44831 +505 455 -227177.812693 +506 455 217630.207531 +507 455 55832.4159336 +508 455 953085.336629 +509 455 -1537234.41392 +510 455 -1196000.48814 +456 456 18361731.7736 +457 456 -2.04890966415e-8 +458 456 3.35276126862e-8 +459 456 1520483.39551 +502 456 -929649.114512 +503 456 1056419.44831 +504 456 -2155790.5645 +505 456 -75251.5247453 +506 456 -55832.4159336 +507 456 -5287984.36193 +508 456 741520.302649 +509 456 -1196000.48814 +510 456 -2434642.38116 +457 457 3880798.16119 +458 457 -7110485.96967 +459 457 3.35276126862e-8 +460 457 -759692.612592 +461 457 1651505.67955 +462 457 -1.86264514923e-8 +505 457 -590912.90871 +506 457 953085.336629 +507 457 -741520.302649 +508 457 -52167.0393845 +509 457 83826.4031561 +510 457 61373.985991 +511 457 -379286.632065 +512 457 824536.156663 +513 457 588085.337671 +458 458 13318817.2723 +459 458 -5.96046447754e-8 +460 458 1651505.67955 +461 458 -3590229.73815 +462 458 4.09781932831e-8 +505 458 953085.336629 +506 458 -1537234.41392 +507 458 1196000.48814 +508 458 83826.4031561 +509 458 -134521.304432 +510 458 32978.3592395 +511 458 824536.156663 +512 458 -1792469.90579 +513 458 -1278446.38624 +459 459 19146980.3477 +460 459 -2.14204192162e-8 +461 459 4.842877388e-8 +462 459 1124555.53479 +505 459 -741520.302649 +506 459 1196000.48814 +507 459 -2434642.38116 +508 459 -61373.985991 +509 459 -32978.3592395 +510 459 -6109264.54972 +511 459 588085.337671 +512 459 -1278446.38624 +513 459 -2352102.70813 +460 460 2558539.62181 +461 460 -6138307.60949 +462 460 4.28408384323e-8 +463 460 -544032.43723 +464 460 1483724.82881 +465 460 -2.421438694e-8 +508 460 -379286.632065 +509 460 824536.156663 +510 460 -588085.337671 +511 460 12227.6197724 +512 460 -33038.3525624 +513 460 41457.2556898 +514 460 -260348.273704 +515 460 710040.746465 +516 460 484442.198447 +461 461 14915778.6682 +462 461 -1.11758708954e-7 +463 461 1483724.82881 +464 461 -4046522.26039 +465 461 7.07805156708e-8 +508 461 824536.156663 +509 461 -1792469.90579 +510 461 1278446.38624 +511 461 -33038.3525624 +512 461 89431.3340665 +513 461 17103.8438088 +514 461 710040.746465 +515 461 -1936474.76309 +516 461 -1321205.99576 +462 462 18669379.7869 +463 462 -2.70083546639e-8 +464 462 7.82310962677e-8 +465 462 931107.693647 +508 462 -588085.337671 +509 462 1278446.38624 +510 462 -2352102.70813 +511 462 -41457.2556898 +512 462 -17103.8438089 +513 462 -5695176.55864 +514 462 484442.198447 +515 462 -1321205.99576 +516 462 -2315242.24089 +463 463 1816739.3315 +464 463 -5341279.87864 +465 463 3.53902578354e-8 +466 463 -413671.243673 +467 463 1334423.36669 +468 463 -5.58793544769e-9 +511 463 -260348.273704 +512 463 710040.746465 +513 463 -484442.198447 +514 463 24667.0078013 +515 463 -73754.1287475 +516 463 27106.375452 +517 463 -193836.559394 +518 463 625279.223852 +519 463 416676.259817 +464 464 15814018.062 +465 464 -1.04308128357e-7 +466 464 1334423.36669 +467 464 -4304591.50545 +468 464 2.23517417908e-8 +511 464 710040.746465 +512 464 -1936474.76309 +513 464 1321205.99576 +514 464 -73754.1287475 +515 464 222052.369365 +516 464 9164.38855486 +517 464 625279.223852 +518 464 -2017029.75436 +519 464 -1344116.96715 +465 465 18448886.5844 +466 465 -1.02445483208e-8 +467 465 3.35276126862e-8 +468 465 830431.306934 +511 465 -484442.198447 +512 465 1321205.99576 +513 465 -2315242.24089 +514 465 -27106.375452 +515 465 -9164.38855491 +516 465 -5492991.14412 +517 465 416676.259817 +518 465 -1344116.96715 +519 465 -2296979.40748 +466 466 2350262.78049 +467 466 -4600302.86075 +468 466 2.04890966415e-8 +469 466 -7.45058059692e-9 +472 466 -311975.890718 +473 466 1184718.57235 +474 466 -1.02445483208e-8 +514 466 -193836.559394 +515 466 625279.223852 +516 466 -416676.259817 +517 466 -474421.1715 +518 466 -131724.429407 +519 466 23329.9193232 +520 466 -800250.281991 +521 466 8988445.57462 +523 466 -144050.092155 +524 466 547025.666412 +525 466 358351.461509 +467 467 17048556.4258 +468 467 -7.45058059692e-8 +469 467 5.96046447754e-8 +470 467 7.45058059692e-9 +472 467 1184718.57235 +473 467 -4498931.28739 +474 467 3.35276126862e-8 +514 467 625279.223852 +515 467 -2017029.75436 +516 467 1344116.96715 +517 467 -131724.429407 +518 467 -28174.4048384 +519 467 6684.54707695 +520 467 -6040681.08601 +521 467 800250.281991 +523 467 547025.666412 +524 467 -2077312.65726 +525 467 -1360828.33484 +468 468 36176122.0894 +472 468 -1.210719347e-8 +473 468 4.09781932831e-8 +474 468 758227.34113 +514 468 -416676.259817 +515 468 1344116.96715 +516 468 -2296979.40748 +517 468 -23329.9193232 +518 468 -6684.54707699 +519 468 -14301137.6219 +523 468 358351.461509 +524 468 -1360828.33484 +525 468 -2284273.33935 +469 469 305878011.196 +470 469 -66093576.1502 +517 469 800250.281991 +518 469 6040681.08601 +520 469 64525513.4986 +521 469 4237777.92342 +470 470 549337215.254 +517 470 -8988445.57462 +518 470 -800250.281991 +520 470 4237777.92342 +521 470 48915433.0595 +471 471 715555.555556 +522 471 -357777.777778 +472 472 1011851.60912 +473 472 -4110093.43272 +474 472 6.14672899246e-8 +517 472 -144050.092155 +518 472 547025.666412 +519 472 -358351.461509 +523 472 22134.9913963 +524 472 -90320.994053 +525 472 18909.0650259 +473 473 16788621.6609 +474 473 -2.53319740295e-7 +517 473 547025.666412 +518 473 -2077312.65726 +519 473 1360828.33484 +523 473 -90320.994053 +524 473 370626.696215 +525 473 4631.25243446 +474 474 18240145.4814 +517 474 -358351.461509 +518 474 1360828.33484 +519 474 -2284273.33935 +523 474 -18909.0650259 +524 474 -4631.25243459 +525 474 -5293544.01962 +475 475 1011851.60912 +476 475 4110093.43272 +477 475 -4.842877388e-8 +478 475 -311975.890718 +479 475 -1184718.57235 +480 475 1.02445483208e-8 +526 475 22134.9913963 +527 475 90320.9940529 +528 475 -18909.0650259 +529 475 -144050.092155 +530 475 -547025.666412 +531 475 358351.461509 +476 476 16788621.6609 +477 476 -2.08616256714e-7 +478 476 -1184718.57235 +479 476 -4498931.28739 +480 476 3.35276126862e-8 +526 476 90320.9940529 +527 476 370626.696215 +528 476 4631.25243447 +529 476 -547025.666412 +530 476 -2077312.65726 +531 476 1360828.33484 +477 477 18240145.4814 +478 477 9.31322574615e-9 +479 477 2.98023223877e-8 +480 477 758227.34113 +526 477 18909.0650259 +527 477 -4631.25243456 +528 477 -5293544.01962 +529 477 358351.461509 +530 477 1360828.33484 +531 477 -2284273.33935 +478 478 2350262.38096 +479 478 4600302.12491 +480 478 -2.421438694e-8 +481 478 2.98023223877e-8 +482 478 -1.19209289551e-7 +484 478 -413671.243673 +485 478 -1334423.36669 +486 478 1.49011611938e-8 +526 478 -144050.092155 +527 478 -547025.666412 +528 478 -358351.461509 +529 478 -474420.971735 +530 478 131724.797327 +531 478 -23329.9193232 +532 478 800256.904552 +533 478 8988441.97885 +535 478 -193836.559394 +536 478 -625279.223852 +537 478 416676.259817 +479 479 17048556.8253 +480 479 -8.19563865662e-8 +481 479 5.96046447754e-8 +484 479 -1334423.36669 +485 479 -4304591.50545 +486 479 4.47034835815e-8 +526 479 -547025.666412 +527 479 -2077312.65726 +528 479 -1360828.33484 +529 479 131724.797327 +530 479 -28174.6046032 +531 479 6684.54707693 +532 479 -6040684.68178 +533 479 -800256.904552 +535 479 -625279.223852 +536 479 -2017029.75436 +537 479 1344116.96715 +480 480 36176122.0894 +484 480 1.210719347e-8 +485 480 3.35276126862e-8 +486 480 830431.306934 +526 480 -358351.461509 +527 480 -1360828.33484 +528 480 -2284273.33935 +529 480 23329.9193232 +530 480 -6684.54707697 +531 480 -14301137.6219 +535 480 416676.259817 +536 480 1344116.96715 +537 480 -2296979.40748 +481 481 305878308.174 +482 481 66094123.115 +529 481 -800256.904552 +530 481 6040684.68178 +532 481 64525494.457 +533 481 -4237812.99363 +482 482 549336918.275 +529 482 -8988441.97885 +530 482 800256.904552 +532 482 -4237812.99363 +533 482 48915452.1011 +483 483 715555.555556 +534 483 -357777.777778 +484 484 1816739.3315 +485 484 5341279.87864 +486 484 -3.72529029846e-8 +487 484 -544032.43723 +488 484 -1483724.82881 +489 484 2.23517417908e-8 +529 484 -193836.559394 +530 484 -625279.223852 +531 484 -416676.259817 +535 484 24667.0078013 +536 484 73754.1287475 +537 484 -27106.375452 +538 484 -260348.273704 +539 484 -710040.746465 +540 484 484442.198447 +485 485 15814018.062 +486 485 -1.19209289551e-7 +487 485 -1483724.82881 +488 485 -4046522.26039 +489 485 5.96046447754e-8 +529 485 -625279.223852 +530 485 -2017029.75436 +531 485 -1344116.96715 +535 485 73754.1287475 +536 485 222052.369365 +537 485 9164.38855485 +538 485 -710040.746465 +539 485 -1936474.76309 +540 485 1321205.99576 +486 486 18448886.5844 +487 486 1.95577740669e-8 +488 486 5.21540641785e-8 +489 486 931107.693647 +529 486 -416676.259817 +530 486 -1344116.96715 +531 486 -2296979.40748 +535 486 27106.375452 +536 486 -9164.3885549 +537 486 -5492991.14412 +538 486 484442.198447 +539 486 1321205.99576 +540 486 -2315242.24089 +487 487 2558539.62181 +488 487 6138307.60949 +489 487 -4.842877388e-8 +490 487 -759692.612592 +491 487 -1651505.67955 +492 487 2.60770320892e-8 +535 487 -260348.273704 +536 487 -710040.746465 +537 487 -484442.198447 +538 487 12227.6197724 +539 487 33038.3525624 +540 487 -41457.2556898 +541 487 -379286.632065 +542 487 -824536.156663 +543 487 588085.337671 +488 488 14915778.6682 +489 488 -1.19209289551e-7 +490 488 -1651505.67955 +491 488 -3590229.73815 +492 488 5.77419996262e-8 +535 488 -710040.746465 +536 488 -1936474.76309 +537 488 -1321205.99576 +538 488 33038.3525624 +539 488 89431.3340664 +540 488 17103.8438088 +541 488 -824536.156663 +542 488 -1792469.90579 +543 488 1278446.38624 +489 489 18669379.7869 +490 489 2.32830643654e-8 +491 489 5.21540641785e-8 +492 489 1124555.53479 +535 489 -484442.198447 +536 489 -1321205.99576 +537 489 -2315242.24089 +538 489 41457.2556898 +539 489 -17103.8438089 +540 489 -5695176.55864 +541 489 588085.337671 +542 489 1278446.38624 +543 489 -2352102.70813 +490 490 3880798.16119 +491 490 7110485.96967 +492 490 -4.842877388e-8 +493 490 -1076372.38828 +494 490 -1736084.49722 +495 490 2.421438694e-8 +538 490 -379286.632065 +539 490 -824536.156663 +540 490 -588085.337671 +541 490 -52167.0393845 +542 490 -83826.4031561 +543 490 -61373.985991 +544 490 -590912.90871 +545 490 -953085.336629 +546 490 741520.302649 +491 491 13318817.2723 +492 491 -9.685754776e-8 +493 491 -1736084.49722 +494 491 -2800136.28584 +495 491 3.91155481339e-8 +538 491 -824536.156663 +539 491 -1792469.90579 +540 491 -1278446.38624 +541 491 -83826.4031561 +542 491 -134521.304432 +543 491 32978.3592395 +544 491 -953085.336629 +545 491 -1537234.41392 +546 491 1196000.48814 +492 492 19146980.3477 +493 492 1.67638063431e-8 +494 492 2.98023223877e-8 +495 492 1520483.39551 +538 492 -588085.337671 +539 492 -1278446.38624 +540 492 -2352102.70813 +541 492 61373.985991 +542 492 -32978.3592395 +543 492 -6109264.54972 +544 492 741520.302649 +545 492 1196000.48814 +546 492 -2434642.38116 +493 493 6516876.42304 +494 493 8531914.96891 +495 493 -1.49011611938e-8 +496 493 -2626121.17726 +497 493 -2984228.61052 +498 493 9.31322574615e-9 +541 493 -590912.90871 +542 493 -953085.336629 +543 493 -741520.302649 +544 493 222027.67781 +545 493 227177.812693 +546 493 -75251.5247453 +547 493 -1038306.19785 +548 493 -1179893.40665 +549 493 929649.114512 +494 494 11512089.4984 +495 494 -2.23517417908e-8 +496 494 -2984228.61052 +497 494 -3391168.87559 +498 494 1.11758708954e-8 +541 494 -953085.336629 +542 494 -1537234.41392 +543 494 -1196000.48814 +544 494 227177.812693 +545 494 217630.207531 +546 494 55832.4159336 +547 494 -1179893.40665 +548 494 -1340787.9621 +549 494 1056419.44831 +495 495 18361731.7736 +496 495 1.86264514923e-9 +497 495 1.86264514923e-9 +498 495 -125380.553907 +541 495 -741520.302649 +542 495 -1196000.48814 +543 495 -2434642.38116 +544 495 75251.5247453 +545 495 -55832.4159336 +546 495 -5287984.36193 +547 495 929649.114512 +548 495 1056419.44831 +549 495 -2155790.5645 +496 496 11197028.9194 +497 496 7624235.1212 +498 496 -1.49011611938e-8 +499 496 -1154846.00933 +500 496 -476225.158488 +501 496 7.45058059692e-9 +544 496 -1038306.19785 +545 496 -1179893.40665 +546 496 -929649.114512 +547 496 -908773.635179 +548 496 -175831.894858 +549 496 -148519.569575 +550 496 -1760951.03338 +551 496 -726165.37459 +552 496 1300948.03845 +497 497 6560950.4012 +498 497 -7.45058059692e-9 +499 497 -476225.158488 +500 497 -196381.508655 +501 497 2.79396772385e-9 +544 497 -1179893.40665 +545 497 -1340787.9621 +546 497 -1056419.44831 +547 497 -175831.894858 +548 497 153537.59263 +549 497 207978.412157 +550 497 -726165.37459 +551 497 -299449.639006 +552 497 536473.417918 +498 498 21281133.3311 +501 498 4075797.18655 +544 498 -929649.114512 +545 498 -1056419.44831 +546 498 -2155790.5645 +547 498 148519.569575 +548 498 -207978.412157 +549 498 -7295491.64648 +550 498 1300948.03845 +551 498 536473.417918 +552 498 -3164492.7709 +499 499 14087608.2601 +501 499 -7.45058059692e-9 +502 499 -1154846.00933 +503 499 476225.158488 +504 499 7.45058059692e-9 +547 499 -1760951.03338 +548 499 -726165.37459 +549 499 -1300948.03845 +550 499 -2367056.05396 +552 499 -5.58793544769e-9 +553 499 -1760951.03338 +554 499 726165.37459 +555 499 1300948.03845 +500 500 2395597.11087 +501 500 7.45058059692e-9 +502 500 476225.158488 +503 500 -196381.508655 +504 500 -2.79396772385e-9 +547 500 -726165.37459 +548 500 -299449.639006 +549 500 -536473.417918 +551 500 -402517.768767 +552 500 429178.734334 +553 500 726165.37459 +554 500 -299449.639006 +555 500 -536473.417918 +501 501 25315942.1547 +504 501 4075797.18655 +547 501 -1300948.03845 +548 501 -536473.417918 +549 501 -3164492.7709 +550 501 3.72529029846e-9 +551 501 -429178.734334 +552 501 -10404782.7221 +553 501 1300948.03845 +554 501 -536473.417918 +555 501 -3164492.7709 +502 502 11197028.9194 +503 502 -7624235.1212 +504 502 1.11758708954e-8 +505 502 -2626121.17726 +506 502 2984228.61052 +507 502 -5.58793544769e-9 +550 502 -1760951.03338 +551 502 726165.37459 +552 502 -1300948.03845 +553 502 -908773.635179 +554 502 175831.894858 +555 502 148519.569575 +556 502 -1038306.19785 +557 502 1179893.40665 +558 502 929649.114512 +503 503 6560950.4012 +504 503 -7.45058059692e-9 +505 503 2984228.61052 +506 503 -3391168.87559 +507 503 5.58793544769e-9 +550 503 726165.37459 +551 503 -299449.639006 +552 503 536473.417918 +553 503 175831.894858 +554 503 153537.59263 +555 503 207978.412157 +556 503 1179893.40665 +557 503 -1340787.9621 +558 503 -1056419.44831 +504 504 21281133.3311 +505 504 -1.30385160446e-8 +506 504 1.30385160446e-8 +507 504 -125380.553907 +550 504 -1300948.03845 +551 504 536473.417918 +552 504 -3164492.7709 +553 504 -148519.569575 +554 504 -207978.412157 +555 504 -7295491.64648 +556 504 929649.114512 +557 504 -1056419.44831 +558 504 -2155790.5645 +505 505 6516876.42304 +506 505 -8531914.96891 +507 505 2.98023223877e-8 +508 505 -1076372.38828 +509 505 1736084.49722 +510 505 -1.49011611938e-8 +553 505 -1038306.19785 +554 505 1179893.40665 +555 505 -929649.114512 +556 505 222027.67781 +557 505 -227177.812693 +558 505 75251.5247453 +559 505 -590912.90871 +560 505 953085.336629 +561 505 741520.302649 +506 506 11512089.4984 +507 506 -3.72529029846e-8 +508 506 1736084.49722 +509 506 -2800136.28584 +510 506 2.421438694e-8 +553 506 1179893.40665 +554 506 -1340787.9621 +555 506 1056419.44831 +556 506 -227177.812693 +557 506 217630.207531 +558 506 55832.4159336 +559 506 953085.336629 +560 506 -1537234.41392 +561 506 -1196000.48814 +507 507 18361731.7736 +508 507 -2.04890966415e-8 +509 507 3.35276126862e-8 +510 507 1520483.39551 +553 507 -929649.114512 +554 507 1056419.44831 +555 507 -2155790.5645 +556 507 -75251.5247453 +557 507 -55832.4159336 +558 507 -5287984.36193 +559 507 741520.302649 +560 507 -1196000.48814 +561 507 -2434642.38116 +508 508 3880798.16119 +509 508 -7110485.96967 +510 508 3.35276126862e-8 +511 508 -759692.612592 +512 508 1651505.67955 +513 508 -1.86264514923e-8 +556 508 -590912.90871 +557 508 953085.336629 +558 508 -741520.302649 +559 508 -52167.0393845 +560 508 83826.4031561 +561 508 61373.985991 +562 508 -379286.632065 +563 508 824536.156663 +564 508 588085.337671 +509 509 13318817.2723 +510 509 -5.96046447754e-8 +511 509 1651505.67955 +512 509 -3590229.73815 +513 509 4.09781932831e-8 +556 509 953085.336629 +557 509 -1537234.41392 +558 509 1196000.48814 +559 509 83826.4031561 +560 509 -134521.304432 +561 509 32978.3592395 +562 509 824536.156663 +563 509 -1792469.90579 +564 509 -1278446.38624 +510 510 19146980.3477 +511 510 -2.14204192162e-8 +512 510 4.842877388e-8 +513 510 1124555.53479 +556 510 -741520.302649 +557 510 1196000.48814 +558 510 -2434642.38116 +559 510 -61373.985991 +560 510 -32978.3592395 +561 510 -6109264.54972 +562 510 588085.337671 +563 510 -1278446.38624 +564 510 -2352102.70813 +511 511 2558539.62181 +512 511 -6138307.60949 +513 511 4.28408384323e-8 +514 511 -544032.43723 +515 511 1483724.82881 +516 511 -2.32830643654e-8 +559 511 -379286.632065 +560 511 824536.156663 +561 511 -588085.337671 +562 511 12227.6197724 +563 511 -33038.3525624 +564 511 41457.2556898 +565 511 -260348.273704 +566 511 710040.746465 +567 511 484442.198447 +512 512 14915778.6682 +513 512 -1.04308128357e-7 +514 512 1483724.82881 +515 512 -4046522.26039 +516 512 6.70552253723e-8 +559 512 824536.156663 +560 512 -1792469.90579 +561 512 1278446.38624 +562 512 -33038.3525624 +563 512 89431.3340665 +564 512 17103.8438088 +565 512 710040.746465 +566 512 -1936474.76309 +567 512 -1321205.99576 +513 513 18669379.7869 +514 513 -2.60770320892e-8 +515 513 7.45058059692e-8 +516 513 931107.693647 +559 513 -588085.337671 +560 513 1278446.38624 +561 513 -2352102.70813 +562 513 -41457.2556898 +563 513 -17103.8438089 +564 513 -5695176.55864 +565 513 484442.198447 +566 513 -1321205.99576 +567 513 -2315242.24089 +514 514 1816739.3315 +515 514 -5341279.87864 +516 514 3.53902578354e-8 +517 514 -413671.243673 +518 514 1334423.36669 +519 514 -6.51925802231e-9 +562 514 -260348.273704 +563 514 710040.746465 +564 514 -484442.198447 +565 514 24667.0078013 +566 514 -73754.1287475 +567 514 27106.375452 +568 514 -193836.559394 +569 514 625279.223852 +570 514 416676.259817 +515 515 15814018.062 +516 515 -1.04308128357e-7 +517 515 1334423.36669 +518 515 -4304591.50545 +519 515 2.60770320892e-8 +562 515 710040.746465 +563 515 -1936474.76309 +564 515 1321205.99576 +565 515 -73754.1287475 +566 515 222052.369366 +567 515 9164.38855486 +568 515 625279.223852 +569 515 -2017029.75436 +570 515 -1344116.96715 +516 516 18448886.5844 +517 516 -1.11758708954e-8 +518 516 3.35276126862e-8 +519 516 830431.306934 +562 516 -484442.198447 +563 516 1321205.99576 +564 516 -2315242.24089 +565 516 -27106.375452 +566 516 -9164.38855491 +567 516 -5492991.14412 +568 516 416676.259817 +569 516 -1344116.96715 +570 516 -2296979.40748 +517 517 2350262.78049 +518 517 -4600302.86075 +519 517 1.86264514923e-8 +520 517 -7.45058059692e-9 +523 517 -311975.890718 +524 517 1184718.57235 +525 517 -1.11758708954e-8 +565 517 -193836.559394 +566 517 625279.223852 +567 517 -416676.259817 +568 517 -474421.1715 +569 517 -131724.429407 +570 517 23329.9193232 +571 517 -800250.281991 +572 517 8988445.57462 +574 517 -144050.092155 +575 517 547025.666412 +576 517 358351.461509 +518 518 17048556.4258 +519 518 -6.70552253723e-8 +520 518 5.96046447754e-8 +521 518 7.45058059692e-9 +523 518 1184718.57235 +524 518 -4498931.28739 +525 518 3.72529029846e-8 +565 518 625279.223852 +566 518 -2017029.75436 +567 518 1344116.96715 +568 518 -131724.429407 +569 518 -28174.4048383 +570 518 6684.54707695 +571 518 -6040681.08601 +572 518 800250.281991 +574 518 547025.666412 +575 518 -2077312.65726 +576 518 -1360828.33484 +519 519 36176122.0894 +523 519 -1.30385160446e-8 +524 519 4.47034835815e-8 +525 519 758227.34113 +565 519 -416676.259817 +566 519 1344116.96715 +567 519 -2296979.40748 +568 519 -23329.9193232 +569 519 -6684.54707699 +570 519 -14301137.6219 +574 519 358351.461509 +575 519 -1360828.33484 +576 519 -2284273.33935 +520 520 305878011.196 +521 520 -66093576.1502 +568 520 800250.281991 +569 520 6040681.08601 +571 520 64525513.4986 +572 520 4237777.92342 +521 521 549337215.254 +568 521 -8988445.57462 +569 521 -800250.281991 +571 521 4237777.92342 +572 521 48915433.0595 +522 522 715555.555556 +573 522 -357777.777778 +523 523 1011851.60912 +524 523 -4110093.43272 +525 523 6.14672899246e-8 +568 523 -144050.092155 +569 523 547025.666412 +570 523 -358351.461509 +574 523 22134.9913963 +575 523 -90320.994053 +576 523 18909.0650259 +524 524 16788621.6609 +525 524 -2.53319740295e-7 +568 524 547025.666412 +569 524 -2077312.65726 +570 524 1360828.33484 +574 524 -90320.994053 +575 524 370626.696215 +576 524 4631.25243446 +525 525 18240145.4814 +568 525 -358351.461509 +569 525 1360828.33484 +570 525 -2284273.33935 +574 525 -18909.0650259 +575 525 -4631.25243459 +576 525 -5293544.01962 +526 526 1011851.60912 +527 526 4110093.43272 +528 526 -4.65661287308e-8 +529 526 -311975.890718 +530 526 -1184718.57235 +531 526 1.02445483208e-8 +577 526 22134.9913963 +578 526 90320.994053 +579 526 -18909.0650259 +580 526 -144050.092155 +581 526 -547025.666412 +582 526 358351.461509 +527 527 16788621.6609 +528 527 -2.01165676117e-7 +529 527 -1184718.57235 +530 527 -4498931.28739 +531 527 3.35276126862e-8 +577 527 90320.994053 +578 527 370626.696215 +579 527 4631.25243448 +580 527 -547025.666412 +581 527 -2077312.65726 +582 527 1360828.33484 +528 528 18240145.4814 +529 528 8.38190317154e-9 +530 528 2.60770320892e-8 +531 528 758227.34113 +577 528 18909.0650259 +578 528 -4631.25243456 +579 528 -5293544.01962 +580 528 358351.461509 +581 528 1360828.33484 +582 528 -2284273.33935 +529 529 2350262.38096 +530 529 4600302.12491 +531 529 -2.23517417908e-8 +532 529 2.98023223877e-8 +533 529 -1.19209289551e-7 +535 529 -413671.243673 +536 529 -1334423.36669 +537 529 1.49011611938e-8 +577 529 -144050.092155 +578 529 -547025.666412 +579 529 -358351.461509 +580 529 -474420.971735 +581 529 131724.797327 +582 529 -23329.9193232 +583 529 800256.904552 +584 529 8988441.97885 +586 529 -193836.559394 +587 529 -625279.223852 +588 529 416676.259817 +530 530 17048556.8253 +531 530 -7.45058059692e-8 +532 530 5.96046447754e-8 +535 530 -1334423.36669 +536 530 -4304591.50545 +537 530 4.47034835815e-8 +577 530 -547025.666412 +578 530 -2077312.65726 +579 530 -1360828.33484 +580 530 131724.797327 +581 530 -28174.6046032 +582 530 6684.54707693 +583 530 -6040684.68178 +584 530 -800256.904552 +586 530 -625279.223852 +587 530 -2017029.75436 +588 530 1344116.96715 +531 531 36176122.0894 +535 531 1.210719347e-8 +536 531 3.35276126862e-8 +537 531 830431.306934 +577 531 -358351.461509 +578 531 -1360828.33484 +579 531 -2284273.33935 +580 531 23329.9193232 +581 531 -6684.54707697 +582 531 -14301137.6219 +586 531 416676.259817 +587 531 1344116.96715 +588 531 -2296979.40748 +532 532 305878308.174 +533 532 66094123.115 +580 532 -800256.904552 +581 532 6040684.68178 +583 532 64525494.457 +584 532 -4237812.99363 +533 533 549336918.275 +580 533 -8988441.97885 +581 533 800256.904552 +583 533 -4237812.99363 +584 533 48915452.1011 +534 534 715555.555556 +585 534 -357777.777778 +535 535 1816739.3315 +536 535 5341279.87864 +537 535 -3.72529029846e-8 +538 535 -544032.43723 +539 535 -1483724.82881 +540 535 2.23517417908e-8 +580 535 -193836.559394 +581 535 -625279.223852 +582 535 -416676.259817 +586 535 24667.0078013 +587 535 73754.1287475 +588 535 -27106.375452 +589 535 -260348.273704 +590 535 -710040.746465 +591 535 484442.198447 +536 536 15814018.062 +537 536 -1.19209289551e-7 +538 536 -1483724.82881 +539 536 -4046522.26039 +540 536 5.96046447754e-8 +580 536 -625279.223852 +581 536 -2017029.75436 +582 536 -1344116.96715 +586 536 73754.1287475 +587 536 222052.369365 +588 536 9164.38855485 +589 536 -710040.746465 +590 536 -1936474.76309 +591 536 1321205.99576 +537 537 18448886.5844 +538 537 1.95577740669e-8 +539 537 5.21540641785e-8 +540 537 931107.693647 +580 537 -416676.259817 +581 537 -1344116.96715 +582 537 -2296979.40748 +586 537 27106.375452 +587 537 -9164.3885549 +588 537 -5492991.14412 +589 537 484442.198447 +590 537 1321205.99576 +591 537 -2315242.24089 +538 538 2558539.62181 +539 538 6138307.60949 +540 538 -4.842877388e-8 +541 538 -759692.612592 +542 538 -1651505.67955 +543 538 2.79396772385e-8 +586 538 -260348.273704 +587 538 -710040.746465 +588 538 -484442.198447 +589 538 12227.6197723 +590 538 33038.3525623 +591 538 -41457.2556898 +592 538 -379286.632065 +593 538 -824536.156663 +594 538 588085.337671 +539 539 14915778.6682 +540 539 -1.19209289551e-7 +541 539 -1651505.67955 +542 539 -3590229.73815 +543 539 6.14672899246e-8 +586 539 -710040.746465 +587 539 -1936474.76309 +588 539 -1321205.99576 +589 539 33038.3525623 +590 539 89431.3340663 +591 539 17103.8438088 +592 539 -824536.156663 +593 539 -1792469.90579 +594 539 1278446.38624 +540 540 18669379.7869 +541 540 2.70083546639e-8 +542 540 5.77419996262e-8 +543 540 1124555.53479 +586 540 -484442.198447 +587 540 -1321205.99576 +588 540 -2315242.24089 +589 540 41457.2556898 +590 540 -17103.8438089 +591 540 -5695176.55864 +592 540 588085.337671 +593 540 1278446.38624 +594 540 -2352102.70813 +541 541 3880798.16119 +542 541 7110485.96967 +543 541 -4.47034835815e-8 +544 541 -1076372.38828 +545 541 -1736084.49722 +546 541 2.421438694e-8 +589 541 -379286.632065 +590 541 -824536.156663 +591 541 -588085.337671 +592 541 -52167.0393845 +593 541 -83826.4031561 +594 541 -61373.985991 +595 541 -590912.90871 +596 541 -953085.336629 +597 541 741520.302649 +542 542 13318817.2723 +543 542 -8.94069671631e-8 +544 542 -1736084.49722 +545 542 -2800136.28584 +546 542 3.91155481339e-8 +589 542 -824536.156663 +590 542 -1792469.90579 +591 542 -1278446.38624 +592 542 -83826.4031561 +593 542 -134521.304432 +594 542 32978.3592395 +595 542 -953085.336629 +596 542 -1537234.41392 +597 542 1196000.48814 +543 543 19146980.3477 +544 543 1.67638063431e-8 +545 543 2.98023223877e-8 +546 543 1520483.39551 +589 543 -588085.337671 +590 543 -1278446.38624 +591 543 -2352102.70813 +592 543 61373.985991 +593 543 -32978.3592395 +594 543 -6109264.54972 +595 543 741520.302649 +596 543 1196000.48814 +597 543 -2434642.38116 +544 544 6516876.42304 +545 544 8531914.96891 +546 544 -1.49011611938e-8 +547 544 -2626121.17726 +548 544 -2984228.61052 +549 544 5.58793544769e-9 +592 544 -590912.90871 +593 544 -953085.336629 +594 544 -741520.302649 +595 544 222027.67781 +596 544 227177.812693 +597 544 -75251.5247453 +598 544 -1038306.19785 +599 544 -1179893.40665 +600 544 929649.114512 +545 545 11512089.4984 +546 545 -2.23517417908e-8 +547 545 -2984228.61052 +548 545 -3391168.87559 +549 545 9.31322574615e-9 +592 545 -953085.336629 +593 545 -1537234.41392 +594 545 -1196000.48814 +595 545 227177.812693 +596 545 217630.207531 +597 545 55832.4159336 +598 545 -1179893.40665 +599 545 -1340787.9621 +600 545 1056419.44831 +546 546 18361731.7736 +549 546 -125380.553907 +592 546 -741520.302649 +593 546 -1196000.48814 +594 546 -2434642.38116 +595 546 75251.5247453 +596 546 -55832.4159336 +597 546 -5287984.36193 +598 546 929649.114512 +599 546 1056419.44831 +600 546 -2155790.5645 +547 547 11197028.9194 +548 547 7624235.1212 +549 547 1.49011611938e-8 +550 547 -1154846.00933 +551 547 -476225.158488 +595 547 -1038306.19785 +596 547 -1179893.40665 +597 547 -929649.114512 +598 547 -908773.635179 +599 547 -175831.894858 +600 547 -148519.569575 +601 547 -1760951.03338 +602 547 -726165.37459 +603 547 1300948.03845 +548 548 6560950.4012 +549 548 7.45058059692e-9 +550 548 -476225.158488 +551 548 -196381.508655 +595 548 -1179893.40665 +596 548 -1340787.9621 +597 548 -1056419.44831 +598 548 -175831.894858 +599 548 153537.59263 +600 548 207978.412157 +601 548 -726165.37459 +602 548 -299449.639006 +603 548 536473.417918 +549 549 21281133.3311 +552 549 4075797.18655 +595 549 -929649.114512 +596 549 -1056419.44831 +597 549 -2155790.5645 +598 549 148519.569575 +599 549 -207978.412157 +600 549 -7295491.64648 +601 549 1300948.03845 +602 549 536473.417918 +603 549 -3164492.7709 +550 550 14087608.2601 +551 550 -7.45058059692e-9 +552 550 -7.45058059692e-9 +553 550 -1154846.00933 +554 550 476225.158488 +598 550 -1760951.03338 +599 550 -726165.37459 +600 550 -1300948.03845 +601 550 -2367056.05396 +602 550 1.11758708954e-8 +603 550 1.86264514923e-9 +604 550 -1760951.03338 +605 550 726165.37459 +606 550 1300948.03845 +551 551 2395597.11087 +552 551 -7.45058059692e-9 +553 551 476225.158488 +554 551 -196381.508655 +598 551 -726165.37459 +599 551 -299449.639006 +600 551 -536473.417918 +601 551 9.31322574615e-9 +602 551 -402517.768767 +603 551 429178.734334 +604 551 726165.37459 +605 551 -299449.639006 +606 551 -536473.417918 +552 552 25315942.1547 +555 552 4075797.18655 +598 552 -1300948.03845 +599 552 -536473.417918 +600 552 -3164492.7709 +602 552 -429178.734334 +603 552 -10404782.7221 +604 552 1300948.03845 +605 552 -536473.417918 +606 552 -3164492.7709 +553 553 11197028.9194 +554 553 -7624235.1212 +555 553 -1.86264514923e-8 +556 553 -2626121.17726 +557 553 2984228.61052 +558 553 -5.58793544769e-9 +601 553 -1760951.03338 +602 553 726165.37459 +603 553 -1300948.03845 +604 553 -908773.635179 +605 553 175831.894858 +606 553 148519.569575 +607 553 -1038306.19785 +608 553 1179893.40665 +609 553 929649.114512 +554 554 6560950.4012 +555 554 7.45058059692e-9 +556 554 2984228.61052 +557 554 -3391168.87559 +558 554 5.58793544769e-9 +601 554 726165.37459 +602 554 -299449.639006 +603 554 536473.417918 +604 554 175831.894858 +605 554 153537.59263 +606 554 207978.412157 +607 554 1179893.40665 +608 554 -1340787.9621 +609 554 -1056419.44831 +555 555 21281133.3311 +556 555 -1.30385160446e-8 +557 555 1.30385160446e-8 +558 555 -125380.553907 +601 555 -1300948.03845 +602 555 536473.417918 +603 555 -3164492.7709 +604 555 -148519.569575 +605 555 -207978.412157 +606 555 -7295491.64648 +607 555 929649.114512 +608 555 -1056419.44831 +609 555 -2155790.5645 +556 556 6516876.42304 +557 556 -8531914.96891 +558 556 2.98023223877e-8 +559 556 -1076372.38828 +560 556 1736084.49722 +561 556 -1.49011611938e-8 +604 556 -1038306.19785 +605 556 1179893.40665 +606 556 -929649.114512 +607 556 222027.67781 +608 556 -227177.812693 +609 556 75251.5247453 +610 556 -590912.90871 +611 556 953085.336629 +612 556 741520.302649 +557 557 11512089.4984 +558 557 -3.72529029846e-8 +559 557 1736084.49722 +560 557 -2800136.28584 +561 557 2.421438694e-8 +604 557 1179893.40665 +605 557 -1340787.9621 +606 557 1056419.44831 +607 557 -227177.812693 +608 557 217630.207531 +609 557 55832.4159336 +610 557 953085.336629 +611 557 -1537234.41392 +612 557 -1196000.48814 +558 558 18361731.7736 +559 558 -1.86264514923e-8 +560 558 3.16649675369e-8 +561 558 1520483.39551 +604 558 -929649.114512 +605 558 1056419.44831 +606 558 -2155790.5645 +607 558 -75251.5247453 +608 558 -55832.4159336 +609 558 -5287984.36193 +610 558 741520.302649 +611 558 -1196000.48814 +612 558 -2434642.38116 +559 559 3880798.16119 +560 559 -7110485.96967 +561 559 2.60770320892e-8 +562 559 -759692.612592 +563 559 1651505.67955 +564 559 -1.67638063431e-8 +607 559 -590912.90871 +608 559 953085.336629 +609 559 -741520.302649 +610 559 -52167.0393845 +611 559 83826.4031561 +612 559 61373.985991 +613 559 -379286.632065 +614 559 824536.156663 +615 559 588085.337671 +560 560 13318817.2723 +561 560 -4.47034835815e-8 +562 560 1651505.67955 +563 560 -3590229.73815 +564 560 3.72529029846e-8 +607 560 953085.336629 +608 560 -1537234.41392 +609 560 1196000.48814 +610 560 83826.4031561 +611 560 -134521.304432 +612 560 32978.3592395 +613 560 824536.156663 +614 560 -1792469.90579 +615 560 -1278446.38624 +561 561 19146980.3477 +562 561 -2.14204192162e-8 +563 561 4.842877388e-8 +564 561 1124555.53479 +607 561 -741520.302649 +608 561 1196000.48814 +609 561 -2434642.38116 +610 561 -61373.985991 +611 561 -32978.3592395 +612 561 -6109264.54972 +613 561 588085.337671 +614 561 -1278446.38624 +615 561 -2352102.70813 +562 562 2558539.62181 +563 562 -6138307.60949 +564 562 4.28408384323e-8 +565 562 -544032.43723 +566 562 1483724.82881 +567 562 -2.32830643654e-8 +610 562 -379286.632065 +611 562 824536.156663 +612 562 -588085.337671 +613 562 12227.6197724 +614 562 -33038.3525623 +615 562 41457.2556898 +616 562 -260348.273704 +617 562 710040.746465 +618 562 484442.198447 +563 563 14915778.6682 +564 563 -1.11758708954e-7 +565 563 1483724.82881 +566 563 -4046522.26039 +567 563 6.70552253723e-8 +610 563 824536.156663 +611 563 -1792469.90579 +612 563 1278446.38624 +613 563 -33038.3525623 +614 563 89431.3340664 +615 563 17103.8438088 +616 563 710040.746465 +617 563 -1936474.76309 +618 563 -1321205.99576 +564 564 18669379.7869 +565 564 -2.60770320892e-8 +566 564 7.45058059692e-8 +567 564 931107.693647 +610 564 -588085.337671 +611 564 1278446.38624 +612 564 -2352102.70813 +613 564 -41457.2556898 +614 564 -17103.8438089 +615 564 -5695176.55864 +616 564 484442.198447 +617 564 -1321205.99576 +618 564 -2315242.24089 +565 565 1816739.3315 +566 565 -5341279.87864 +567 565 3.53902578354e-8 +568 565 -413671.243673 +569 565 1334423.36669 +570 565 -6.51925802231e-9 +613 565 -260348.273704 +614 565 710040.746465 +615 565 -484442.198447 +616 565 24667.0078013 +617 565 -73754.1287475 +618 565 27106.375452 +619 565 -193836.559394 +620 565 625279.223852 +621 565 416676.259817 +566 566 15814018.062 +567 566 -1.04308128357e-7 +568 566 1334423.36669 +569 566 -4304591.50545 +570 566 2.60770320892e-8 +613 566 710040.746465 +614 566 -1936474.76309 +615 566 1321205.99576 +616 566 -73754.1287475 +617 566 222052.369366 +618 566 9164.38855486 +619 566 625279.223852 +620 566 -2017029.75436 +621 566 -1344116.96715 +567 567 18448886.5844 +568 567 -1.11758708954e-8 +569 567 3.35276126862e-8 +570 567 830431.306934 +613 567 -484442.198447 +614 567 1321205.99576 +615 567 -2315242.24089 +616 567 -27106.375452 +617 567 -9164.38855491 +618 567 -5492991.14412 +619 567 416676.259817 +620 567 -1344116.96715 +621 567 -2296979.40748 +568 568 2350262.78049 +569 568 -4600302.86075 +570 568 1.86264514923e-8 +571 568 -7.45058059692e-9 +574 568 -311975.890718 +575 568 1184718.57235 +576 568 -1.11758708954e-8 +616 568 -193836.559394 +617 568 625279.223852 +618 568 -416676.259817 +619 568 -474421.1715 +620 568 -131724.429407 +621 568 23329.9193232 +622 568 -800250.281991 +623 568 8988445.57462 +625 568 -144050.092155 +626 568 547025.666412 +627 568 358351.461509 +569 569 17048556.4258 +570 569 -6.70552253723e-8 +571 569 5.96046447754e-8 +572 569 7.45058059692e-9 +574 569 1184718.57235 +575 569 -4498931.28739 +576 569 3.72529029846e-8 +616 569 625279.223852 +617 569 -2017029.75436 +618 569 1344116.96715 +619 569 -131724.429407 +620 569 -28174.4048383 +621 569 6684.54707695 +622 569 -6040681.08601 +623 569 800250.281991 +625 569 547025.666412 +626 569 -2077312.65726 +627 569 -1360828.33484 +570 570 36176122.0894 +574 570 -1.30385160446e-8 +575 570 4.47034835815e-8 +576 570 758227.34113 +616 570 -416676.259817 +617 570 1344116.96715 +618 570 -2296979.40748 +619 570 -23329.9193232 +620 570 -6684.54707699 +621 570 -14301137.6219 +625 570 358351.461509 +626 570 -1360828.33484 +627 570 -2284273.33935 +571 571 305878011.196 +572 571 -66093576.1502 +619 571 800250.281991 +620 571 6040681.08601 +622 571 64525513.4986 +623 571 4237777.92342 +572 572 549337215.254 +619 572 -8988445.57462 +620 572 -800250.281991 +622 572 4237777.92342 +623 572 48915433.0595 +573 573 715555.555556 +624 573 -357777.777778 +574 574 1011851.60912 +575 574 -4110093.43272 +576 574 6.33299350738e-8 +619 574 -144050.092155 +620 574 547025.666412 +621 574 -358351.461509 +625 574 22134.9913963 +626 574 -90320.994053 +627 574 18909.0650259 +575 575 16788621.6609 +576 575 -2.60770320892e-7 +619 575 547025.666412 +620 575 -2077312.65726 +621 575 1360828.33484 +625 575 -90320.994053 +626 575 370626.696215 +627 575 4631.25243446 +576 576 18240145.4814 +619 576 -358351.461509 +620 576 1360828.33484 +621 576 -2284273.33935 +625 576 -18909.0650259 +626 576 -4631.25243459 +627 576 -5293544.01962 +577 577 1011851.60912 +578 577 4110093.43272 +579 577 -4.842877388e-8 +580 577 -311975.890718 +581 577 -1184718.57235 +582 577 1.02445483208e-8 +628 577 22134.9913963 +629 577 90320.994053 +630 577 -18909.0650259 +631 577 -144050.092155 +632 577 -547025.666412 +633 577 358351.461509 +578 578 16788621.6609 +579 578 -2.08616256714e-7 +580 578 -1184718.57235 +581 578 -4498931.28739 +582 578 3.35276126862e-8 +628 578 90320.994053 +629 578 370626.696215 +630 578 4631.25243447 +631 578 -547025.666412 +632 578 -2077312.65726 +633 578 1360828.33484 +579 579 18240145.4814 +580 579 8.38190317154e-9 +581 579 2.60770320892e-8 +582 579 758227.34113 +628 579 18909.0650259 +629 579 -4631.25243456 +630 579 -5293544.01962 +631 579 358351.461509 +632 579 1360828.33484 +633 579 -2284273.33935 +580 580 2350262.38096 +581 580 4600302.12491 +582 580 -2.23517417908e-8 +583 580 2.98023223877e-8 +584 580 -1.19209289551e-7 +586 580 -413671.243673 +587 580 -1334423.36669 +588 580 1.49011611938e-8 +628 580 -144050.092155 +629 580 -547025.666412 +630 580 -358351.461509 +631 580 -474420.971735 +632 580 131724.797327 +633 580 -23329.9193232 +634 580 800256.904552 +635 580 8988441.97885 +637 580 -193836.559394 +638 580 -625279.223852 +639 580 416676.259817 +581 581 17048556.8253 +582 581 -7.45058059692e-8 +583 581 5.96046447754e-8 +586 581 -1334423.36669 +587 581 -4304591.50545 +588 581 4.47034835815e-8 +628 581 -547025.666412 +629 581 -2077312.65726 +630 581 -1360828.33484 +631 581 131724.797327 +632 581 -28174.6046032 +633 581 6684.54707693 +634 581 -6040684.68178 +635 581 -800256.904552 +637 581 -625279.223852 +638 581 -2017029.75436 +639 581 1344116.96715 +582 582 36176122.0894 +586 582 1.210719347e-8 +587 582 3.35276126862e-8 +588 582 830431.306934 +628 582 -358351.461509 +629 582 -1360828.33484 +630 582 -2284273.33935 +631 582 23329.9193232 +632 582 -6684.54707697 +633 582 -14301137.6219 +637 582 416676.259817 +638 582 1344116.96715 +639 582 -2296979.40748 +583 583 305878308.174 +584 583 66094123.115 +631 583 -800256.904552 +632 583 6040684.68178 +634 583 64525494.457 +635 583 -4237812.99363 +584 584 549336918.275 +631 584 -8988441.97885 +632 584 800256.904552 +634 584 -4237812.99363 +635 584 48915452.1011 +585 585 715555.555556 +636 585 -357777.777778 +586 586 1816739.3315 +587 586 5341279.87864 +588 586 -3.72529029846e-8 +589 586 -544032.43723 +590 586 -1483724.82881 +591 586 2.23517417908e-8 +631 586 -193836.559394 +632 586 -625279.223852 +633 586 -416676.259817 +637 586 24667.0078013 +638 586 73754.1287475 +639 586 -27106.375452 +640 586 -260348.273704 +641 586 -710040.746465 +642 586 484442.198447 +587 587 15814018.062 +588 587 -1.19209289551e-7 +589 587 -1483724.82881 +590 587 -4046522.26039 +591 587 5.96046447754e-8 +631 587 -625279.223852 +632 587 -2017029.75436 +633 587 -1344116.96715 +637 587 73754.1287475 +638 587 222052.369365 +639 587 9164.38855485 +640 587 -710040.746465 +641 587 -1936474.76309 +642 587 1321205.99576 +588 588 18448886.5844 +589 588 1.95577740669e-8 +590 588 5.21540641785e-8 +591 588 931107.693647 +631 588 -416676.259817 +632 588 -1344116.96715 +633 588 -2296979.40748 +637 588 27106.375452 +638 588 -9164.3885549 +639 588 -5492991.14412 +640 588 484442.198447 +641 588 1321205.99576 +642 588 -2315242.24089 +589 589 2558539.62181 +590 589 6138307.60949 +591 589 -5.21540641785e-8 +592 589 -759692.612592 +593 589 -1651505.67955 +594 589 2.98023223877e-8 +637 589 -260348.273704 +638 589 -710040.746465 +639 589 -484442.198447 +640 589 12227.6197723 +641 589 33038.3525623 +642 589 -41457.2556898 +643 589 -379286.632065 +644 589 -824536.156663 +645 589 588085.337671 +590 590 14915778.6682 +591 590 -1.26659870148e-7 +592 590 -1651505.67955 +593 590 -3590229.73815 +594 590 6.33299350738e-8 +637 590 -710040.746465 +638 590 -1936474.76309 +639 590 -1321205.99576 +640 590 33038.3525623 +641 590 89431.3340663 +642 590 17103.8438088 +643 590 -824536.156663 +644 590 -1792469.90579 +645 590 1278446.38624 +591 591 18669379.7869 +592 591 2.70083546639e-8 +593 591 5.77419996262e-8 +594 591 1124555.53479 +637 591 -484442.198447 +638 591 -1321205.99576 +639 591 -2315242.24089 +640 591 41457.2556898 +641 591 -17103.8438089 +642 591 -5695176.55864 +643 591 588085.337671 +644 591 1278446.38624 +645 591 -2352102.70813 +592 592 3880798.16119 +593 592 7110485.96967 +594 592 -4.09781932831e-8 +595 592 -1076372.38828 +596 592 -1736084.49722 +597 592 2.421438694e-8 +640 592 -379286.632065 +641 592 -824536.156663 +642 592 -588085.337671 +643 592 -52167.0393845 +644 592 -83826.4031561 +645 592 -61373.985991 +646 592 -590912.90871 +647 592 -953085.336629 +648 592 741520.302649 +593 593 13318817.2723 +594 593 -8.19563865662e-8 +595 593 -1736084.49722 +596 593 -2800136.28584 +597 593 3.91155481339e-8 +640 593 -824536.156663 +641 593 -1792469.90579 +642 593 -1278446.38624 +643 593 -83826.4031561 +644 593 -134521.304432 +645 593 32978.3592395 +646 593 -953085.336629 +647 593 -1537234.41392 +648 593 1196000.48814 +594 594 19146980.3477 +595 594 1.67638063431e-8 +596 594 2.98023223877e-8 +597 594 1520483.39551 +640 594 -588085.337671 +641 594 -1278446.38624 +642 594 -2352102.70813 +643 594 61373.985991 +644 594 -32978.3592395 +645 594 -6109264.54972 +646 594 741520.302649 +647 594 1196000.48814 +648 594 -2434642.38116 +595 595 6516876.42304 +596 595 8531914.96891 +597 595 -1.49011611938e-8 +598 595 -2626121.17726 +599 595 -2984228.61052 +600 595 5.58793544769e-9 +643 595 -590912.90871 +644 595 -953085.336629 +645 595 -741520.302649 +646 595 222027.67781 +647 595 227177.812693 +648 595 -75251.5247453 +649 595 -1038306.19785 +650 595 -1179893.40665 +651 595 929649.114512 +596 596 11512089.4984 +597 596 -2.23517417908e-8 +598 596 -2984228.61052 +599 596 -3391168.87559 +600 596 9.31322574615e-9 +643 596 -953085.336629 +644 596 -1537234.41392 +645 596 -1196000.48814 +646 596 227177.812693 +647 596 217630.207531 +648 596 55832.4159336 +649 596 -1179893.40665 +650 596 -1340787.9621 +651 596 1056419.44831 +597 597 18361731.7736 +600 597 -125380.553907 +643 597 -741520.302649 +644 597 -1196000.48814 +645 597 -2434642.38116 +646 597 75251.5247453 +647 597 -55832.4159336 +648 597 -5287984.36193 +649 597 929649.114512 +650 597 1056419.44831 +651 597 -2155790.5645 +598 598 11197028.9194 +599 598 7624235.1212 +600 598 -7.45058059692e-9 +601 598 -1154846.00933 +602 598 -476225.158488 +603 598 1.86264514923e-9 +646 598 -1038306.19785 +647 598 -1179893.40665 +648 598 -929649.114512 +649 598 -908773.635179 +650 598 -175831.894858 +651 598 -148519.569575 +652 598 -1760951.03338 +653 598 -726165.37459 +654 598 1300948.03845 +599 599 6560950.4012 +601 599 -476225.158488 +602 599 -196381.508655 +603 599 9.31322574615e-10 +646 599 -1179893.40665 +647 599 -1340787.9621 +648 599 -1056419.44831 +649 599 -175831.894858 +650 599 153537.59263 +651 599 207978.412157 +652 599 -726165.37459 +653 599 -299449.639006 +654 599 536473.417918 +600 600 21281133.3311 +603 600 4075797.18655 +646 600 -929649.114512 +647 600 -1056419.44831 +648 600 -2155790.5645 +649 600 148519.569575 +650 600 -207978.412157 +651 600 -7295491.64648 +652 600 1300948.03845 +653 600 536473.417918 +654 600 -3164492.7709 +601 601 14087608.2601 +602 601 -7.45058059692e-9 +603 601 -7.45058059692e-9 +604 601 -1154846.00933 +605 601 476225.158488 +606 601 1.86264514923e-9 +649 601 -1760951.03338 +650 601 -726165.37459 +651 601 -1300948.03845 +652 601 -2367056.05396 +653 601 1.11758708954e-8 +654 601 1.86264514923e-9 +655 601 -1760951.03338 +656 601 726165.37459 +657 601 1300948.03845 +602 602 2395597.11087 +603 602 7.45058059692e-9 +604 602 476225.158488 +605 602 -196381.508655 +606 602 -9.31322574615e-10 +649 602 -726165.37459 +650 602 -299449.639006 +651 602 -536473.417918 +652 602 9.31322574615e-9 +653 602 -402517.768767 +654 602 429178.734334 +655 602 726165.37459 +656 602 -299449.639006 +657 602 -536473.417918 +603 603 25315942.1547 +606 603 4075797.18655 +649 603 -1300948.03845 +650 603 -536473.417918 +651 603 -3164492.7709 +653 603 -429178.734334 +654 603 -10404782.7221 +655 603 1300948.03845 +656 603 -536473.417918 +657 603 -3164492.7709 +604 604 11197028.9194 +605 604 -7624235.1212 +606 604 3.72529029846e-9 +607 604 -2626121.17726 +608 604 2984228.61052 +609 604 -5.58793544769e-9 +652 604 -1760951.03338 +653 604 726165.37459 +654 604 -1300948.03845 +655 604 -908773.635179 +656 604 175831.894858 +657 604 148519.569575 +658 604 -1038306.19785 +659 604 1179893.40665 +660 604 929649.114512 +605 605 6560950.4012 +607 605 2984228.61052 +608 605 -3391168.87559 +609 605 5.58793544769e-9 +652 605 726165.37459 +653 605 -299449.639006 +654 605 536473.417918 +655 605 175831.894858 +656 605 153537.59263 +657 605 207978.412157 +658 605 1179893.40665 +659 605 -1340787.9621 +660 605 -1056419.44831 +606 606 21281133.3311 +607 606 -1.30385160446e-8 +608 606 1.30385160446e-8 +609 606 -125380.553907 +652 606 -1300948.03845 +653 606 536473.417918 +654 606 -3164492.7709 +655 606 -148519.569575 +656 606 -207978.412157 +657 606 -7295491.64648 +658 606 929649.114512 +659 606 -1056419.44831 +660 606 -2155790.5645 +607 607 6516876.42304 +608 607 -8531914.96891 +609 607 2.98023223877e-8 +610 607 -1076372.38828 +611 607 1736084.49722 +612 607 -1.49011611938e-8 +655 607 -1038306.19785 +656 607 1179893.40665 +657 607 -929649.114512 +658 607 222027.67781 +659 607 -227177.812693 +660 607 75251.5247453 +661 607 -590912.90871 +662 607 953085.336629 +663 607 741520.302649 +608 608 11512089.4984 +609 608 -3.72529029846e-8 +610 608 1736084.49722 +611 608 -2800136.28584 +612 608 2.421438694e-8 +655 608 1179893.40665 +656 608 -1340787.9621 +657 608 1056419.44831 +658 608 -227177.812693 +659 608 217630.207531 +660 608 55832.4159336 +661 608 953085.336629 +662 608 -1537234.41392 +663 608 -1196000.48814 +609 609 18361731.7736 +610 609 -1.86264514923e-8 +611 609 3.16649675369e-8 +612 609 1520483.39551 +655 609 -929649.114512 +656 609 1056419.44831 +657 609 -2155790.5645 +658 609 -75251.5247453 +659 609 -55832.4159336 +660 609 -5287984.36193 +661 609 741520.302649 +662 609 -1196000.48814 +663 609 -2434642.38116 +610 610 3880798.16119 +611 610 -7110485.96967 +612 610 2.98023223877e-8 +613 610 -759692.612592 +614 610 1651505.67955 +615 610 -1.86264514923e-8 +658 610 -590912.90871 +659 610 953085.336629 +660 610 -741520.302649 +661 610 -52167.0393845 +662 610 83826.4031561 +663 610 61373.985991 +664 610 -379286.632065 +665 610 824536.156663 +666 610 588085.337671 +611 611 13318817.2723 +612 611 -5.21540641785e-8 +613 611 1651505.67955 +614 611 -3590229.73815 +615 611 4.09781932831e-8 +658 611 953085.336629 +659 611 -1537234.41392 +660 611 1196000.48814 +661 611 83826.4031561 +662 611 -134521.304432 +663 611 32978.3592395 +664 611 824536.156663 +665 611 -1792469.90579 +666 611 -1278446.38624 +612 612 19146980.3477 +613 612 -2.51457095146e-8 +614 612 5.40167093277e-8 +615 612 1124555.53479 +658 612 -741520.302649 +659 612 1196000.48814 +660 612 -2434642.38116 +661 612 -61373.985991 +662 612 -32978.3592395 +663 612 -6109264.54972 +664 612 588085.337671 +665 612 -1278446.38624 +666 612 -2352102.70813 +613 613 2558539.62181 +614 613 -6138307.60949 +615 613 4.28408384323e-8 +616 613 -544032.43723 +617 613 1483724.82881 +618 613 -2.32830643654e-8 +661 613 -379286.632065 +662 613 824536.156663 +663 613 -588085.337671 +664 613 12227.6197724 +665 613 -33038.3525623 +666 613 41457.2556898 +667 613 -260348.273704 +668 613 710040.746465 +669 613 484442.198447 +614 614 14915778.6682 +615 614 -1.11758708954e-7 +616 614 1483724.82881 +617 614 -4046522.26039 +618 614 6.70552253723e-8 +661 614 824536.156663 +662 614 -1792469.90579 +663 614 1278446.38624 +664 614 -33038.3525623 +665 614 89431.3340664 +666 614 17103.8438088 +667 614 710040.746465 +668 614 -1936474.76309 +669 614 -1321205.99576 +615 615 18669379.7869 +616 615 -2.60770320892e-8 +617 615 7.45058059692e-8 +618 615 931107.693647 +661 615 -588085.337671 +662 615 1278446.38624 +663 615 -2352102.70813 +664 615 -41457.2556898 +665 615 -17103.8438089 +666 615 -5695176.55864 +667 615 484442.198447 +668 615 -1321205.99576 +669 615 -2315242.24089 +616 616 1816739.3315 +617 616 -5341279.87864 +618 616 3.53902578354e-8 +619 616 -413671.243673 +620 616 1334423.36669 +621 616 -6.51925802231e-9 +664 616 -260348.273704 +665 616 710040.746465 +666 616 -484442.198447 +667 616 24667.0078013 +668 616 -73754.1287475 +669 616 27106.375452 +670 616 -193836.559394 +671 616 625279.223852 +672 616 416676.259817 +617 617 15814018.062 +618 617 -1.04308128357e-7 +619 617 1334423.36669 +620 617 -4304591.50545 +621 617 2.60770320892e-8 +664 617 710040.746465 +665 617 -1936474.76309 +666 617 1321205.99576 +667 617 -73754.1287475 +668 617 222052.369366 +669 617 9164.38855486 +670 617 625279.223852 +671 617 -2017029.75436 +672 617 -1344116.96715 +618 618 18448886.5844 +619 618 -1.11758708954e-8 +620 618 3.35276126862e-8 +621 618 830431.306934 +664 618 -484442.198447 +665 618 1321205.99576 +666 618 -2315242.24089 +667 618 -27106.375452 +668 618 -9164.38855491 +669 618 -5492991.14412 +670 618 416676.259817 +671 618 -1344116.96715 +672 618 -2296979.40748 +619 619 2350262.78049 +620 619 -4600302.86075 +621 619 1.86264514923e-8 +622 619 -7.45058059692e-9 +625 619 -311975.890718 +626 619 1184718.57235 +627 619 -1.11758708954e-8 +667 619 -193836.559394 +668 619 625279.223852 +669 619 -416676.259817 +670 619 -474421.1715 +671 619 -131724.429407 +672 619 23329.9193232 +673 619 -800250.281991 +674 619 8988445.57462 +676 619 -144050.092155 +677 619 547025.666412 +678 619 358351.461509 +620 620 17048556.4258 +621 620 -6.70552253723e-8 +622 620 5.96046447754e-8 +623 620 7.45058059692e-9 +625 620 1184718.57235 +626 620 -4498931.28739 +627 620 3.72529029846e-8 +667 620 625279.223852 +668 620 -2017029.75436 +669 620 1344116.96715 +670 620 -131724.429407 +671 620 -28174.4048383 +672 620 6684.54707695 +673 620 -6040681.08601 +674 620 800250.281991 +676 620 547025.666412 +677 620 -2077312.65726 +678 620 -1360828.33484 +621 621 36176122.0894 +625 621 -1.30385160446e-8 +626 621 4.47034835815e-8 +627 621 758227.34113 +667 621 -416676.259817 +668 621 1344116.96715 +669 621 -2296979.40748 +670 621 -23329.9193232 +671 621 -6684.54707699 +672 621 -14301137.6219 +676 621 358351.461509 +677 621 -1360828.33484 +678 621 -2284273.33935 +622 622 305878011.196 +623 622 -66093576.1502 +670 622 800250.281991 +671 622 6040681.08601 +673 622 64525513.4986 +674 622 4237777.92342 +623 623 549337215.254 +670 623 -8988445.57462 +671 623 -800250.281991 +673 623 4237777.92342 +674 623 48915433.0595 +624 624 715555.555556 +675 624 -357777.777778 +625 625 1011851.60912 +626 625 -4110093.43272 +627 625 6.14672899246e-8 +670 625 -144050.092155 +671 625 547025.666412 +672 625 -358351.461509 +676 625 22134.9913963 +677 625 -90320.994053 +678 625 18909.0650259 +626 626 16788621.6609 +627 626 -2.53319740295e-7 +670 626 547025.666412 +671 626 -2077312.65726 +672 626 1360828.33484 +676 626 -90320.994053 +677 626 370626.696215 +678 626 4631.25243446 +627 627 18240145.4814 +670 627 -358351.461509 +671 627 1360828.33484 +672 627 -2284273.33935 +676 627 -18909.0650259 +677 627 -4631.25243459 +678 627 -5293544.01962 +628 628 983097.517609 +629 628 3993191.05725 +630 628 -3.91155481339e-8 +631 628 -268071.177508 +632 628 -1017991.81332 +633 628 1.30385160446e-8 +629 629 16310675.5472 +630 629 -1.63912773132e-7 +631 629 -1017991.81332 +632 629 -3865791.69616 +633 629 4.47034835815e-8 +630 630 19175351.0344 +631 630 5.58793544769e-9 +632 630 1.86264514923e-8 +633 630 1324183.34296 +631 631 2528206.57902 +632 631 4472508.77878 +633 631 -1.49011611938e-8 +634 631 -161794.959894 +635 631 1643725.47145 +637 631 -354732.167298 +638 631 -1144297.31386 +639 631 1.49011611938e-8 +679 631 -183325.791023 +680 631 -591373.519429 +681 631 416676.259817 +632 632 16817023.5501 +633 632 -5.21540641785e-8 +634 632 -2239699.42799 +635 632 161794.959894 +637 632 -1144297.31386 +638 632 -3691281.65763 +639 632 4.47034835815e-8 +679 632 -591373.519429 +680 632 -1907656.51429 +681 632 1344116.96715 +633 633 39001825.1235 +637 633 1.210719347e-8 +638 633 3.35276126862e-8 +639 633 1397983.15475 +679 633 416676.259817 +680 633 1344116.96715 +681 633 -2538358.87127 +634 634 329627273.449 +635 634 65098710.708 +635 635 569419268.28 +636 636 790573.476703 +637 637 1768961.88645 +638 637 5200497.8108 +639 637 -4.28408384323e-8 +640 637 -465133.879389 +641 637 -1268546.94379 +642 637 2.421438694e-8 +679 637 -89281.9039061 +680 637 -261158.775213 +681 637 -27106.375452 +682 637 -246970.319543 +683 637 -673555.416935 +684 637 484442.198447 +638 638 15396260.6937 +639 638 -1.34110450745e-7 +640 638 -1268546.94379 +641 638 -3459673.48306 +642 638 7.07805156708e-8 +679 638 -261158.775213 +680 638 -769227.571739 +681 638 9164.38855484 +682 638 -673555.416935 +683 638 -1836969.31891 +684 638 1321205.99576 +639 639 19430253.1852 +640 639 1.58324837685e-8 +641 639 3.72529029846e-8 +642 639 1501024.18544 +679 639 27106.375452 +680 639 -9164.38855489 +681 639 -7121142.78387 +682 639 484442.198447 +683 639 1321205.99576 +684 639 -2564546.07772 +640 640 2497002.01247 +641 640 5989724.5569 +642 640 -4.842877388e-8 +643 640 -645500.420471 +644 640 -1403261.78363 +645 640 3.16649675369e-8 +679 640 -246970.319543 +680 640 -673555.416935 +681 640 -484442.198447 +682 640 -150094.32553 +683 640 -356091.902114 +684 640 -41457.2556898 +685 640 -361895.781536 +686 640 -786729.959862 +687 640 588085.337671 +641 641 14552393.0113 +642 641 -1.04308128357e-7 +643 641 -1403261.78363 +644 641 -3050569.09485 +645 641 6.70552253723e-8 +679 641 -673555.416935 +680 641 -1836969.31891 +681 641 -1321205.99576 +682 641 -356091.902114 +683 641 -855385.258208 +684 641 17103.8438088 +685 641 -786729.959862 +686 641 -1710282.52144 +687 641 1278446.38624 +642 642 19697338.3132 +643 642 2.51457095146e-8 +644 642 5.40167093277e-8 +645 642 1699467.28443 +679 642 -484442.198447 +680 642 -1321205.99576 +681 642 -2564546.07772 +682 642 41457.2556898 +683 642 -17103.8438089 +684 642 -7353984.06298 +685 642 588085.337671 +686 642 1278446.38624 +687 642 -2616778.13471 +643 643 3805513.9161 +644 643 6969546.89216 +645 643 -4.09781932831e-8 +646 643 -900913.14915 +647 643 -1453085.72444 +648 643 2.23517417908e-8 +682 643 -361895.781536 +683 643 -786729.959862 +684 643 -588085.337671 +685 643 -304176.348108 +686 643 -544599.533137 +687 643 -61373.985991 +688 643 -570661.636677 +689 643 -920421.994641 +690 643 741520.302649 +644 644 13049076.8844 +645 644 -8.19563865662e-8 +646 644 -1453085.72444 +647 644 -2343686.65232 +648 644 3.72529029846e-8 +682 644 -786729.959862 +683 644 -1710282.52144 +684 644 -1278446.38624 +685 644 -544599.533137 +686 644 -995761.387375 +687 644 32978.3592395 +688 644 -920421.994641 +689 644 -1484551.60426 +690 644 1196000.48814 +645 645 20269767.9526 +646 645 1.67638063431e-8 +647 645 2.98023223877e-8 +648 645 2107431.22341 +682 645 -588085.337671 +683 645 -1278446.38624 +684 645 -2616778.13471 +685 645 61373.985991 +686 645 -32978.3592395 +687 645 -7832517.92941 +688 645 741520.302649 +689 645 1196000.48814 +690 645 -2731360.75727 +646 646 6311393.43185 +647 646 8279110.50413 +648 646 -2.60770320892e-8 +649 646 -2301041.15788 +650 646 -2614819.49759 +651 646 1.86264514923e-8 +685 646 -570661.636677 +686 646 -920421.994641 +687 646 -741520.302649 +688 646 -175770.085153 +689 646 -298827.840692 +690 646 -75251.5247453 +691 646 -955815.974241 +692 646 -1086154.51618 +693 646 929649.114512 +647 647 11193680.9464 +648 647 -3.72529029846e-8 +649 647 -2614819.49759 +650 647 -2971385.79271 +651 647 2.421438694e-8 +685 647 -920421.994641 +686 647 -1484551.60426 +687 647 -1196000.48814 +688 647 -298827.840692 +689 647 -499398.232929 +690 647 55832.4159336 +691 647 -1086154.51618 +692 647 -1234266.49566 +693 647 1056419.44831 +648 648 19292741.7183 +649 648 1.11758708954e-8 +650 648 1.30385160446e-8 +651 648 427942.721782 +685 648 -741520.302649 +686 648 -1196000.48814 +687 648 -2731360.75727 +688 648 75251.5247453 +689 648 -55832.4159336 +690 648 -6893760.43768 +691 648 929649.114512 +692 648 1056419.44831 +693 648 -2324577.161 +649 649 11173430.2083 +650 649 7495059.08719 +651 649 -3.72529029846e-8 +652 649 -678836.974673 +653 649 -279932.773061 +654 649 9.31322574615e-9 +688 649 -955815.974241 +689 649 -1086154.51618 +690 649 -929649.114512 +691 649 -1698063.33368 +692 649 -676945.37624 +693 649 -148519.569575 +694 649 -1831641.90145 +695 649 -755316.248019 +696 649 1300948.03845 +650 650 6371949.42588 +651 650 -7.45058059692e-9 +652 650 -279932.773061 +653 650 -115436.195077 +654 650 2.79396772385e-9 +688 650 -1086154.51618 +689 650 -1234266.49566 +690 650 -1056419.44831 +691 650 -676945.37624 +692 650 -252690.316209 +693 650 207978.412157 +694 650 -755316.248019 +695 650 -311470.61774 +696 650 536473.417918 +651 651 22658086.7563 +652 651 -1.86264514923e-9 +654 651 4790763.62708 +688 651 -929649.114512 +689 651 -1056419.44831 +690 651 -2324577.161 +691 651 148519.569575 +692 651 -207978.412157 +693 651 -9252258.07493 +694 651 1300948.03845 +695 651 536473.417918 +696 651 -3684182.88731 +652 652 14370371.7322 +654 652 -7.45058059692e-9 +655 652 -678836.974673 +656 652 279932.773061 +657 652 9.31322574615e-9 +691 652 -1831641.90145 +692 652 -755316.248019 +693 652 -1300948.03845 +694 652 -3460455.85927 +695 652 1.11758708954e-8 +697 652 -1831641.90145 +698 652 755316.248019 +699 652 1300948.03845 +653 653 2443681.02578 +654 653 7.45058059692e-9 +655 653 279932.773061 +656 653 -115436.195077 +657 653 -2.79396772385e-9 +691 653 -755316.248019 +692 653 -311470.61774 +693 653 -536473.417918 +694 653 1.11758708954e-8 +695 653 -588450.353367 +696 653 429178.734334 +697 653 755316.248019 +698 653 -311470.61774 +699 653 -536473.417918 +654 654 27394702.6193 +655 654 -1.86264514923e-9 +657 654 4790763.62708 +691 654 -1300948.03845 +692 654 -536473.417918 +693 654 -3684182.88731 +694 654 3.72529029846e-9 +695 654 -429178.734334 +696 654 -12874095.835 +697 654 1300948.03845 +698 654 -536473.417918 +699 654 -3684182.88731 +655 655 11173430.2083 +656 655 -7495059.08719 +657 655 1.49011611938e-8 +658 655 -2301041.15788 +659 655 2614819.49759 +660 655 -7.45058059692e-9 +694 655 -1831641.90145 +695 655 755316.248019 +696 655 -1300948.03845 +697 655 -1698063.33368 +698 655 676945.37624 +699 655 148519.569575 +700 655 -955815.974241 +701 655 1086154.51618 +702 655 929649.114512 +656 656 6371949.42588 +657 656 7.45058059692e-9 +658 656 2614819.49759 +659 656 -2971385.79271 +660 656 7.45058059692e-9 +694 656 755316.248019 +695 656 -311470.61774 +696 656 536473.417918 +697 656 676945.37624 +698 656 -252690.316209 +699 656 207978.412157 +700 656 1086154.51618 +701 656 -1234266.49566 +702 656 -1056419.44831 +657 657 22658086.7563 +658 657 -1.67638063431e-8 +659 657 1.86264514923e-8 +660 657 427942.721782 +694 657 -1300948.03845 +695 657 536473.417918 +696 657 -3684182.88731 +697 657 -148519.569575 +698 657 -207978.412157 +699 657 -9252258.07493 +700 657 929649.114512 +701 657 -1056419.44831 +702 657 -2324577.161 +658 658 6311393.43185 +659 658 -8279110.50413 +660 658 3.35276126862e-8 +661 658 -900913.14915 +662 658 1453085.72444 +663 658 -1.67638063431e-8 +697 658 -955815.974241 +698 658 1086154.51618 +699 658 -929649.114512 +700 658 -175770.085153 +701 658 298827.840692 +702 658 75251.5247453 +703 658 -570661.636677 +704 658 920421.994641 +705 658 741520.302649 +659 659 11193680.9464 +660 659 -4.47034835815e-8 +661 659 1453085.72444 +662 659 -2343686.65232 +663 659 2.60770320892e-8 +697 659 1086154.51618 +698 659 -1234266.49566 +699 659 1056419.44831 +700 659 298827.840692 +701 659 -499398.232929 +702 659 55832.4159336 +703 659 920421.994641 +704 659 -1484551.60426 +705 659 -1196000.48814 +660 660 19292741.7183 +661 660 -1.67638063431e-8 +662 660 2.79396772385e-8 +663 660 2107431.22341 +697 660 -929649.114512 +698 660 1056419.44831 +699 660 -2324577.161 +700 660 -75251.5247453 +701 660 -55832.4159336 +702 660 -6893760.43768 +703 660 741520.302649 +704 660 -1196000.48814 +705 660 -2731360.75727 +661 661 3805513.9161 +662 661 -6969546.89216 +663 661 2.60770320892e-8 +664 661 -645500.420471 +665 661 1403261.78363 +666 661 -1.86264514923e-8 +700 661 -570661.636677 +701 661 920421.994641 +702 661 -741520.302649 +703 661 -304176.348108 +704 661 544599.533137 +705 661 61373.985991 +706 661 -361895.781536 +707 661 786729.959862 +708 661 588085.337671 +662 662 13049076.8844 +663 662 -5.21540641785e-8 +664 662 1403261.78363 +665 662 -3050569.09485 +666 662 4.09781932831e-8 +700 662 920421.994641 +701 662 -1484551.60426 +702 662 1196000.48814 +703 662 544599.533137 +704 662 -995761.387375 +705 662 32978.3592395 +706 662 786729.959862 +707 662 -1710282.52144 +708 662 -1278446.38624 +663 663 20269767.9526 +664 663 -2.70083546639e-8 +665 663 5.77419996262e-8 +666 663 1699467.28443 +700 663 -741520.302649 +701 663 1196000.48814 +702 663 -2731360.75727 +703 663 -61373.985991 +704 663 -32978.3592395 +705 663 -7832517.92941 +706 663 588085.337671 +707 663 -1278446.38624 +708 663 -2616778.13471 +664 664 2497002.01247 +665 664 -5989724.5569 +666 664 4.28408384323e-8 +667 664 -465133.879389 +668 664 1268546.94379 +669 664 -2.04890966415e-8 +703 664 -361895.781536 +704 664 786729.959862 +705 664 -588085.337671 +706 664 -150094.32553 +707 664 356091.902114 +708 664 41457.2556898 +709 664 -246970.319543 +710 664 673555.416935 +711 664 484442.198447 +665 665 14552393.0113 +666 665 -9.685754776e-8 +667 665 1268546.94379 +668 665 -3459673.48306 +669 665 5.21540641785e-8 +703 665 786729.959862 +704 665 -1710282.52144 +705 665 1278446.38624 +706 665 356091.902114 +707 665 -855385.258208 +708 665 17103.8438088 +709 665 673555.416935 +710 665 -1836969.31891 +711 665 -1321205.99576 +666 666 19697338.3132 +667 666 -2.60770320892e-8 +668 666 7.82310962677e-8 +669 666 1501024.18544 +703 666 -588085.337671 +704 666 1278446.38624 +705 666 -2616778.13471 +706 666 -41457.2556898 +707 666 -17103.8438089 +708 666 -7353984.06298 +709 666 484442.198447 +710 666 -1321205.99576 +711 666 -2564546.07772 +667 667 1768961.88645 +668 667 -5200497.8108 +669 667 3.72529029846e-8 +670 667 -354732.167298 +671 667 1144297.31386 +672 667 -8.38190317154e-9 +706 667 -246970.319543 +707 667 673555.416935 +708 667 -484442.198447 +709 667 -89281.9039061 +710 667 261158.775213 +711 667 27106.375452 +668 668 15396260.6937 +669 668 -1.11758708954e-7 +670 668 1144297.31386 +671 668 -3691281.65763 +672 668 2.98023223877e-8 +706 668 673555.416935 +707 668 -1836969.31891 +708 668 1321205.99576 +709 668 261158.775213 +710 668 -769227.571739 +711 668 9164.38855485 +669 669 19430253.1852 +670 669 -1.30385160446e-8 +671 669 4.09781932831e-8 +672 669 1397983.15475 +706 669 -484442.198447 +707 669 1321205.99576 +708 669 -2564546.07772 +709 669 -27106.375452 +710 669 -9164.38855489 +711 669 -7121142.78387 +670 670 2528206.97157 +671 670 -4472509.50178 +672 670 1.67638063431e-8 +673 670 161793.620953 +674 670 1643724.74446 +676 670 -268071.177508 +677 670 1017991.81332 +678 670 -1.02445483208e-8 +709 670 -183325.791023 +710 670 591373.519429 +711 670 -416676.259817 +671 671 16817023.1576 +672 671 -5.96046447754e-8 +673 671 -2239700.15498 +674 671 -161793.620953 +676 671 1017991.81332 +677 671 -3865791.69616 +678 671 3.72529029846e-8 +709 671 591373.519429 +710 671 -1907656.51429 +711 671 1344116.96715 +672 672 39001825.1235 +676 672 -1.49011611938e-8 +677 672 5.21540641785e-8 +678 672 1324183.34296 +709 672 -416676.259817 +710 672 1344116.96715 +711 672 -2538358.87127 +673 673 329626980.943 +674 673 -65098171.9808 +674 674 569419560.785 +675 675 790573.476703 +676 676 983097.517609 +677 676 -3993191.05725 +678 676 4.65661287308e-8 +677 677 16310675.5472 +678 677 -1.86264514923e-7 +678 678 19175351.0344 +679 679 1902223.18074 +680 679 5590205.99024 +681 679 -3.53902578354e-8 +682 679 -77037.3094959 +683 679 -210101.753171 +684 679 1.86264514923e-8 +898 679 -718178.246541 +899 679 -2108958.17516 +900 679 -27106.375452 +901 679 -300633.375282 +902 679 -819909.205315 +903 679 484442.198447 +680 680 16543837.5893 +681 680 -1.11758708954e-7 +682 680 -210101.753171 +683 680 -573004.781374 +684 680 4.842877388e-8 +898 680 -2108958.17516 +899 680 -6236511.30779 +900 680 9164.38855486 +901 680 -819909.205315 +902 680 -2236116.0145 +903 680 1321205.99576 +681 681 30501291.4656 +682 681 2.14204192162e-8 +683 681 5.96046447754e-8 +684 681 5508023.65257 +898 681 27106.375452 +899 681 -9164.38855489 +900 681 -19001911.4947 +901 681 484442.198447 +902 681 1321205.99576 +903 681 -5113778.6239 +682 682 2722764.08058 +683 682 6525095.96362 +684 682 -4.47034835815e-8 +685 682 -76937.634873 +686 682 -167255.727985 +687 682 2.421438694e-8 +898 682 -300633.375282 +899 682 -819909.205315 +900 682 -484442.198447 +901 682 -1057312.76972 +902 682 -2529098.59693 +903 682 -41457.2556898 +904 682 -451882.564601 +905 682 -982353.401306 +906 682 588085.337671 +683 683 15837837.4467 +684 683 -1.04308128357e-7 +685 683 -167255.727985 +686 683 -363599.408663 +687 683 5.21540641785e-8 +898 683 -819909.205315 +899 683 -2236116.0145 +900 683 -1321205.99576 +901 683 -2529098.59693 +902 683 -6126929.2712 +903 683 17103.8438088 +904 683 -982353.401306 +905 683 -2135550.8724 +906 683 1278446.38624 +684 684 31131706.3633 +685 684 3.53902578354e-8 +686 684 7.63684511185e-8 +687 684 5787467.22392 +898 684 -484442.198447 +899 684 -1321205.99576 +900 684 -5113778.6239 +901 684 41457.2556898 +902 684 -17103.8438089 +903 684 -19507359.9875 +904 684 588085.337671 +905 684 1278446.38624 +906 684 -5270750.35301 +685 685 4267595.81742 +686 685 7796294.34309 +687 685 -5.21540641785e-8 +688 685 -4790.05038338 +689 685 -7725.88771512 +690 685 2.04890966415e-8 +901 685 -451882.564601 +902 685 -982353.401306 +903 685 -588085.337671 +904 685 -1747893.8743 +905 685 -3178566.02079 +906 685 -61373.985991 +907 685 -749357.926948 +908 685 -1208641.81766 +909 685 741520.302649 +686 686 14559614.5621 +687 686 -9.685754776e-8 +688 686 -7725.88771512 +689 686 -12461.109218 +690 686 2.98023223877e-8 +901 686 -982353.401306 +902 686 -2135550.8724 +903 686 -1278446.38624 +904 686 -3178566.02079 +905 686 -5907985.37222 +906 686 32978.3592395 +907 686 -1208641.81766 +908 686 -1949422.28655 +909 686 1196000.48814 +687 687 32450763.4987 +688 687 1.11758708954e-8 +689 687 2.421438694e-8 +690 687 6373223.13441 +901 687 -588085.337671 +902 687 -1278446.38624 +903 687 -5270750.35301 +904 687 61373.985991 +905 687 -32978.3592395 +906 687 -20553554.1703 +907 687 741520.302649 +908 687 1196000.48814 +909 687 -5606492.51233 +688 688 6563466.92161 +689 688 8716568.29938 +690 688 -2.23517417908e-8 +691 688 -797223.130676 +692 688 -905935.375768 +693 688 1.86264514923e-8 +904 688 -749357.926948 +905 688 -1208641.81766 +906 688 -741520.302649 +907 688 -2303950.19297 +908 688 -3145795.04337 +909 688 -75251.5247453 +910 688 -1005897.92455 +911 688 -1143065.82336 +912 688 929649.114512 +689 689 11934357.6383 +690 689 -3.72529029846e-8 +691 689 -905935.375768 +692 689 -1029472.01792 +693 689 2.04890966415e-8 +904 689 -1208641.81766 +905 689 -1949422.28655 +906 689 -1196000.48814 +907 689 -3145795.04337 +908 689 -4425847.45616 +909 689 55832.4159336 +910 689 -1143065.82336 +911 689 -1298938.43563 +912 689 1056419.44831 +690 690 30008520.3637 +691 690 1.67638063431e-8 +692 690 2.04890966415e-8 +693 690 4063389.39899 +904 690 -741520.302649 +905 690 -1196000.48814 +906 690 -5606492.51233 +907 690 75251.5247453 +908 690 -55832.4159336 +909 690 -18547112.2702 +910 690 929649.114512 +911 690 1056419.44831 +912 690 -4341829.75862 +691 691 13789595.5493 +692 691 8526963.46189 +693 691 -2.23517417908e-8 +694 691 2189391.67462 +695 691 902841.927677 +696 691 3.72529029846e-9 +907 691 -1005897.92455 +908 691 -1143065.82336 +909 691 -929649.114512 +910 691 -6588902.98151 +911 691 -3583442.90451 +912 691 -148519.569575 +913 691 -3101441.97781 +914 691 -1278945.14549 +915 691 1300948.03845 +692 692 6744151.21474 +693 692 -2.23517417908e-8 +694 692 902841.927677 +695 692 372305.949557 +696 692 2.79396772385e-9 +907 692 -1143065.82336 +908 692 -1298938.43563 +909 692 -1056419.44831 +910 692 -3583442.90451 +911 692 -2462219.22114 +912 692 207978.412157 +913 692 -1278945.14549 +914 692 -527400.059996 +915 692 536473.417918 +693 693 37051709.8254 +695 693 -9.31322574615e-10 +696 693 10537577.5004 +907 693 -929649.114512 +908 693 -1056419.44831 +909 693 -4341829.75862 +910 693 148519.569575 +911 693 -207978.412157 +912 693 -23874563.7281 +913 693 1300948.03845 +914 693 536473.417918 +915 693 -8175265.11488 +694 694 19732335.5073 +695 694 -1.49011611938e-8 +696 694 -7.45058059692e-9 +697 694 2189391.67462 +698 694 -902841.927677 +699 694 3.72529029846e-9 +910 694 -3101441.97781 +911 694 -1278945.14549 +912 694 -1300948.03845 +913 694 -10784495.2388 +914 694 1.49011611938e-8 +916 694 -3101441.97781 +917 694 1278945.14549 +918 694 1300948.03845 +695 695 3355482.70929 +697 695 -902841.927677 +698 695 372305.949557 +699 695 -2.79396772385e-9 +910 695 -1278945.14549 +911 695 -527400.059996 +912 695 -536473.417918 +913 695 1.49011611938e-8 +914 695 -1833902.89956 +915 695 429178.734334 +916 695 1278945.14549 +917 695 -527400.059996 +918 695 -536473.417918 +696 696 47437791.9854 +698 696 9.31322574615e-10 +699 696 10537577.5004 +910 696 -1300948.03845 +911 696 -536473.417918 +912 696 -8175265.11488 +913 696 5.58793544769e-9 +914 696 -429178.734334 +915 696 -31919955.1469 +916 696 1300948.03845 +917 696 -536473.417918 +918 696 -8175265.11488 +697 697 13789595.5493 +698 697 -8526963.46189 +699 697 -7.45058059692e-9 +700 697 -797223.130676 +701 697 905935.375768 +702 697 -5.58793544769e-9 +913 697 -3101441.97781 +914 697 1278945.14549 +915 697 -1300948.03845 +916 697 -6588902.98151 +917 697 3583442.90451 +918 697 148519.569575 +919 697 -1005897.92455 +920 697 1143065.82336 +921 697 929649.114512 +698 698 6744151.21474 +699 698 -7.45058059692e-9 +700 698 905935.375768 +701 698 -1029472.01792 +702 698 7.45058059692e-9 +913 698 1278945.14549 +914 698 -527400.059996 +915 698 536473.417918 +916 698 3583442.90451 +917 698 -2462219.22114 +918 698 207978.412157 +919 698 1143065.82336 +920 698 -1298938.43563 +921 698 -1056419.44831 +699 699 37051709.8254 +700 699 -9.31322574615e-9 +701 699 7.45058059692e-9 +702 699 4063389.39899 +913 699 -1300948.03845 +914 699 536473.417918 +915 699 -8175265.11488 +916 699 -148519.569575 +917 699 -207978.412157 +918 699 -23874563.7281 +919 699 929649.114512 +920 699 -1056419.44831 +921 699 -4341829.75862 +700 700 6563466.92161 +701 700 -8716568.29938 +702 700 1.11758708954e-8 +703 700 -4790.05038338 +704 700 7725.88771513 +705 700 -1.49011611938e-8 +916 700 -1005897.92455 +917 700 1143065.82336 +918 700 -929649.114512 +919 700 -2303950.19297 +920 700 3145795.04337 +921 700 75251.5247453 +922 700 -749357.926948 +923 700 1208641.81766 +924 700 741520.302649 +701 701 11934357.6383 +702 701 -1.49011611938e-8 +703 701 7725.88771514 +704 701 -12461.109218 +705 701 2.60770320892e-8 +916 701 1143065.82336 +917 701 -1298938.43563 +918 701 1056419.44831 +919 701 3145795.04337 +920 701 -4425847.45616 +921 701 55832.4159336 +922 701 1208641.81766 +923 701 -1949422.28655 +924 701 -1196000.48814 +702 702 30008520.3637 +703 702 -1.67638063431e-8 +704 702 2.421438694e-8 +705 702 6373223.13441 +916 702 -929649.114512 +917 702 1056419.44831 +918 702 -4341829.75862 +919 702 -75251.5247453 +920 702 -55832.4159336 +921 702 -18547112.2702 +922 702 741520.302649 +923 702 -1196000.48814 +924 702 -5606492.51233 +703 703 4267595.81742 +704 703 -7796294.34309 +705 703 3.72529029846e-8 +706 703 -76937.634873 +707 703 167255.727985 +708 703 -2.14204192162e-8 +919 703 -749357.926948 +920 703 1208641.81766 +921 703 -741520.302649 +922 703 -1747893.8743 +923 703 3178566.02079 +924 703 61373.985991 +925 703 -451882.564601 +926 703 982353.401306 +927 703 588085.337671 +704 704 14559614.5621 +705 704 -7.45058059692e-8 +706 704 167255.727985 +707 704 -363599.408663 +708 704 4.65661287308e-8 +919 704 1208641.81766 +920 704 -1949422.28655 +921 704 1196000.48814 +922 704 3178566.02079 +923 704 -5907985.37222 +924 704 32978.3592395 +925 704 982353.401306 +926 704 -2135550.8724 +927 704 -1278446.38624 +705 705 32450763.4987 +706 705 -1.58324837685e-8 +707 705 3.53902578354e-8 +708 705 5787467.22392 +919 705 -741520.302649 +920 705 1196000.48814 +921 705 -5606492.51233 +922 705 -61373.985991 +923 705 -32978.3592395 +924 705 -20553554.1703 +925 705 588085.337671 +926 705 -1278446.38624 +927 705 -5270750.35301 +706 706 2722764.08058 +707 706 -6525095.96362 +708 706 4.09781932831e-8 +709 706 -77037.3094959 +710 706 210101.753171 +711 706 -2.51457095146e-8 +922 706 -451882.564601 +923 706 982353.401306 +924 706 -588085.337671 +925 706 -1057312.76972 +926 706 2529098.59693 +927 706 41457.2556898 +928 706 -300633.375282 +929 706 819909.205315 +930 706 484442.198447 +707 707 15837837.4467 +708 707 -1.04308128357e-7 +709 707 210101.753171 +710 707 -573004.781374 +711 707 7.45058059692e-8 +922 707 982353.401306 +923 707 -2135550.8724 +924 707 1278446.38624 +925 707 2529098.59693 +926 707 -6126929.2712 +927 707 17103.8438089 +928 707 819909.205315 +929 707 -2236116.0145 +930 707 -1321205.99576 +708 708 31131706.3633 +709 708 -2.23517417908e-8 +710 708 5.96046447754e-8 +711 708 5508023.65257 +922 708 -588085.337671 +923 708 1278446.38624 +924 708 -5270750.35301 +925 708 -41457.2556898 +926 708 -17103.8438089 +927 708 -19507359.9875 +928 708 484442.198447 +929 708 -1321205.99576 +930 708 -5113778.6239 +709 709 1902223.18074 +710 709 -5590205.99024 +711 709 2.421438694e-8 +925 709 -300633.375282 +926 709 819909.205315 +927 709 -484442.198447 +928 709 -718178.246541 +929 709 2108958.17516 +930 709 27106.375452 +710 710 16543837.5893 +711 710 -7.45058059692e-8 +925 710 819909.205315 +926 710 -2236116.0145 +927 710 1321205.99576 +928 710 2108958.17516 +929 710 -6236511.30779 +930 710 9164.38855486 +711 711 30501291.4656 +925 711 -484442.198447 +926 711 1321205.99576 +927 711 -5113778.6239 +928 711 -27106.375452 +929 711 -9164.38855491 +930 711 -19001911.4947 +712 712 1011851.60912 +713 712 4110093.43272 +714 712 -4.842877388e-8 +715 712 -311975.890718 +716 712 -1184718.57235 +717 712 1.02445483208e-8 +763 712 22134.9913963 +764 712 90320.994053 +765 712 -18909.0650259 +766 712 -144050.092155 +767 712 -547025.666412 +768 712 358351.461509 +1354 712 22134.9913963 +1355 712 90320.994053 +1356 712 18909.0650259 +1357 712 -144050.092155 +1358 712 -547025.666412 +1359 712 -358351.461509 +713 713 16788621.6609 +714 713 -2.08616256714e-7 +715 713 -1184718.57235 +716 713 -4498931.28739 +717 713 3.35276126862e-8 +763 713 90320.994053 +764 713 370626.696215 +765 713 4631.25243448 +766 713 -547025.666412 +767 713 -2077312.65726 +768 713 1360828.33484 +1354 713 90320.994053 +1355 713 370626.696215 +1356 713 -4631.25243456 +1357 713 -547025.666412 +1358 713 -2077312.65726 +1359 713 -1360828.33484 +714 714 18240145.4814 +715 714 1.11758708954e-8 +716 714 3.72529029846e-8 +717 714 758227.34113 +763 714 18909.0650259 +764 714 -4631.25243456 +765 714 -5293544.01962 +766 714 358351.461509 +767 714 1360828.33484 +768 714 -2284273.33935 +1354 714 -18909.0650259 +1355 714 4631.25243448 +1356 714 -5293544.01962 +1357 714 -358351.461509 +1358 714 -1360828.33484 +1359 714 -2284273.33935 +715 715 2350262.38096 +716 715 4600302.12491 +717 715 -2.60770320892e-8 +718 715 2.98023223877e-8 +719 715 -1.19209289551e-7 +721 715 -413671.243673 +722 715 -1334423.36669 +723 715 1.58324837685e-8 +763 715 -144050.092155 +764 715 -547025.666412 +765 715 -358351.461509 +766 715 -474420.971735 +767 715 131724.797328 +768 715 -23329.9193232 +769 715 800256.904552 +770 715 8988441.97885 +772 715 -193836.559394 +773 715 -625279.223852 +774 715 416676.259817 +1354 715 -144050.092155 +1355 715 -547025.666412 +1356 715 358351.461509 +1357 715 -474420.971735 +1358 715 131724.797328 +1359 715 23329.9193232 +1360 715 -800256.904552 +1361 715 -8988441.97885 +1363 715 -193836.559394 +1364 715 -625279.223852 +1365 715 -416676.259817 +716 716 17048556.8253 +717 716 -8.94069671631e-8 +718 716 5.96046447754e-8 +721 716 -1334423.36669 +722 716 -4304591.50545 +723 716 4.842877388e-8 +763 716 -547025.666412 +764 716 -2077312.65726 +765 716 -1360828.33484 +766 716 131724.797328 +767 716 -28174.604603 +768 716 6684.54707694 +769 716 -6040684.68178 +770 716 -800256.904552 +772 716 -625279.223852 +773 716 -2017029.75436 +774 716 1344116.96715 +1354 716 -547025.666412 +1355 716 -2077312.65726 +1356 716 1360828.33484 +1357 716 131724.797328 +1358 716 -28174.604603 +1359 716 -6684.54707697 +1360 716 6040684.68178 +1361 716 800256.904552 +1363 716 -625279.223852 +1364 716 -2017029.75436 +1365 716 -1344116.96715 +717 717 36176122.0894 +721 717 1.39698386192e-8 +722 717 4.09781932831e-8 +723 717 830431.306934 +763 717 -358351.461509 +764 717 -1360828.33484 +765 717 -2284273.33935 +766 717 23329.9193232 +767 717 -6684.54707697 +768 717 -14301137.6219 +772 717 416676.259817 +773 717 1344116.96715 +774 717 -2296979.40748 +1354 717 358351.461509 +1355 717 1360828.33484 +1356 717 -2284273.33935 +1357 717 -23329.9193232 +1358 717 6684.54707694 +1359 717 -14301137.6219 +1363 717 -416676.259817 +1364 717 -1344116.96715 +1365 717 -2296979.40748 +718 718 305878308.174 +719 718 66094123.115 +766 718 -800256.904552 +767 718 6040684.68178 +769 718 64525494.457 +770 718 -4237812.99363 +1357 718 800256.904552 +1358 718 -6040684.68178 +1360 718 64525494.457 +1361 718 -4237812.99363 +719 719 549336918.275 +766 719 -8988441.97885 +767 719 800256.904552 +769 719 -4237812.99363 +770 719 48915452.1011 +1357 719 8988441.97885 +1358 719 -800256.904552 +1360 719 -4237812.99363 +1361 719 48915452.1011 +720 720 715555.555556 +771 720 -357777.777778 +1362 720 -357777.777778 +721 721 1816739.3315 +722 721 5341279.87864 +723 721 -3.53902578354e-8 +724 721 -544032.43723 +725 721 -1483724.82881 +726 721 2.04890966415e-8 +766 721 -193836.559394 +767 721 -625279.223852 +768 721 -416676.259817 +772 721 24667.0078013 +773 721 73754.1287476 +774 721 -27106.375452 +775 721 -260348.273704 +776 721 -710040.746465 +777 721 484442.198447 +1357 721 -193836.559394 +1358 721 -625279.223852 +1359 721 416676.259817 +1363 721 24667.0078013 +1364 721 73754.1287476 +1365 721 27106.375452 +1366 721 -260348.273704 +1367 721 -710040.746465 +1368 721 -484442.198447 +722 722 15814018.062 +723 722 -1.11758708954e-7 +724 722 -1483724.82881 +725 722 -4046522.26039 +726 722 5.58793544769e-8 +766 722 -625279.223852 +767 722 -2017029.75436 +768 722 -1344116.96715 +772 722 73754.1287476 +773 722 222052.369366 +774 722 9164.38855485 +775 722 -710040.746465 +776 722 -1936474.76309 +777 722 1321205.99576 +1357 722 -625279.223852 +1358 722 -2017029.75436 +1359 722 1344116.96715 +1363 722 73754.1287476 +1364 722 222052.369366 +1365 722 -9164.3885549 +1366 722 -710040.746465 +1367 722 -1936474.76309 +1368 722 -1321205.99576 +723 723 18448886.5844 +724 723 2.04890966415e-8 +725 723 5.58793544769e-8 +726 723 931107.693646 +766 723 -416676.259817 +767 723 -1344116.96715 +768 723 -2296979.40748 +772 723 27106.375452 +773 723 -9164.3885549 +774 723 -5492991.14412 +775 723 484442.198447 +776 723 1321205.99576 +777 723 -2315242.24089 +1357 723 416676.259817 +1358 723 1344116.96715 +1359 723 -2296979.40748 +1363 723 -27106.375452 +1364 723 9164.38855485 +1365 723 -5492991.14412 +1366 723 -484442.198447 +1367 723 -1321205.99576 +1368 723 -2315242.24089 +724 724 2558539.62181 +725 724 6138307.60949 +726 724 -5.58793544769e-8 +727 724 -759692.612592 +728 724 -1651505.67955 +729 724 2.98023223877e-8 +772 724 -260348.273704 +773 724 -710040.746465 +774 724 -484442.198447 +775 724 12227.6197724 +776 724 33038.3525624 +777 724 -41457.2556898 +778 724 -379286.632065 +779 724 -824536.156663 +780 724 588085.337671 +1363 724 -260348.273704 +1364 724 -710040.746465 +1365 724 484442.198447 +1366 724 12227.6197724 +1367 724 33038.3525624 +1368 724 41457.2556898 +1369 724 -379286.632065 +1370 724 -824536.156663 +1371 724 -588085.337671 +725 725 14915778.6682 +726 725 -1.34110450745e-7 +727 725 -1651505.67955 +728 725 -3590229.73815 +729 725 6.33299350738e-8 +772 725 -710040.746465 +773 725 -1936474.76309 +774 725 -1321205.99576 +775 725 33038.3525624 +776 725 89431.3340664 +777 725 17103.8438088 +778 725 -824536.156663 +779 725 -1792469.90579 +780 725 1278446.38624 +1363 725 -710040.746465 +1364 725 -1936474.76309 +1365 725 1321205.99576 +1366 725 33038.3525624 +1367 725 89431.3340664 +1368 725 -17103.8438089 +1369 725 -824536.156663 +1370 725 -1792469.90579 +1371 725 -1278446.38624 +726 726 18669379.7869 +727 726 2.70083546639e-8 +728 726 5.77419996262e-8 +729 726 1124555.53479 +772 726 -484442.198447 +773 726 -1321205.99576 +774 726 -2315242.24089 +775 726 41457.2556898 +776 726 -17103.8438089 +777 726 -5695176.55864 +778 726 588085.337671 +779 726 1278446.38624 +780 726 -2352102.70813 +1363 726 484442.198447 +1364 726 1321205.99576 +1365 726 -2315242.24089 +1366 726 -41457.2556898 +1367 726 17103.8438088 +1368 726 -5695176.55864 +1369 726 -588085.337671 +1370 726 -1278446.38624 +1371 726 -2352102.70813 +727 727 3880798.16119 +728 727 7110485.96967 +729 727 -4.47034835815e-8 +730 727 -1076372.38828 +731 727 -1736084.49722 +732 727 2.421438694e-8 +775 727 -379286.632065 +776 727 -824536.156663 +777 727 -588085.337671 +778 727 -52167.0393845 +779 727 -83826.4031561 +780 727 -61373.985991 +781 727 -590912.90871 +782 727 -953085.336629 +783 727 741520.302649 +1366 727 -379286.632065 +1367 727 -824536.156663 +1368 727 588085.337671 +1369 727 -52167.0393845 +1370 727 -83826.4031561 +1371 727 61373.985991 +1372 727 -590912.90871 +1373 727 -953085.336629 +1374 727 -741520.302649 +728 728 13318817.2723 +729 728 -8.94069671631e-8 +730 728 -1736084.49722 +731 728 -2800136.28584 +732 728 3.91155481339e-8 +775 728 -824536.156663 +776 728 -1792469.90579 +777 728 -1278446.38624 +778 728 -83826.4031561 +779 728 -134521.304432 +780 728 32978.3592395 +781 728 -953085.336629 +782 728 -1537234.41392 +783 728 1196000.48814 +1366 728 -824536.156663 +1367 728 -1792469.90579 +1368 728 1278446.38624 +1369 728 -83826.4031561 +1370 728 -134521.304432 +1371 728 -32978.3592395 +1372 728 -953085.336629 +1373 728 -1537234.41392 +1374 728 -1196000.48814 +729 729 19146980.3477 +730 729 2.23517417908e-8 +731 729 3.53902578354e-8 +732 729 1520483.39551 +775 729 -588085.337671 +776 729 -1278446.38624 +777 729 -2352102.70813 +778 729 61373.985991 +779 729 -32978.3592395 +780 729 -6109264.54972 +781 729 741520.302649 +782 729 1196000.48814 +783 729 -2434642.38116 +1366 729 588085.337671 +1367 729 1278446.38624 +1368 729 -2352102.70813 +1369 729 -61373.985991 +1370 729 32978.3592395 +1371 729 -6109264.54972 +1372 729 -741520.302649 +1373 729 -1196000.48814 +1374 729 -2434642.38116 +730 730 6516876.42304 +731 730 8531914.96891 +732 730 -2.23517417908e-8 +733 730 -2626121.17726 +734 730 -2984228.61052 +735 730 1.30385160446e-8 +778 730 -590912.90871 +779 730 -953085.336629 +780 730 -741520.302649 +781 730 222027.67781 +782 730 227177.812693 +783 730 -75251.5247453 +784 730 -1038306.19785 +785 730 -1179893.40665 +786 730 929649.114512 +1369 730 -590912.90871 +1370 730 -953085.336629 +1371 730 741520.302649 +1372 730 222027.67781 +1373 730 227177.812693 +1374 730 75251.5247453 +1375 730 -1038306.19785 +1376 730 -1179893.40665 +1377 730 -929649.114512 +731 731 11512089.4984 +732 731 -2.98023223877e-8 +733 731 -2984228.61052 +734 731 -3391168.87559 +735 731 1.30385160446e-8 +778 731 -953085.336629 +779 731 -1537234.41392 +780 731 -1196000.48814 +781 731 227177.812693 +782 731 217630.207531 +783 731 55832.4159336 +784 731 -1179893.40665 +785 731 -1340787.9621 +786 731 1056419.44831 +1369 731 -953085.336629 +1370 731 -1537234.41392 +1371 731 1196000.48814 +1372 731 227177.812693 +1373 731 217630.207531 +1374 731 -55832.4159336 +1375 731 -1179893.40665 +1376 731 -1340787.9621 +1377 731 -1056419.44831 +732 732 18361731.7736 +733 732 3.72529029846e-9 +734 732 5.58793544769e-9 +735 732 -125380.553907 +778 732 -741520.302649 +779 732 -1196000.48814 +780 732 -2434642.38116 +781 732 75251.5247453 +782 732 -55832.4159336 +783 732 -5287984.36193 +784 732 929649.114512 +785 732 1056419.44831 +786 732 -2155790.5645 +1369 732 741520.302649 +1370 732 1196000.48814 +1371 732 -2434642.38116 +1372 732 -75251.5247453 +1373 732 55832.4159336 +1374 732 -5287984.36193 +1375 732 -929649.114512 +1376 732 -1056419.44831 +1377 732 -2155790.5645 +733 733 11197028.9194 +734 733 7624235.1212 +735 733 -7.45058059692e-9 +736 733 -1154846.00933 +737 733 -476225.158488 +738 733 1.86264514923e-9 +781 733 -1038306.19785 +782 733 -1179893.40665 +783 733 -929649.114512 +784 733 -908773.635179 +785 733 -175831.894858 +786 733 -148519.569575 +787 733 -1760951.03338 +788 733 -726165.37459 +789 733 1300948.03845 +1372 733 -1038306.19785 +1373 733 -1179893.40665 +1374 733 929649.114512 +1375 733 -908773.635179 +1376 733 -175831.894858 +1377 733 148519.569575 +1378 733 -1760951.03338 +1379 733 -726165.37459 +1380 733 -1300948.03845 +734 734 6560950.4012 +735 734 -3.72529029846e-9 +736 734 -476225.158488 +737 734 -196381.508655 +738 734 9.31322574615e-10 +781 734 -1179893.40665 +782 734 -1340787.9621 +783 734 -1056419.44831 +784 734 -175831.894858 +785 734 153537.59263 +786 734 207978.412157 +787 734 -726165.37459 +788 734 -299449.639006 +789 734 536473.417918 +1372 734 -1179893.40665 +1373 734 -1340787.9621 +1374 734 1056419.44831 +1375 734 -175831.894858 +1376 734 153537.59263 +1377 734 -207978.412157 +1378 734 -726165.37459 +1379 734 -299449.639006 +1380 734 -536473.417918 +735 735 21281133.3311 +738 735 4075797.18655 +781 735 -929649.114512 +782 735 -1056419.44831 +783 735 -2155790.5645 +784 735 148519.569575 +785 735 -207978.412157 +786 735 -7295491.64648 +787 735 1300948.03845 +788 735 536473.417918 +789 735 -3164492.7709 +1372 735 929649.114512 +1373 735 1056419.44831 +1374 735 -2155790.5645 +1375 735 -148519.569575 +1376 735 207978.412157 +1377 735 -7295491.64648 +1378 735 -1300948.03845 +1379 735 -536473.417918 +1380 735 -3164492.7709 +736 736 14087608.2601 +737 736 -1.49011611938e-8 +738 736 -7.45058059692e-9 +739 736 -1154846.00933 +740 736 476225.158488 +741 736 1.86264514923e-9 +784 736 -1760951.03338 +785 736 -726165.37459 +786 736 -1300948.03845 +787 736 -2367056.05396 +788 736 1.11758708954e-8 +789 736 1.86264514923e-9 +790 736 -1760951.03338 +791 736 726165.37459 +792 736 1300948.03845 +1375 736 -1760951.03338 +1376 736 -726165.37459 +1377 736 1300948.03845 +1378 736 -2367056.05396 +1379 736 9.31322574615e-9 +1381 736 -1760951.03338 +1382 736 726165.37459 +1383 736 -1300948.03845 +737 737 2395597.11087 +738 737 3.72529029846e-9 +739 737 476225.158488 +740 737 -196381.508655 +741 737 -9.31322574615e-10 +784 737 -726165.37459 +785 737 -299449.639006 +786 737 -536473.417918 +787 737 9.31322574615e-9 +788 737 -402517.768767 +789 737 429178.734334 +790 737 726165.37459 +791 737 -299449.639006 +792 737 -536473.417918 +1375 737 -726165.37459 +1376 737 -299449.639006 +1377 737 536473.417918 +1378 737 1.11758708954e-8 +1379 737 -402517.768767 +1380 737 -429178.734334 +1381 737 726165.37459 +1382 737 -299449.639006 +1383 737 536473.417918 +738 738 25315942.1547 +741 738 4075797.18655 +784 738 -1300948.03845 +785 738 -536473.417918 +786 738 -3164492.7709 +788 738 -429178.734334 +789 738 -10404782.7221 +790 738 1300948.03845 +791 738 -536473.417918 +792 738 -3164492.7709 +1375 738 1300948.03845 +1376 738 536473.417918 +1377 738 -3164492.7709 +1378 738 1.86264514923e-9 +1379 738 429178.734334 +1380 738 -10404782.7221 +1381 738 -1300948.03845 +1382 738 536473.417918 +1383 738 -3164492.7709 +739 739 11197028.9194 +740 739 -7624235.1212 +741 739 7.45058059692e-9 +742 739 -2626121.17726 +743 739 2984228.61052 +744 739 -5.58793544769e-9 +787 739 -1760951.03338 +788 739 726165.37459 +789 739 -1300948.03845 +790 739 -908773.635179 +791 739 175831.894858 +792 739 148519.569575 +793 739 -1038306.19785 +794 739 1179893.40665 +795 739 929649.114512 +1378 739 -1760951.03338 +1379 739 726165.37459 +1380 739 1300948.03845 +1381 739 -908773.635179 +1382 739 175831.894858 +1383 739 -148519.569575 +1384 739 -1038306.19785 +1385 739 1179893.40665 +1386 739 -929649.114512 +740 740 6560950.4012 +741 740 -7.45058059692e-9 +742 740 2984228.61052 +743 740 -3391168.87559 +744 740 5.58793544769e-9 +787 740 726165.37459 +788 740 -299449.639006 +789 740 536473.417918 +790 740 175831.894858 +791 740 153537.59263 +792 740 207978.412157 +793 740 1179893.40665 +794 740 -1340787.9621 +795 740 -1056419.44831 +1378 740 726165.37459 +1379 740 -299449.639006 +1380 740 -536473.417918 +1381 740 175831.894858 +1382 740 153537.59263 +1383 740 -207978.412157 +1384 740 1179893.40665 +1385 740 -1340787.9621 +1386 740 1056419.44831 +741 741 21281133.3311 +742 741 -1.11758708954e-8 +743 741 1.11758708954e-8 +744 741 -125380.553907 +787 741 -1300948.03845 +788 741 536473.417918 +789 741 -3164492.7709 +790 741 -148519.569575 +791 741 -207978.412157 +792 741 -7295491.64648 +793 741 929649.114512 +794 741 -1056419.44831 +795 741 -2155790.5645 +1378 741 1300948.03845 +1379 741 -536473.417918 +1380 741 -3164492.7709 +1381 741 148519.569575 +1382 741 207978.412157 +1383 741 -7295491.64648 +1384 741 -929649.114512 +1385 741 1056419.44831 +1386 741 -2155790.5645 +742 742 6516876.42304 +743 742 -8531914.96891 +744 742 2.98023223877e-8 +745 742 -1076372.38828 +746 742 1736084.49722 +747 742 -1.67638063431e-8 +790 742 -1038306.19785 +791 742 1179893.40665 +792 742 -929649.114512 +793 742 222027.67781 +794 742 -227177.812693 +795 742 75251.5247453 +796 742 -590912.90871 +797 742 953085.336629 +798 742 741520.302649 +1381 742 -1038306.19785 +1382 742 1179893.40665 +1383 742 929649.114512 +1384 742 222027.67781 +1385 742 -227177.812693 +1386 742 -75251.5247453 +1387 742 -590912.90871 +1388 742 953085.336629 +1389 742 -741520.302649 +743 743 11512089.4984 +744 743 -3.72529029846e-8 +745 743 1736084.49722 +746 743 -2800136.28584 +747 743 2.60770320892e-8 +790 743 1179893.40665 +791 743 -1340787.9621 +792 743 1056419.44831 +793 743 -227177.812693 +794 743 217630.207531 +795 743 55832.4159336 +796 743 953085.336629 +797 743 -1537234.41392 +798 743 -1196000.48814 +1381 743 1179893.40665 +1382 743 -1340787.9621 +1383 743 -1056419.44831 +1384 743 -227177.812693 +1385 743 217630.207531 +1386 743 -55832.4159336 +1387 743 953085.336629 +1388 743 -1537234.41392 +1389 743 1196000.48814 +744 744 18361731.7736 +745 744 -2.04890966415e-8 +746 744 3.35276126862e-8 +747 744 1520483.39551 +790 744 -929649.114512 +791 744 1056419.44831 +792 744 -2155790.5645 +793 744 -75251.5247453 +794 744 -55832.4159336 +795 744 -5287984.36193 +796 744 741520.302649 +797 744 -1196000.48814 +798 744 -2434642.38116 +1381 744 929649.114512 +1382 744 -1056419.44831 +1383 744 -2155790.5645 +1384 744 75251.5247453 +1385 744 55832.4159336 +1386 744 -5287984.36193 +1387 744 -741520.302649 +1388 744 1196000.48814 +1389 744 -2434642.38116 +745 745 3880798.16119 +746 745 -7110485.96967 +747 745 3.72529029846e-8 +748 745 -759692.612592 +749 745 1651505.67955 +750 745 -1.86264514923e-8 +793 745 -590912.90871 +794 745 953085.336629 +795 745 -741520.302649 +796 745 -52167.0393845 +797 745 83826.4031561 +798 745 61373.985991 +799 745 -379286.632065 +800 745 824536.156663 +801 745 588085.337671 +1384 745 -590912.90871 +1385 745 953085.336629 +1386 745 741520.302649 +1387 745 -52167.0393845 +1388 745 83826.4031561 +1389 745 -61373.985991 +1390 745 -379286.632065 +1391 745 824536.156663 +1392 745 -588085.337671 +746 746 13318817.2723 +747 746 -6.70552253723e-8 +748 746 1651505.67955 +749 746 -3590229.73815 +750 746 4.09781932831e-8 +793 746 953085.336629 +794 746 -1537234.41392 +795 746 1196000.48814 +796 746 83826.4031561 +797 746 -134521.304432 +798 746 32978.3592395 +799 746 824536.156663 +800 746 -1792469.90579 +801 746 -1278446.38624 +1384 746 953085.336629 +1385 746 -1537234.41392 +1386 746 -1196000.48814 +1387 746 83826.4031561 +1388 746 -134521.304432 +1389 746 -32978.3592395 +1390 746 824536.156663 +1391 746 -1792469.90579 +1392 746 1278446.38624 +747 747 19146980.3477 +748 747 -2.51457095146e-8 +749 747 5.40167093277e-8 +750 747 1124555.53479 +793 747 -741520.302649 +794 747 1196000.48814 +795 747 -2434642.38116 +796 747 -61373.985991 +797 747 -32978.3592395 +798 747 -6109264.54972 +799 747 588085.337671 +800 747 -1278446.38624 +801 747 -2352102.70813 +1384 747 741520.302649 +1385 747 -1196000.48814 +1386 747 -2434642.38116 +1387 747 61373.985991 +1388 747 32978.3592395 +1389 747 -6109264.54972 +1390 747 -588085.337671 +1391 747 1278446.38624 +1392 747 -2352102.70813 +748 748 2558539.62181 +749 748 -6138307.60949 +750 748 4.28408384323e-8 +751 748 -544032.43723 +752 748 1483724.82881 +753 748 -2.51457095146e-8 +796 748 -379286.632065 +797 748 824536.156663 +798 748 -588085.337671 +799 748 12227.6197724 +800 748 -33038.3525624 +801 748 41457.2556898 +802 748 -260348.273704 +803 748 710040.746465 +804 748 484442.198447 +1387 748 -379286.632065 +1388 748 824536.156663 +1389 748 588085.337671 +1390 748 12227.6197724 +1391 748 -33038.3525624 +1392 748 -41457.2556898 +1393 748 -260348.273704 +1394 748 710040.746465 +1395 748 -484442.198447 +749 749 14915778.6682 +750 749 -1.19209289551e-7 +751 749 1483724.82881 +752 749 -4046522.26039 +753 749 7.07805156708e-8 +796 749 824536.156663 +797 749 -1792469.90579 +798 749 1278446.38624 +799 749 -33038.3525624 +800 749 89431.3340665 +801 749 17103.8438088 +802 749 710040.746465 +803 749 -1936474.76309 +804 749 -1321205.99576 +1387 749 824536.156663 +1388 749 -1792469.90579 +1389 749 -1278446.38624 +1390 749 -33038.3525624 +1391 749 89431.3340665 +1392 749 -17103.8438089 +1393 749 710040.746465 +1394 749 -1936474.76309 +1395 749 1321205.99576 +750 750 18669379.7869 +751 750 -2.79396772385e-8 +752 750 7.82310962677e-8 +753 750 931107.693646 +796 750 -588085.337671 +797 750 1278446.38624 +798 750 -2352102.70813 +799 750 -41457.2556898 +800 750 -17103.8438089 +801 750 -5695176.55864 +802 750 484442.198447 +803 750 -1321205.99576 +804 750 -2315242.24089 +1387 750 588085.337671 +1388 750 -1278446.38624 +1389 750 -2352102.70813 +1390 750 41457.2556898 +1391 750 17103.8438088 +1392 750 -5695176.55864 +1393 750 -484442.198447 +1394 750 1321205.99576 +1395 750 -2315242.24089 +751 751 1816739.3315 +752 751 -5341279.87864 +753 751 3.72529029846e-8 +754 751 -413671.243673 +755 751 1334423.36669 +756 751 -8.38190317154e-9 +799 751 -260348.273704 +800 751 710040.746465 +801 751 -484442.198447 +802 751 24667.0078013 +803 751 -73754.1287476 +804 751 27106.375452 +805 751 -193836.559394 +806 751 625279.223852 +807 751 416676.259817 +1390 751 -260348.273704 +1391 751 710040.746465 +1392 751 484442.198447 +1393 751 24667.0078013 +1394 751 -73754.1287476 +1395 751 -27106.375452 +1396 751 -193836.559394 +1397 751 625279.223852 +1398 751 -416676.259817 +752 752 15814018.062 +753 752 -1.11758708954e-7 +754 752 1334423.36669 +755 752 -4304591.50545 +756 752 2.98023223877e-8 +799 752 710040.746465 +800 752 -1936474.76309 +801 752 1321205.99576 +802 752 -73754.1287476 +803 752 222052.369366 +804 752 9164.38855486 +805 752 625279.223852 +806 752 -2017029.75436 +807 752 -1344116.96715 +1390 752 710040.746465 +1391 752 -1936474.76309 +1392 752 -1321205.99576 +1393 752 -73754.1287476 +1394 752 222052.369366 +1395 752 -9164.38855491 +1396 752 625279.223852 +1397 752 -2017029.75436 +1398 752 1344116.96715 +753 753 18448886.5844 +754 753 -1.11758708954e-8 +755 753 3.72529029846e-8 +756 753 830431.306934 +799 753 -484442.198447 +800 753 1321205.99576 +801 753 -2315242.24089 +802 753 -27106.375452 +803 753 -9164.38855491 +804 753 -5492991.14412 +805 753 416676.259817 +806 753 -1344116.96715 +807 753 -2296979.40748 +1390 753 484442.198447 +1391 753 -1321205.99576 +1392 753 -2315242.24089 +1393 753 27106.375452 +1394 753 9164.38855486 +1395 753 -5492991.14412 +1396 753 -416676.259817 +1397 753 1344116.96715 +1398 753 -2296979.40748 +754 754 2350262.78049 +755 754 -4600302.86075 +756 754 2.04890966415e-8 +757 754 -7.45058059692e-9 +760 754 -311975.890718 +761 754 1184718.57235 +762 754 -1.02445483208e-8 +802 754 -193836.559394 +803 754 625279.223852 +804 754 -416676.259817 +805 754 -474421.1715 +806 754 -131724.429407 +807 754 23329.9193232 +808 754 -800250.281991 +809 754 8988445.57462 +811 754 -144050.092155 +812 754 547025.666412 +813 754 358351.461509 +1393 754 -193836.559394 +1394 754 625279.223852 +1395 754 416676.259817 +1396 754 -474421.1715 +1397 754 -131724.429407 +1398 754 -23329.9193232 +1399 754 800250.281991 +1400 754 -8988445.57462 +1402 754 -144050.092155 +1403 754 547025.666412 +1404 754 -358351.461509 +755 755 17048556.4258 +756 755 -7.45058059692e-8 +757 755 5.96046447754e-8 +758 755 7.45058059692e-9 +760 755 1184718.57235 +761 755 -4498931.28739 +762 755 3.35276126862e-8 +802 755 625279.223852 +803 755 -2017029.75436 +804 755 1344116.96715 +805 755 -131724.429407 +806 755 -28174.4048381 +807 755 6684.54707694 +808 755 -6040681.08601 +809 755 800250.281991 +811 755 547025.666412 +812 755 -2077312.65726 +813 755 -1360828.33484 +1393 755 625279.223852 +1394 755 -2017029.75436 +1395 755 -1344116.96715 +1396 755 -131724.429407 +1397 755 -28174.4048381 +1398 755 -6684.54707698 +1399 755 6040681.08601 +1400 755 -800250.281991 +1402 755 547025.666412 +1403 755 -2077312.65726 +1404 755 1360828.33484 +756 756 36176122.0894 +760 756 -1.30385160446e-8 +761 756 4.47034835815e-8 +762 756 758227.341129 +802 756 -416676.259817 +803 756 1344116.96715 +804 756 -2296979.40748 +805 756 -23329.9193232 +806 756 -6684.54707698 +807 756 -14301137.6219 +811 756 358351.461509 +812 756 -1360828.33484 +813 756 -2284273.33935 +1393 756 416676.259817 +1394 756 -1344116.96715 +1395 756 -2296979.40748 +1396 756 23329.9193232 +1397 756 6684.54707694 +1398 756 -14301137.6219 +1402 756 -358351.461509 +1403 756 1360828.33484 +1404 756 -2284273.33935 +757 757 305878011.196 +758 757 -66093576.1502 +805 757 800250.281991 +806 757 6040681.08601 +808 757 64525513.4986 +809 757 4237777.92342 +1396 757 -800250.281991 +1397 757 -6040681.08601 +1399 757 64525513.4986 +1400 757 4237777.92342 +758 758 549337215.254 +805 758 -8988445.57462 +806 758 -800250.281991 +808 758 4237777.92342 +809 758 48915433.0595 +1396 758 8988445.57462 +1397 758 800250.281991 +1399 758 4237777.92342 +1400 758 48915433.0595 +759 759 715555.555556 +810 759 -357777.777778 +1401 759 -357777.777778 +760 760 1011851.60912 +761 760 -4110093.43272 +762 760 6.14672899246e-8 +805 760 -144050.092155 +806 760 547025.666412 +807 760 -358351.461509 +811 760 22134.9913963 +812 760 -90320.994053 +813 760 18909.0650259 +1396 760 -144050.092155 +1397 760 547025.666412 +1398 760 358351.461509 +1402 760 22134.9913963 +1403 760 -90320.994053 +1404 760 -18909.0650259 +761 761 16788621.6609 +762 761 -2.53319740295e-7 +805 761 547025.666412 +806 761 -2077312.65726 +807 761 1360828.33484 +811 761 -90320.994053 +812 761 370626.696215 +813 761 4631.25243446 +1396 761 547025.666412 +1397 761 -2077312.65726 +1398 761 -1360828.33484 +1402 761 -90320.994053 +1403 761 370626.696215 +1404 761 -4631.25243458 +762 762 18240145.4814 +805 762 -358351.461509 +806 762 1360828.33484 +807 762 -2284273.33935 +811 762 -18909.0650259 +812 762 -4631.25243458 +813 762 -5293544.01962 +1396 762 358351.461509 +1397 762 -1360828.33484 +1398 762 -2284273.33935 +1402 762 18909.0650259 +1403 762 4631.25243446 +1404 762 -5293544.01962 +763 763 1011851.60912 +764 763 4110093.43272 +765 763 -4.842877388e-8 +766 763 -311975.890718 +767 763 -1184718.57235 +768 763 1.02445483208e-8 +814 763 22134.9913963 +815 763 90320.994053 +816 763 -18909.0650259 +817 763 -144050.092155 +818 763 -547025.666412 +819 763 358351.461509 +764 764 16788621.6609 +765 764 -2.08616256714e-7 +766 764 -1184718.57235 +767 764 -4498931.28739 +768 764 3.35276126862e-8 +814 764 90320.994053 +815 764 370626.696215 +816 764 4631.25243447 +817 764 -547025.666412 +818 764 -2077312.65726 +819 764 1360828.33484 +765 765 18240145.4814 +766 765 1.11758708954e-8 +767 765 3.72529029846e-8 +768 765 758227.34113 +814 765 18909.0650259 +815 765 -4631.25243456 +816 765 -5293544.01962 +817 765 358351.461509 +818 765 1360828.33484 +819 765 -2284273.33935 +766 766 2350262.38096 +767 766 4600302.12491 +768 766 -2.60770320892e-8 +769 766 2.98023223877e-8 +770 766 -1.19209289551e-7 +772 766 -413671.243673 +773 766 -1334423.36669 +774 766 1.58324837685e-8 +814 766 -144050.092155 +815 766 -547025.666412 +816 766 -358351.461509 +817 766 -474420.971735 +818 766 131724.797328 +819 766 -23329.9193232 +820 766 800256.904552 +821 766 8988441.97885 +823 766 -193836.559394 +824 766 -625279.223852 +825 766 416676.259817 +767 767 17048556.8253 +768 767 -8.94069671631e-8 +769 767 5.96046447754e-8 +772 767 -1334423.36669 +773 767 -4304591.50545 +774 767 4.842877388e-8 +814 767 -547025.666412 +815 767 -2077312.65726 +816 767 -1360828.33484 +817 767 131724.797328 +818 767 -28174.604603 +819 767 6684.54707694 +820 767 -6040684.68178 +821 767 -800256.904552 +823 767 -625279.223852 +824 767 -2017029.75436 +825 767 1344116.96715 +768 768 36176122.0894 +772 768 1.39698386192e-8 +773 768 4.09781932831e-8 +774 768 830431.306934 +814 768 -358351.461509 +815 768 -1360828.33484 +816 768 -2284273.33935 +817 768 23329.9193232 +818 768 -6684.54707697 +819 768 -14301137.6219 +823 768 416676.259817 +824 768 1344116.96715 +825 768 -2296979.40748 +769 769 305878308.174 +770 769 66094123.115 +817 769 -800256.904552 +818 769 6040684.68178 +820 769 64525494.457 +821 769 -4237812.99363 +770 770 549336918.275 +817 770 -8988441.97885 +818 770 800256.904552 +820 770 -4237812.99363 +821 770 48915452.1011 +771 771 715555.555556 +822 771 -357777.777778 +772 772 1816739.3315 +773 772 5341279.87864 +774 772 -3.53902578354e-8 +775 772 -544032.43723 +776 772 -1483724.82881 +777 772 2.04890966415e-8 +817 772 -193836.559394 +818 772 -625279.223852 +819 772 -416676.259817 +823 772 24667.0078013 +824 772 73754.1287476 +825 772 -27106.375452 +826 772 -260348.273704 +827 772 -710040.746465 +828 772 484442.198447 +773 773 15814018.062 +774 773 -1.11758708954e-7 +775 773 -1483724.82881 +776 773 -4046522.26039 +777 773 5.58793544769e-8 +817 773 -625279.223852 +818 773 -2017029.75436 +819 773 -1344116.96715 +823 773 73754.1287476 +824 773 222052.369366 +825 773 9164.38855485 +826 773 -710040.746465 +827 773 -1936474.76309 +828 773 1321205.99576 +774 774 18448886.5844 +775 774 2.04890966415e-8 +776 774 5.58793544769e-8 +777 774 931107.693646 +817 774 -416676.259817 +818 774 -1344116.96715 +819 774 -2296979.40748 +823 774 27106.375452 +824 774 -9164.3885549 +825 774 -5492991.14412 +826 774 484442.198447 +827 774 1321205.99576 +828 774 -2315242.24089 +775 775 2558539.62181 +776 775 6138307.60949 +777 775 -5.58793544769e-8 +778 775 -759692.612592 +779 775 -1651505.67955 +780 775 2.98023223877e-8 +823 775 -260348.273704 +824 775 -710040.746465 +825 775 -484442.198447 +826 775 12227.6197724 +827 775 33038.3525624 +828 775 -41457.2556898 +829 775 -379286.632065 +830 775 -824536.156663 +831 775 588085.337671 +776 776 14915778.6682 +777 776 -1.34110450745e-7 +778 776 -1651505.67955 +779 776 -3590229.73815 +780 776 6.33299350738e-8 +823 776 -710040.746465 +824 776 -1936474.76309 +825 776 -1321205.99576 +826 776 33038.3525624 +827 776 89431.3340664 +828 776 17103.8438088 +829 776 -824536.156663 +830 776 -1792469.90579 +831 776 1278446.38624 +777 777 18669379.7869 +778 777 2.70083546639e-8 +779 777 5.77419996262e-8 +780 777 1124555.53479 +823 777 -484442.198447 +824 777 -1321205.99576 +825 777 -2315242.24089 +826 777 41457.2556898 +827 777 -17103.8438089 +828 777 -5695176.55864 +829 777 588085.337671 +830 777 1278446.38624 +831 777 -2352102.70813 +778 778 3880798.16119 +779 778 7110485.96967 +780 778 -4.47034835815e-8 +781 778 -1076372.38828 +782 778 -1736084.49722 +783 778 2.421438694e-8 +826 778 -379286.632065 +827 778 -824536.156663 +828 778 -588085.337671 +829 778 -52167.0393845 +830 778 -83826.4031561 +831 778 -61373.985991 +832 778 -590912.90871 +833 778 -953085.336629 +834 778 741520.302649 +779 779 13318817.2723 +780 779 -8.94069671631e-8 +781 779 -1736084.49722 +782 779 -2800136.28584 +783 779 3.91155481339e-8 +826 779 -824536.156663 +827 779 -1792469.90579 +828 779 -1278446.38624 +829 779 -83826.4031561 +830 779 -134521.304432 +831 779 32978.3592395 +832 779 -953085.336629 +833 779 -1537234.41392 +834 779 1196000.48814 +780 780 19146980.3477 +781 780 2.23517417908e-8 +782 780 3.53902578354e-8 +783 780 1520483.39551 +826 780 -588085.337671 +827 780 -1278446.38624 +828 780 -2352102.70813 +829 780 61373.985991 +830 780 -32978.3592395 +831 780 -6109264.54972 +832 780 741520.302649 +833 780 1196000.48814 +834 780 -2434642.38116 +781 781 6516876.42304 +782 781 8531914.96891 +783 781 -2.23517417908e-8 +784 781 -2626121.17726 +785 781 -2984228.61052 +786 781 1.30385160446e-8 +829 781 -590912.90871 +830 781 -953085.336629 +831 781 -741520.302649 +832 781 222027.67781 +833 781 227177.812693 +834 781 -75251.5247453 +835 781 -1038306.19785 +836 781 -1179893.40665 +837 781 929649.114512 +782 782 11512089.4984 +783 782 -2.98023223877e-8 +784 782 -2984228.61052 +785 782 -3391168.87559 +786 782 1.30385160446e-8 +829 782 -953085.336629 +830 782 -1537234.41392 +831 782 -1196000.48814 +832 782 227177.812693 +833 782 217630.207531 +834 782 55832.4159336 +835 782 -1179893.40665 +836 782 -1340787.9621 +837 782 1056419.44831 +783 783 18361731.7736 +784 783 3.72529029846e-9 +785 783 5.58793544769e-9 +786 783 -125380.553907 +829 783 -741520.302649 +830 783 -1196000.48814 +831 783 -2434642.38116 +832 783 75251.5247453 +833 783 -55832.4159336 +834 783 -5287984.36193 +835 783 929649.114512 +836 783 1056419.44831 +837 783 -2155790.5645 +784 784 11197028.9194 +785 784 7624235.1212 +786 784 -7.45058059692e-9 +787 784 -1154846.00933 +788 784 -476225.158488 +789 784 1.86264514923e-9 +832 784 -1038306.19785 +833 784 -1179893.40665 +834 784 -929649.114512 +835 784 -908773.635179 +836 784 -175831.894858 +837 784 -148519.569575 +838 784 -1760951.03338 +839 784 -726165.37459 +840 784 1300948.03845 +785 785 6560950.4012 +787 785 -476225.158488 +788 785 -196381.508655 +789 785 9.31322574615e-10 +832 785 -1179893.40665 +833 785 -1340787.9621 +834 785 -1056419.44831 +835 785 -175831.894858 +836 785 153537.59263 +837 785 207978.412157 +838 785 -726165.37459 +839 785 -299449.639006 +840 785 536473.417918 +786 786 21281133.3311 +789 786 4075797.18655 +832 786 -929649.114512 +833 786 -1056419.44831 +834 786 -2155790.5645 +835 786 148519.569575 +836 786 -207978.412157 +837 786 -7295491.64648 +838 786 1300948.03845 +839 786 536473.417918 +840 786 -3164492.7709 +787 787 14087608.2601 +788 787 -7.45058059692e-9 +789 787 -7.45058059692e-9 +790 787 -1154846.00933 +791 787 476225.158488 +792 787 1.86264514923e-9 +835 787 -1760951.03338 +836 787 -726165.37459 +837 787 -1300948.03845 +838 787 -2367056.05396 +839 787 1.11758708954e-8 +840 787 1.86264514923e-9 +841 787 -1760951.03338 +842 787 726165.37459 +843 787 1300948.03845 +788 788 2395597.11087 +789 788 7.45058059692e-9 +790 788 476225.158488 +791 788 -196381.508655 +792 788 -9.31322574615e-10 +835 788 -726165.37459 +836 788 -299449.639006 +837 788 -536473.417918 +838 788 9.31322574615e-9 +839 788 -402517.768767 +840 788 429178.734334 +841 788 726165.37459 +842 788 -299449.639006 +843 788 -536473.417918 +789 789 25315942.1547 +792 789 4075797.18655 +835 789 -1300948.03845 +836 789 -536473.417918 +837 789 -3164492.7709 +839 789 -429178.734334 +840 789 -10404782.7221 +841 789 1300948.03845 +842 789 -536473.417918 +843 789 -3164492.7709 +790 790 11197028.9194 +791 790 -7624235.1212 +792 790 7.45058059692e-9 +793 790 -2626121.17726 +794 790 2984228.61052 +795 790 -5.58793544769e-9 +838 790 -1760951.03338 +839 790 726165.37459 +840 790 -1300948.03845 +841 790 -908773.635179 +842 790 175831.894858 +843 790 148519.569575 +844 790 -1038306.19785 +845 790 1179893.40665 +846 790 929649.114512 +791 791 6560950.4012 +793 791 2984228.61052 +794 791 -3391168.87559 +795 791 5.58793544769e-9 +838 791 726165.37459 +839 791 -299449.639006 +840 791 536473.417918 +841 791 175831.894858 +842 791 153537.59263 +843 791 207978.412157 +844 791 1179893.40665 +845 791 -1340787.9621 +846 791 -1056419.44831 +792 792 21281133.3311 +793 792 -1.11758708954e-8 +794 792 1.11758708954e-8 +795 792 -125380.553907 +838 792 -1300948.03845 +839 792 536473.417918 +840 792 -3164492.7709 +841 792 -148519.569575 +842 792 -207978.412157 +843 792 -7295491.64648 +844 792 929649.114512 +845 792 -1056419.44831 +846 792 -2155790.5645 +793 793 6516876.42304 +794 793 -8531914.96891 +795 793 2.98023223877e-8 +796 793 -1076372.38828 +797 793 1736084.49722 +798 793 -1.67638063431e-8 +841 793 -1038306.19785 +842 793 1179893.40665 +843 793 -929649.114512 +844 793 222027.67781 +845 793 -227177.812693 +846 793 75251.5247453 +847 793 -590912.90871 +848 793 953085.336629 +849 793 741520.302649 +794 794 11512089.4984 +795 794 -3.72529029846e-8 +796 794 1736084.49722 +797 794 -2800136.28584 +798 794 2.60770320892e-8 +841 794 1179893.40665 +842 794 -1340787.9621 +843 794 1056419.44831 +844 794 -227177.812693 +845 794 217630.207531 +846 794 55832.4159336 +847 794 953085.336629 +848 794 -1537234.41392 +849 794 -1196000.48814 +795 795 18361731.7736 +796 795 -2.04890966415e-8 +797 795 3.35276126862e-8 +798 795 1520483.39551 +841 795 -929649.114512 +842 795 1056419.44831 +843 795 -2155790.5645 +844 795 -75251.5247453 +845 795 -55832.4159336 +846 795 -5287984.36193 +847 795 741520.302649 +848 795 -1196000.48814 +849 795 -2434642.38116 +796 796 3880798.16119 +797 796 -7110485.96967 +798 796 3.72529029846e-8 +799 796 -759692.612592 +800 796 1651505.67955 +801 796 -1.86264514923e-8 +844 796 -590912.90871 +845 796 953085.336629 +846 796 -741520.302649 +847 796 -52167.0393845 +848 796 83826.4031561 +849 796 61373.985991 +850 796 -379286.632065 +851 796 824536.156663 +852 796 588085.337671 +797 797 13318817.2723 +798 797 -6.70552253723e-8 +799 797 1651505.67955 +800 797 -3590229.73815 +801 797 4.09781932831e-8 +844 797 953085.336629 +845 797 -1537234.41392 +846 797 1196000.48814 +847 797 83826.4031561 +848 797 -134521.304432 +849 797 32978.3592395 +850 797 824536.156663 +851 797 -1792469.90579 +852 797 -1278446.38624 +798 798 19146980.3477 +799 798 -2.51457095146e-8 +800 798 5.40167093277e-8 +801 798 1124555.53479 +844 798 -741520.302649 +845 798 1196000.48814 +846 798 -2434642.38116 +847 798 -61373.985991 +848 798 -32978.3592395 +849 798 -6109264.54972 +850 798 588085.337671 +851 798 -1278446.38624 +852 798 -2352102.70813 +799 799 2558539.62181 +800 799 -6138307.60949 +801 799 4.28408384323e-8 +802 799 -544032.43723 +803 799 1483724.82881 +804 799 -2.51457095146e-8 +847 799 -379286.632065 +848 799 824536.156663 +849 799 -588085.337671 +850 799 12227.6197724 +851 799 -33038.3525624 +852 799 41457.2556898 +853 799 -260348.273704 +854 799 710040.746465 +855 799 484442.198447 +800 800 14915778.6682 +801 800 -1.19209289551e-7 +802 800 1483724.82881 +803 800 -4046522.26039 +804 800 7.07805156708e-8 +847 800 824536.156663 +848 800 -1792469.90579 +849 800 1278446.38624 +850 800 -33038.3525624 +851 800 89431.3340665 +852 800 17103.8438088 +853 800 710040.746465 +854 800 -1936474.76309 +855 800 -1321205.99576 +801 801 18669379.7869 +802 801 -2.79396772385e-8 +803 801 7.82310962677e-8 +804 801 931107.693646 +847 801 -588085.337671 +848 801 1278446.38624 +849 801 -2352102.70813 +850 801 -41457.2556898 +851 801 -17103.8438089 +852 801 -5695176.55864 +853 801 484442.198447 +854 801 -1321205.99576 +855 801 -2315242.24089 +802 802 1816739.3315 +803 802 -5341279.87864 +804 802 3.72529029846e-8 +805 802 -413671.243673 +806 802 1334423.36669 +807 802 -8.38190317154e-9 +850 802 -260348.273704 +851 802 710040.746465 +852 802 -484442.198447 +853 802 24667.0078013 +854 802 -73754.1287476 +855 802 27106.375452 +856 802 -193836.559394 +857 802 625279.223852 +858 802 416676.259817 +803 803 15814018.062 +804 803 -1.11758708954e-7 +805 803 1334423.36669 +806 803 -4304591.50545 +807 803 2.98023223877e-8 +850 803 710040.746465 +851 803 -1936474.76309 +852 803 1321205.99576 +853 803 -73754.1287476 +854 803 222052.369366 +855 803 9164.38855486 +856 803 625279.223852 +857 803 -2017029.75436 +858 803 -1344116.96715 +804 804 18448886.5844 +805 804 -1.11758708954e-8 +806 804 3.72529029846e-8 +807 804 830431.306934 +850 804 -484442.198447 +851 804 1321205.99576 +852 804 -2315242.24089 +853 804 -27106.375452 +854 804 -9164.38855491 +855 804 -5492991.14412 +856 804 416676.259817 +857 804 -1344116.96715 +858 804 -2296979.40748 +805 805 2350262.78049 +806 805 -4600302.86075 +807 805 2.04890966415e-8 +808 805 -7.45058059692e-9 +811 805 -311975.890718 +812 805 1184718.57235 +813 805 -1.02445483208e-8 +853 805 -193836.559394 +854 805 625279.223852 +855 805 -416676.259817 +856 805 -474421.1715 +857 805 -131724.429407 +858 805 23329.9193232 +859 805 -800250.281991 +860 805 8988445.57462 +862 805 -144050.092155 +863 805 547025.666412 +864 805 358351.461509 +806 806 17048556.4258 +807 806 -7.45058059692e-8 +808 806 5.96046447754e-8 +809 806 7.45058059692e-9 +811 806 1184718.57235 +812 806 -4498931.28739 +813 806 3.35276126862e-8 +853 806 625279.223852 +854 806 -2017029.75436 +855 806 1344116.96715 +856 806 -131724.429407 +857 806 -28174.4048381 +858 806 6684.54707694 +859 806 -6040681.08601 +860 806 800250.281991 +862 806 547025.666412 +863 806 -2077312.65726 +864 806 -1360828.33484 +807 807 36176122.0894 +811 807 -1.30385160446e-8 +812 807 4.47034835815e-8 +813 807 758227.341129 +853 807 -416676.259817 +854 807 1344116.96715 +855 807 -2296979.40748 +856 807 -23329.9193232 +857 807 -6684.54707698 +858 807 -14301137.6219 +862 807 358351.461509 +863 807 -1360828.33484 +864 807 -2284273.33935 +808 808 305878011.196 +809 808 -66093576.1502 +856 808 800250.281991 +857 808 6040681.08601 +859 808 64525513.4986 +860 808 4237777.92342 +809 809 549337215.254 +856 809 -8988445.57462 +857 809 -800250.281991 +859 809 4237777.92342 +860 809 48915433.0595 +810 810 715555.555556 +861 810 -357777.777778 +811 811 1011851.60912 +812 811 -4110093.43272 +813 811 6.14672899246e-8 +856 811 -144050.092155 +857 811 547025.666412 +858 811 -358351.461509 +862 811 22134.9913963 +863 811 -90320.9940531 +864 811 18909.0650259 +812 812 16788621.6609 +813 812 -2.53319740295e-7 +856 812 547025.666412 +857 812 -2077312.65726 +858 812 1360828.33484 +862 812 -90320.9940531 +863 812 370626.696215 +864 812 4631.25243447 +813 813 18240145.4814 +856 813 -358351.461509 +857 813 1360828.33484 +858 813 -2284273.33935 +862 813 -18909.0650259 +863 813 -4631.25243458 +864 813 -5293544.01962 +814 814 1011851.60912 +815 814 4110093.43272 +816 814 -4.65661287308e-8 +817 814 -311975.890718 +818 814 -1184718.57235 +819 814 1.02445483208e-8 +865 814 22134.9913963 +866 814 90320.994053 +867 814 -18909.0650259 +868 814 -144050.092155 +869 814 -547025.666412 +870 814 358351.461509 +815 815 16788621.6609 +816 815 -2.01165676117e-7 +817 815 -1184718.57235 +818 815 -4498931.28739 +819 815 3.35276126862e-8 +865 815 90320.994053 +866 815 370626.696215 +867 815 4631.25243447 +868 815 -547025.666412 +869 815 -2077312.65726 +870 815 1360828.33484 +816 816 18240145.4814 +817 816 1.11758708954e-8 +818 816 3.72529029846e-8 +819 816 758227.34113 +865 816 18909.0650259 +866 816 -4631.25243456 +867 816 -5293544.01962 +868 816 358351.461509 +869 816 1360828.33484 +870 816 -2284273.33935 +817 817 2350262.38096 +818 817 4600302.12491 +819 817 -2.60770320892e-8 +820 817 2.98023223877e-8 +821 817 -1.19209289551e-7 +823 817 -413671.243673 +824 817 -1334423.36669 +825 817 1.58324837685e-8 +865 817 -144050.092155 +866 817 -547025.666412 +867 817 -358351.461509 +868 817 -474420.971735 +869 817 131724.797328 +870 817 -23329.9193232 +871 817 800256.904552 +872 817 8988441.97885 +874 817 -193836.559394 +875 817 -625279.223852 +876 817 416676.259817 +818 818 17048556.8253 +819 818 -8.94069671631e-8 +820 818 5.96046447754e-8 +823 818 -1334423.36669 +824 818 -4304591.50545 +825 818 4.842877388e-8 +865 818 -547025.666412 +866 818 -2077312.65726 +867 818 -1360828.33484 +868 818 131724.797328 +869 818 -28174.604603 +870 818 6684.54707694 +871 818 -6040684.68178 +872 818 -800256.904552 +874 818 -625279.223852 +875 818 -2017029.75436 +876 818 1344116.96715 +819 819 36176122.0894 +823 819 1.39698386192e-8 +824 819 4.09781932831e-8 +825 819 830431.306934 +865 819 -358351.461509 +866 819 -1360828.33484 +867 819 -2284273.33935 +868 819 23329.9193232 +869 819 -6684.54707697 +870 819 -14301137.6219 +874 819 416676.259817 +875 819 1344116.96715 +876 819 -2296979.40748 +820 820 305878308.174 +821 820 66094123.115 +868 820 -800256.904552 +869 820 6040684.68178 +871 820 64525494.457 +872 820 -4237812.99363 +821 821 549336918.275 +868 821 -8988441.97885 +869 821 800256.904552 +871 821 -4237812.99363 +872 821 48915452.1011 +822 822 715555.555556 +873 822 -357777.777778 +823 823 1816739.3315 +824 823 5341279.87864 +825 823 -3.53902578354e-8 +826 823 -544032.43723 +827 823 -1483724.82881 +828 823 2.04890966415e-8 +868 823 -193836.559394 +869 823 -625279.223852 +870 823 -416676.259817 +874 823 24667.0078013 +875 823 73754.1287476 +876 823 -27106.375452 +877 823 -260348.273704 +878 823 -710040.746465 +879 823 484442.198447 +824 824 15814018.062 +825 824 -1.11758708954e-7 +826 824 -1483724.82881 +827 824 -4046522.26039 +828 824 5.58793544769e-8 +868 824 -625279.223852 +869 824 -2017029.75436 +870 824 -1344116.96715 +874 824 73754.1287476 +875 824 222052.369366 +876 824 9164.38855485 +877 824 -710040.746465 +878 824 -1936474.76309 +879 824 1321205.99576 +825 825 18448886.5844 +826 825 2.04890966415e-8 +827 825 5.58793544769e-8 +828 825 931107.693646 +868 825 -416676.259817 +869 825 -1344116.96715 +870 825 -2296979.40748 +874 825 27106.375452 +875 825 -9164.3885549 +876 825 -5492991.14412 +877 825 484442.198447 +878 825 1321205.99576 +879 825 -2315242.24089 +826 826 2558539.62181 +827 826 6138307.60949 +828 826 -5.58793544769e-8 +829 826 -759692.612592 +830 826 -1651505.67955 +831 826 2.98023223877e-8 +874 826 -260348.273704 +875 826 -710040.746465 +876 826 -484442.198447 +877 826 12227.6197724 +878 826 33038.3525624 +879 826 -41457.2556898 +880 826 -379286.632065 +881 826 -824536.156663 +882 826 588085.337671 +827 827 14915778.6682 +828 827 -1.34110450745e-7 +829 827 -1651505.67955 +830 827 -3590229.73815 +831 827 6.33299350738e-8 +874 827 -710040.746465 +875 827 -1936474.76309 +876 827 -1321205.99576 +877 827 33038.3525624 +878 827 89431.3340664 +879 827 17103.8438088 +880 827 -824536.156663 +881 827 -1792469.90579 +882 827 1278446.38624 +828 828 18669379.7869 +829 828 2.70083546639e-8 +830 828 5.77419996262e-8 +831 828 1124555.53479 +874 828 -484442.198447 +875 828 -1321205.99576 +876 828 -2315242.24089 +877 828 41457.2556898 +878 828 -17103.8438089 +879 828 -5695176.55864 +880 828 588085.337671 +881 828 1278446.38624 +882 828 -2352102.70813 +829 829 3880798.16119 +830 829 7110485.96967 +831 829 -4.47034835815e-8 +832 829 -1076372.38828 +833 829 -1736084.49722 +834 829 2.421438694e-8 +877 829 -379286.632065 +878 829 -824536.156663 +879 829 -588085.337671 +880 829 -52167.0393845 +881 829 -83826.4031561 +882 829 -61373.985991 +883 829 -590912.90871 +884 829 -953085.336629 +885 829 741520.302649 +830 830 13318817.2723 +831 830 -8.94069671631e-8 +832 830 -1736084.49722 +833 830 -2800136.28584 +834 830 3.91155481339e-8 +877 830 -824536.156663 +878 830 -1792469.90579 +879 830 -1278446.38624 +880 830 -83826.4031561 +881 830 -134521.304432 +882 830 32978.3592395 +883 830 -953085.336629 +884 830 -1537234.41392 +885 830 1196000.48814 +831 831 19146980.3477 +832 831 2.23517417908e-8 +833 831 3.53902578354e-8 +834 831 1520483.39551 +877 831 -588085.337671 +878 831 -1278446.38624 +879 831 -2352102.70813 +880 831 61373.985991 +881 831 -32978.3592395 +882 831 -6109264.54972 +883 831 741520.302649 +884 831 1196000.48814 +885 831 -2434642.38116 +832 832 6516876.42304 +833 832 8531914.96891 +834 832 -2.23517417908e-8 +835 832 -2626121.17726 +836 832 -2984228.61052 +837 832 1.30385160446e-8 +880 832 -590912.90871 +881 832 -953085.336629 +882 832 -741520.302649 +883 832 222027.67781 +884 832 227177.812693 +885 832 -75251.5247453 +886 832 -1038306.19785 +887 832 -1179893.40665 +888 832 929649.114512 +833 833 11512089.4984 +834 833 -2.98023223877e-8 +835 833 -2984228.61052 +836 833 -3391168.87559 +837 833 1.30385160446e-8 +880 833 -953085.336629 +881 833 -1537234.41392 +882 833 -1196000.48814 +883 833 227177.812693 +884 833 217630.207531 +885 833 55832.4159336 +886 833 -1179893.40665 +887 833 -1340787.9621 +888 833 1056419.44831 +834 834 18361731.7736 +835 834 5.58793544769e-9 +836 834 7.45058059692e-9 +837 834 -125380.553907 +880 834 -741520.302649 +881 834 -1196000.48814 +882 834 -2434642.38116 +883 834 75251.5247453 +884 834 -55832.4159336 +885 834 -5287984.36193 +886 834 929649.114512 +887 834 1056419.44831 +888 834 -2155790.5645 +835 835 11197028.9194 +836 835 7624235.1212 +837 835 -1.49011611938e-8 +838 835 -1154846.00933 +839 835 -476225.158488 +840 835 1.86264514923e-9 +883 835 -1038306.19785 +884 835 -1179893.40665 +885 835 -929649.114512 +886 835 -908773.635179 +887 835 -175831.894858 +888 835 -148519.569575 +889 835 -1760951.03338 +890 835 -726165.37459 +891 835 1300948.03845 +836 836 6560950.4012 +837 836 -7.45058059692e-9 +838 836 -476225.158488 +839 836 -196381.508655 +840 836 9.31322574615e-10 +883 836 -1179893.40665 +884 836 -1340787.9621 +885 836 -1056419.44831 +886 836 -175831.894858 +887 836 153537.59263 +888 836 207978.412157 +889 836 -726165.37459 +890 836 -299449.639006 +891 836 536473.417918 +837 837 21281133.3311 +840 837 4075797.18655 +883 837 -929649.114512 +884 837 -1056419.44831 +885 837 -2155790.5645 +886 837 148519.569575 +887 837 -207978.412157 +888 837 -7295491.64648 +889 837 1300948.03845 +890 837 536473.417918 +891 837 -3164492.7709 +838 838 14087608.2601 +839 838 -7.45058059692e-9 +840 838 -7.45058059692e-9 +841 838 -1154846.00933 +842 838 476225.158488 +843 838 1.86264514923e-9 +886 838 -1760951.03338 +887 838 -726165.37459 +888 838 -1300948.03845 +889 838 -2367056.05396 +890 838 1.11758708954e-8 +891 838 1.86264514923e-9 +892 838 -1760951.03338 +893 838 726165.37459 +894 838 1300948.03845 +839 839 2395597.11087 +840 839 7.45058059692e-9 +841 839 476225.158488 +842 839 -196381.508655 +843 839 -9.31322574615e-10 +886 839 -726165.37459 +887 839 -299449.639006 +888 839 -536473.417918 +889 839 9.31322574615e-9 +890 839 -402517.768767 +891 839 429178.734334 +892 839 726165.37459 +893 839 -299449.639006 +894 839 -536473.417918 +840 840 25315942.1547 +843 840 4075797.18655 +886 840 -1300948.03845 +887 840 -536473.417918 +888 840 -3164492.7709 +890 840 -429178.734334 +891 840 -10404782.7221 +892 840 1300948.03845 +893 840 -536473.417918 +894 840 -3164492.7709 +841 841 11197028.9194 +842 841 -7624235.1212 +843 841 7.45058059692e-9 +844 841 -2626121.17726 +845 841 2984228.61052 +846 841 -5.58793544769e-9 +889 841 -1760951.03338 +890 841 726165.37459 +891 841 -1300948.03845 +892 841 -908773.635179 +893 841 175831.894858 +894 841 148519.569575 +895 841 -1038306.19785 +896 841 1179893.40665 +897 841 929649.114512 +842 842 6560950.4012 +844 842 2984228.61052 +845 842 -3391168.87559 +846 842 5.58793544769e-9 +889 842 726165.37459 +890 842 -299449.639006 +891 842 536473.417918 +892 842 175831.894858 +893 842 153537.59263 +894 842 207978.412157 +895 842 1179893.40665 +896 842 -1340787.9621 +897 842 -1056419.44831 +843 843 21281133.3311 +844 843 -9.31322574615e-9 +845 843 7.45058059692e-9 +846 843 -125380.553907 +889 843 -1300948.03845 +890 843 536473.417918 +891 843 -3164492.7709 +892 843 -148519.569575 +893 843 -207978.412157 +894 843 -7295491.64648 +895 843 929649.114512 +896 843 -1056419.44831 +897 843 -2155790.5645 +844 844 6516876.42304 +845 844 -8531914.96891 +846 844 1.86264514923e-8 +847 844 -1076372.38828 +848 844 1736084.49722 +849 844 -1.67638063431e-8 +892 844 -1038306.19785 +893 844 1179893.40665 +894 844 -929649.114512 +895 844 222027.67781 +896 844 -227177.812693 +897 844 75251.5247453 +1405 844 -590912.90871 +1406 844 953085.336629 +1407 844 741520.302649 +845 845 11512089.4984 +846 845 -2.23517417908e-8 +847 845 1736084.49722 +848 845 -2800136.28584 +849 845 2.60770320892e-8 +892 845 1179893.40665 +893 845 -1340787.9621 +894 845 1056419.44831 +895 845 -227177.812693 +896 845 217630.207531 +897 845 55832.4159336 +1405 845 953085.336629 +1406 845 -1537234.41392 +1407 845 -1196000.48814 +846 846 18361731.7736 +847 846 -2.04890966415e-8 +848 846 3.35276126862e-8 +849 846 1520483.39551 +892 846 -929649.114512 +893 846 1056419.44831 +894 846 -2155790.5645 +895 846 -75251.5247453 +896 846 -55832.4159336 +897 846 -5287984.36193 +1405 846 741520.302649 +1406 846 -1196000.48814 +1407 846 -2434642.38116 +847 847 3880798.16119 +848 847 -7110485.96967 +849 847 3.72529029846e-8 +850 847 -759692.612592 +851 847 1651505.67955 +852 847 -1.86264514923e-8 +895 847 -590912.90871 +896 847 953085.336629 +897 847 -741520.302649 +1405 847 -52167.0393845 +1406 847 83826.4031561 +1407 847 61373.985991 +1408 847 -379286.632065 +1409 847 824536.156663 +1410 847 588085.337671 +848 848 13318817.2723 +849 848 -6.70552253723e-8 +850 848 1651505.67955 +851 848 -3590229.73815 +852 848 4.09781932831e-8 +895 848 953085.336629 +896 848 -1537234.41392 +897 848 1196000.48814 +1405 848 83826.4031561 +1406 848 -134521.304432 +1407 848 32978.3592395 +1408 848 824536.156663 +1409 848 -1792469.90579 +1410 848 -1278446.38624 +849 849 19146980.3477 +850 849 -2.51457095146e-8 +851 849 5.40167093277e-8 +852 849 1124555.53479 +895 849 -741520.302649 +896 849 1196000.48814 +897 849 -2434642.38116 +1405 849 -61373.985991 +1406 849 -32978.3592395 +1407 849 -6109264.54972 +1408 849 588085.337671 +1409 849 -1278446.38624 +1410 849 -2352102.70813 +850 850 2558539.62181 +851 850 -6138307.60949 +852 850 4.28408384323e-8 +853 850 -544032.43723 +854 850 1483724.82881 +855 850 -2.51457095146e-8 +1405 850 -379286.632065 +1406 850 824536.156663 +1407 850 -588085.337671 +1408 850 12227.6197724 +1409 850 -33038.3525624 +1410 850 41457.2556898 +1411 850 -260348.273704 +1412 850 710040.746465 +1413 850 484442.198447 +851 851 14915778.6682 +852 851 -1.19209289551e-7 +853 851 1483724.82881 +854 851 -4046522.26039 +855 851 7.07805156708e-8 +1405 851 824536.156663 +1406 851 -1792469.90579 +1407 851 1278446.38624 +1408 851 -33038.3525624 +1409 851 89431.3340665 +1410 851 17103.8438088 +1411 851 710040.746465 +1412 851 -1936474.76309 +1413 851 -1321205.99576 +852 852 18669379.7869 +853 852 -2.79396772385e-8 +854 852 7.82310962677e-8 +855 852 931107.693646 +1405 852 -588085.337671 +1406 852 1278446.38624 +1407 852 -2352102.70813 +1408 852 -41457.2556898 +1409 852 -17103.8438089 +1410 852 -5695176.55864 +1411 852 484442.198447 +1412 852 -1321205.99576 +1413 852 -2315242.24089 +853 853 1816739.3315 +854 853 -5341279.87864 +855 853 3.72529029846e-8 +856 853 -413671.243673 +857 853 1334423.36669 +858 853 -8.38190317154e-9 +1408 853 -260348.273704 +1409 853 710040.746465 +1410 853 -484442.198447 +1411 853 24667.0078013 +1412 853 -73754.1287476 +1413 853 27106.375452 +1414 853 -193836.559394 +1415 853 625279.223852 +1416 853 416676.259817 +854 854 15814018.062 +855 854 -1.11758708954e-7 +856 854 1334423.36669 +857 854 -4304591.50545 +858 854 2.98023223877e-8 +1408 854 710040.746465 +1409 854 -1936474.76309 +1410 854 1321205.99576 +1411 854 -73754.1287476 +1412 854 222052.369366 +1413 854 9164.38855486 +1414 854 625279.223852 +1415 854 -2017029.75436 +1416 854 -1344116.96715 +855 855 18448886.5844 +856 855 -1.11758708954e-8 +857 855 3.72529029846e-8 +858 855 830431.306934 +1408 855 -484442.198447 +1409 855 1321205.99576 +1410 855 -2315242.24089 +1411 855 -27106.375452 +1412 855 -9164.38855491 +1413 855 -5492991.14412 +1414 855 416676.259817 +1415 855 -1344116.96715 +1416 855 -2296979.40748 +856 856 2350262.78049 +857 856 -4600302.86075 +858 856 2.04890966415e-8 +859 856 -7.45058059692e-9 +862 856 -311975.890718 +863 856 1184718.57235 +864 856 -1.02445483208e-8 +1411 856 -193836.559394 +1412 856 625279.223852 +1413 856 -416676.259817 +1414 856 -474421.1715 +1415 856 -131724.429407 +1416 856 23329.9193232 +1417 856 -800250.281991 +1418 856 8988445.57462 +1420 856 -144050.092155 +1421 856 547025.666412 +1422 856 358351.461509 +857 857 17048556.4258 +858 857 -7.45058059692e-8 +859 857 5.96046447754e-8 +860 857 7.45058059692e-9 +862 857 1184718.57235 +863 857 -4498931.28739 +864 857 3.35276126862e-8 +1411 857 625279.223852 +1412 857 -2017029.75436 +1413 857 1344116.96715 +1414 857 -131724.429407 +1415 857 -28174.4048381 +1416 857 6684.54707694 +1417 857 -6040681.08601 +1418 857 800250.281991 +1420 857 547025.666412 +1421 857 -2077312.65726 +1422 857 -1360828.33484 +858 858 36176122.0894 +862 858 -1.30385160446e-8 +863 858 4.47034835815e-8 +864 858 758227.341129 +1411 858 -416676.259817 +1412 858 1344116.96715 +1413 858 -2296979.40748 +1414 858 -23329.9193232 +1415 858 -6684.54707698 +1416 858 -14301137.6219 +1420 858 358351.461509 +1421 858 -1360828.33484 +1422 858 -2284273.33935 +859 859 305878011.196 +860 859 -66093576.1502 +1414 859 800250.281991 +1415 859 6040681.08601 +1417 859 64525513.4986 +1418 859 4237777.92342 +860 860 549337215.254 +1414 860 -8988445.57462 +1415 860 -800250.281991 +1417 860 4237777.92342 +1418 860 48915433.0595 +861 861 715555.555556 +1419 861 -357777.777778 +862 862 1011851.60912 +863 862 -4110093.43272 +864 862 6.33299350738e-8 +1414 862 -144050.092155 +1415 862 547025.666412 +1416 862 -358351.461509 +1420 862 22134.9913963 +1421 862 -90320.9940531 +1422 862 18909.0650259 +863 863 16788621.6609 +864 863 -2.60770320892e-7 +1414 863 547025.666412 +1415 863 -2077312.65726 +1416 863 1360828.33484 +1420 863 -90320.9940531 +1421 863 370626.696215 +1422 863 4631.25243447 +864 864 18240145.4814 +1414 864 -358351.461509 +1415 864 1360828.33484 +1416 864 -2284273.33935 +1420 864 -18909.0650259 +1421 864 -4631.25243458 +1422 864 -5293544.01962 +865 865 1011851.60912 +866 865 4110093.43272 +867 865 -4.65661287308e-8 +868 865 -311975.890718 +869 865 -1184718.57235 +870 865 1.02445483208e-8 +1423 865 22134.9913963 +1424 865 90320.994053 +1425 865 -18909.0650259 +1426 865 -144050.092155 +1427 865 -547025.666412 +1428 865 358351.461509 +866 866 16788621.6609 +867 866 -2.01165676117e-7 +868 866 -1184718.57235 +869 866 -4498931.28739 +870 866 3.35276126862e-8 +1423 866 90320.994053 +1424 866 370626.696215 +1425 866 4631.25243447 +1426 866 -547025.666412 +1427 866 -2077312.65726 +1428 866 1360828.33484 +867 867 18240145.4814 +868 867 1.02445483208e-8 +869 867 3.35276126862e-8 +870 867 758227.341129 +1423 867 18909.0650259 +1424 867 -4631.25243456 +1425 867 -5293544.01962 +1426 867 358351.461509 +1427 867 1360828.33484 +1428 867 -2284273.33935 +868 868 2350262.38096 +869 868 4600302.12491 +870 868 -2.98023223877e-8 +871 868 2.98023223877e-8 +872 868 -1.19209289551e-7 +874 868 -413671.243673 +875 868 -1334423.36669 +876 868 1.58324837685e-8 +1423 868 -144050.092155 +1424 868 -547025.666412 +1425 868 -358351.461509 +1426 868 -474420.971735 +1427 868 131724.797328 +1428 868 -23329.9193232 +1429 868 800256.904552 +1430 868 8988441.97885 +1432 868 -193836.559394 +1433 868 -625279.223852 +1434 868 416676.259817 +869 869 17048556.8253 +870 869 -9.685754776e-8 +871 869 5.96046447754e-8 +874 869 -1334423.36669 +875 869 -4304591.50545 +876 869 4.842877388e-8 +1423 869 -547025.666412 +1424 869 -2077312.65726 +1425 869 -1360828.33484 +1426 869 131724.797328 +1427 869 -28174.6046029 +1428 869 6684.54707694 +1429 869 -6040684.68178 +1430 869 -800256.904552 +1432 869 -625279.223852 +1433 869 -2017029.75436 +1434 869 1344116.96715 +870 870 36176122.0894 +874 870 1.210719347e-8 +875 870 3.35276126862e-8 +876 870 830431.306934 +1423 870 -358351.461509 +1424 870 -1360828.33484 +1425 870 -2284273.33935 +1426 870 23329.9193232 +1427 870 -6684.54707696 +1428 870 -14301137.6219 +1432 870 416676.259817 +1433 870 1344116.96715 +1434 870 -2296979.40748 +871 871 305878308.174 +872 871 66094123.115 +1426 871 -800256.904552 +1427 871 6040684.68178 +1429 871 64525494.457 +1430 871 -4237812.99363 +872 872 549336918.275 +1426 872 -8988441.97885 +1427 872 800256.904552 +1429 872 -4237812.99363 +1430 872 48915452.1011 +873 873 715555.555556 +1431 873 -357777.777778 +874 874 1816739.3315 +875 874 5341279.87864 +876 874 -3.53902578354e-8 +877 874 -544032.43723 +878 874 -1483724.82881 +879 874 2.04890966415e-8 +1426 874 -193836.559394 +1427 874 -625279.223852 +1428 874 -416676.259817 +1432 874 24667.0078013 +1433 874 73754.1287476 +1434 874 -27106.375452 +1435 874 -260348.273704 +1436 874 -710040.746465 +1437 874 484442.198447 +875 875 15814018.062 +876 875 -1.11758708954e-7 +877 875 -1483724.82881 +878 875 -4046522.26039 +879 875 5.58793544769e-8 +1426 875 -625279.223852 +1427 875 -2017029.75436 +1428 875 -1344116.96715 +1432 875 73754.1287476 +1433 875 222052.369366 +1434 875 9164.38855485 +1435 875 -710040.746465 +1436 875 -1936474.76309 +1437 875 1321205.99576 +876 876 18448886.5844 +877 876 1.95577740669e-8 +878 876 5.21540641785e-8 +879 876 931107.693646 +1426 876 -416676.259817 +1427 876 -1344116.96715 +1428 876 -2296979.40748 +1432 876 27106.375452 +1433 876 -9164.38855491 +1434 876 -5492991.14412 +1435 876 484442.198447 +1436 876 1321205.99576 +1437 876 -2315242.24089 +877 877 2558539.62181 +878 877 6138307.60949 +879 877 -5.21540641785e-8 +880 877 -759692.612592 +881 877 -1651505.67955 +882 877 2.98023223877e-8 +1432 877 -260348.273704 +1433 877 -710040.746465 +1434 877 -484442.198447 +1435 877 12227.6197724 +1436 877 33038.3525624 +1437 877 -41457.2556898 +1438 877 -379286.632065 +1439 877 -824536.156663 +1440 877 588085.337671 +878 878 14915778.6682 +879 878 -1.26659870148e-7 +880 878 -1651505.67955 +881 878 -3590229.73815 +882 878 6.33299350738e-8 +1432 878 -710040.746465 +1433 878 -1936474.76309 +1434 878 -1321205.99576 +1435 878 33038.3525624 +1436 878 89431.3340664 +1437 878 17103.8438088 +1438 878 -824536.156663 +1439 878 -1792469.90579 +1440 878 1278446.38624 +879 879 18669379.7869 +880 879 2.70083546639e-8 +881 879 5.77419996262e-8 +882 879 1124555.53479 +1432 879 -484442.198447 +1433 879 -1321205.99576 +1434 879 -2315242.24089 +1435 879 41457.2556898 +1436 879 -17103.8438089 +1437 879 -5695176.55864 +1438 879 588085.337671 +1439 879 1278446.38624 +1440 879 -2352102.70813 +880 880 3880798.16119 +881 880 7110485.96967 +882 880 -4.47034835815e-8 +883 880 -1076372.38828 +884 880 -1736084.49722 +885 880 2.421438694e-8 +1435 880 -379286.632065 +1436 880 -824536.156663 +1437 880 -588085.337671 +1438 880 -52167.0393845 +1439 880 -83826.4031561 +1440 880 -61373.985991 +1441 880 -590912.90871 +1442 880 -953085.336629 +1443 880 741520.302649 +881 881 13318817.2723 +882 881 -8.94069671631e-8 +883 881 -1736084.49722 +884 881 -2800136.28584 +885 881 3.91155481339e-8 +1435 881 -824536.156663 +1436 881 -1792469.90579 +1437 881 -1278446.38624 +1438 881 -83826.4031561 +1439 881 -134521.304432 +1440 881 32978.3592395 +1441 881 -953085.336629 +1442 881 -1537234.41392 +1443 881 1196000.48814 +882 882 19146980.3477 +883 882 2.23517417908e-8 +884 882 3.53902578354e-8 +885 882 1520483.39551 +1435 882 -588085.337671 +1436 882 -1278446.38624 +1437 882 -2352102.70813 +1438 882 61373.985991 +1439 882 -32978.3592395 +1440 882 -6109264.54972 +1441 882 741520.302649 +1442 882 1196000.48814 +1443 882 -2434642.38116 +883 883 6516876.42304 +884 883 8531914.96891 +885 883 -2.23517417908e-8 +886 883 -2626121.17726 +887 883 -2984228.61052 +888 883 1.30385160446e-8 +1438 883 -590912.90871 +1439 883 -953085.336629 +1440 883 -741520.302649 +1441 883 222027.67781 +1442 883 227177.812693 +1443 883 -75251.5247453 +1444 883 -1038306.19785 +1445 883 -1179893.40665 +1446 883 929649.114512 +884 884 11512089.4984 +885 884 -2.98023223877e-8 +886 884 -2984228.61052 +887 884 -3391168.87559 +888 884 1.30385160446e-8 +1438 884 -953085.336629 +1439 884 -1537234.41392 +1440 884 -1196000.48814 +1441 884 227177.812693 +1442 884 217630.207531 +1443 884 55832.4159336 +1444 884 -1179893.40665 +1445 884 -1340787.9621 +1446 884 1056419.44831 +885 885 18361731.7736 +886 885 5.58793544769e-9 +887 885 7.45058059692e-9 +888 885 -125380.553907 +1438 885 -741520.302649 +1439 885 -1196000.48814 +1440 885 -2434642.38116 +1441 885 75251.5247453 +1442 885 -55832.4159336 +1443 885 -5287984.36193 +1444 885 929649.114512 +1445 885 1056419.44831 +1446 885 -2155790.5645 +886 886 11197028.9194 +887 886 7624235.1212 +888 886 -1.49011611938e-8 +889 886 -1154846.00933 +890 886 -476225.158488 +891 886 1.86264514923e-9 +1441 886 -1038306.19785 +1442 886 -1179893.40665 +1443 886 -929649.114512 +1444 886 -908773.635179 +1445 886 -175831.894858 +1446 886 -148519.569575 +1447 886 -1760951.03338 +1448 886 -726165.37459 +1449 886 1300948.03845 +887 887 6560950.4012 +888 887 -7.45058059692e-9 +889 887 -476225.158488 +890 887 -196381.508655 +891 887 9.31322574615e-10 +1441 887 -1179893.40665 +1442 887 -1340787.9621 +1443 887 -1056419.44831 +1444 887 -175831.894858 +1445 887 153537.59263 +1446 887 207978.412157 +1447 887 -726165.37459 +1448 887 -299449.639006 +1449 887 536473.417918 +888 888 21281133.3311 +891 888 4075797.18655 +1441 888 -929649.114512 +1442 888 -1056419.44831 +1443 888 -2155790.5645 +1444 888 148519.569575 +1445 888 -207978.412157 +1446 888 -7295491.64648 +1447 888 1300948.03845 +1448 888 536473.417918 +1449 888 -3164492.7709 +889 889 14087608.2601 +890 889 -7.45058059692e-9 +891 889 -7.45058059692e-9 +892 889 -1154846.00933 +893 889 476225.158488 +894 889 1.86264514923e-9 +1444 889 -1760951.03338 +1445 889 -726165.37459 +1446 889 -1300948.03845 +1447 889 -2367056.05396 +1448 889 1.11758708954e-8 +1449 889 1.86264514923e-9 +1450 889 -1760951.03338 +1451 889 726165.37459 +1452 889 1300948.03845 +890 890 2395597.11087 +891 890 7.45058059692e-9 +892 890 476225.158488 +893 890 -196381.508655 +894 890 -9.31322574615e-10 +1444 890 -726165.37459 +1445 890 -299449.639006 +1446 890 -536473.417918 +1447 890 9.31322574615e-9 +1448 890 -402517.768767 +1449 890 429178.734334 +1450 890 726165.37459 +1451 890 -299449.639006 +1452 890 -536473.417918 +891 891 25315942.1547 +894 891 4075797.18655 +1444 891 -1300948.03845 +1445 891 -536473.417918 +1446 891 -3164492.7709 +1448 891 -429178.734334 +1449 891 -10404782.7221 +1450 891 1300948.03845 +1451 891 -536473.417918 +1452 891 -3164492.7709 +892 892 11197028.9194 +893 892 -7624235.1212 +894 892 7.45058059692e-9 +895 892 -2626121.17726 +896 892 2984228.61052 +897 892 -5.58793544769e-9 +1447 892 -1760951.03338 +1448 892 726165.37459 +1449 892 -1300948.03845 +1450 892 -908773.635179 +1451 892 175831.894858 +1452 892 148519.569575 +1453 892 -1038306.19785 +1454 892 1179893.40665 +1455 892 929649.114512 +893 893 6560950.4012 +895 893 2984228.61052 +896 893 -3391168.87559 +897 893 5.58793544769e-9 +1447 893 726165.37459 +1448 893 -299449.639006 +1449 893 536473.417918 +1450 893 175831.894858 +1451 893 153537.59263 +1452 893 207978.412157 +1453 893 1179893.40665 +1454 893 -1340787.9621 +1455 893 -1056419.44831 +894 894 21281133.3311 +895 894 -1.30385160446e-8 +896 894 1.30385160446e-8 +897 894 -125380.553907 +1447 894 -1300948.03845 +1448 894 536473.417918 +1449 894 -3164492.7709 +1450 894 -148519.569575 +1451 894 -207978.412157 +1452 894 -7295491.64648 +1453 894 929649.114512 +1454 894 -1056419.44831 +1455 894 -2155790.5645 +895 895 6516876.42304 +896 895 -8531914.96891 +897 895 2.60770320892e-8 +1405 895 -1076372.38828 +1406 895 1736084.49722 +1407 895 -1.67638063431e-8 +1450 895 -1038306.19785 +1451 895 1179893.40665 +1452 895 -929649.114512 +1453 895 222027.67781 +1454 895 -227177.812693 +1455 895 75251.5247453 +1456 895 -590912.90871 +1457 895 953085.336629 +1458 895 741520.302649 +896 896 11512089.4984 +897 896 -2.98023223877e-8 +1405 896 1736084.49722 +1406 896 -2800136.28584 +1407 896 2.60770320892e-8 +1450 896 1179893.40665 +1451 896 -1340787.9621 +1452 896 1056419.44831 +1453 896 -227177.812693 +1454 896 217630.207531 +1455 896 55832.4159336 +1456 896 953085.336629 +1457 896 -1537234.41392 +1458 896 -1196000.48814 +897 897 18361731.7736 +1405 897 -2.04890966415e-8 +1406 897 3.35276126862e-8 +1407 897 1520483.39551 +1450 897 -929649.114512 +1451 897 1056419.44831 +1452 897 -2155790.5645 +1453 897 -75251.5247453 +1454 897 -55832.4159336 +1455 897 -5287984.36193 +1456 897 741520.302649 +1457 897 -1196000.48814 +1458 897 -2434642.38116 +898 898 1902223.18074 +899 898 5590205.99024 +900 898 -2.60770320892e-8 +901 898 -77037.3094959 +902 898 -210101.75317 +903 898 2.60770320892e-8 +934 898 -183325.791023 +935 898 -591373.519429 +936 898 -416676.259817 +940 898 -89281.9039062 +941 898 -261158.775213 +942 898 -27106.375452 +943 898 -246970.319543 +944 898 -673555.416935 +945 898 484442.198447 +899 899 16543837.5893 +900 899 -5.96046447754e-8 +901 899 -210101.75317 +902 899 -573004.781374 +903 899 6.70552253723e-8 +934 899 -591373.519429 +935 899 -1907656.51429 +936 899 -1344116.96715 +940 899 -261158.775213 +941 899 -769227.57174 +942 899 9164.38855486 +943 899 -673555.416935 +944 899 -1836969.31891 +945 899 1321205.99576 +900 900 30501291.4656 +901 900 1.58324837685e-8 +902 900 4.47034835815e-8 +903 900 5508023.65257 +934 900 -416676.259817 +935 900 -1344116.96715 +936 900 -2538358.87127 +940 900 27106.375452 +941 900 -9164.38855491 +942 900 -7121142.78387 +943 900 484442.198447 +944 900 1321205.99576 +945 900 -2564546.07772 +901 901 2722764.08058 +902 901 6525095.96362 +903 901 -5.58793544769e-8 +904 901 -76937.634873 +905 901 -167255.727985 +906 901 3.07336449623e-8 +940 901 -246970.319543 +941 901 -673555.416935 +942 901 -484442.198447 +943 901 -150094.32553 +944 901 -356091.902114 +945 901 -41457.2556898 +946 901 -361895.781536 +947 901 -786729.959862 +948 901 588085.337671 +902 902 15837837.4467 +903 902 -1.34110450745e-7 +904 902 -167255.727985 +905 902 -363599.408663 +906 902 6.51925802231e-8 +940 902 -673555.416935 +941 902 -1836969.31891 +942 902 -1321205.99576 +943 902 -356091.902114 +944 902 -855385.258209 +945 902 17103.8438088 +946 902 -786729.959862 +947 902 -1710282.52144 +948 902 1278446.38624 +903 903 31131706.3633 +904 903 1.95577740669e-8 +905 903 4.09781932831e-8 +906 903 5787467.22392 +940 903 -484442.198447 +941 903 -1321205.99576 +942 903 -2564546.07772 +943 903 41457.2556898 +944 903 -17103.8438089 +945 903 -7353984.06298 +946 903 588085.337671 +947 903 1278446.38624 +948 903 -2616778.13471 +904 904 4267595.81742 +905 904 7796294.34309 +906 904 -1.11758708954e-8 +907 904 -4790.05038338 +908 904 -7725.88771513 +909 904 1.30385160446e-8 +943 904 -361895.781536 +944 904 -786729.959862 +945 904 -588085.337671 +946 904 -304176.348108 +947 904 -544599.533137 +948 904 -61373.985991 +949 904 -570661.636677 +950 904 -920421.994641 +951 904 741520.302649 +905 905 14559614.5621 +906 905 -2.98023223877e-8 +907 905 -7725.88771513 +908 905 -12461.109218 +909 905 2.04890966415e-8 +943 905 -786729.959862 +944 905 -1710282.52144 +945 905 -1278446.38624 +946 905 -544599.533137 +947 905 -995761.387374 +948 905 32978.3592395 +949 905 -920421.994641 +950 905 -1484551.60426 +951 905 1196000.48814 +906 906 32450763.4987 +907 906 2.04890966415e-8 +908 906 3.35276126862e-8 +909 906 6373223.13441 +943 906 -588085.337671 +944 906 -1278446.38624 +945 906 -2616778.13471 +946 906 61373.985991 +947 906 -32978.3592395 +948 906 -7832517.9294 +949 906 741520.302649 +950 906 1196000.48814 +951 906 -2731360.75727 +907 907 6563466.92161 +908 907 8716568.29938 +909 907 -5.96046447754e-8 +910 907 -797223.130676 +911 907 -905935.375768 +912 907 1.86264514923e-8 +946 907 -570661.636677 +947 907 -920421.994641 +948 907 -741520.302649 +949 907 -175770.085153 +950 907 -298827.840693 +951 907 -75251.5247454 +952 907 -955815.974241 +953 907 -1086154.51618 +954 907 929649.114512 +908 908 11934357.6383 +909 908 -8.19563865662e-8 +910 908 -905935.375768 +911 908 -1029472.01792 +912 908 2.23517417908e-8 +946 908 -920421.994641 +947 908 -1484551.60426 +948 908 -1196000.48814 +949 908 -298827.840693 +950 908 -499398.232929 +951 908 55832.4159336 +952 908 -1086154.51618 +953 908 -1234266.49566 +954 908 1056419.44831 +909 909 30008520.3637 +910 909 -7.45058059692e-9 +911 909 -7.45058059692e-9 +912 909 4063389.39899 +946 909 -741520.302649 +947 909 -1196000.48814 +948 909 -2731360.75727 +949 909 75251.5247453 +950 909 -55832.4159336 +951 909 -6893760.43768 +952 909 929649.114512 +953 909 1056419.44831 +954 909 -2324577.161 +910 910 13789595.5493 +911 910 8526963.46189 +912 910 -7.45058059692e-9 +913 910 2189391.67462 +914 910 902841.927677 +915 910 9.31322574615e-9 +949 910 -955815.974241 +950 910 -1086154.51618 +951 910 -929649.114512 +952 910 -1698063.33368 +953 910 -676945.37624 +954 910 -148519.569575 +955 910 -1831641.90145 +956 910 -755316.248019 +957 910 1300948.03845 +911 911 6744151.21474 +912 911 1.86264514923e-8 +913 911 902841.927677 +914 911 372305.949557 +915 911 2.79396772385e-9 +949 911 -1086154.51618 +950 911 -1234266.49566 +951 911 -1056419.44831 +952 911 -676945.37624 +953 911 -252690.316209 +954 911 207978.412157 +955 911 -755316.248019 +956 911 -311470.61774 +957 911 536473.417918 +912 912 37051709.8254 +913 912 -3.72529029846e-9 +914 912 -9.31322574615e-10 +915 912 10537577.5004 +949 912 -929649.114512 +950 912 -1056419.44831 +951 912 -2324577.161 +952 912 148519.569575 +953 912 -207978.412157 +954 912 -9252258.07493 +955 912 1300948.03845 +956 912 536473.417918 +957 912 -3684182.88731 +913 913 19732335.5073 +914 913 1.49011611938e-8 +915 913 -7.45058059692e-9 +916 913 2189391.67462 +917 913 -902841.927677 +918 913 9.31322574615e-9 +952 913 -1831641.90145 +953 913 -755316.248019 +954 913 -1300948.03845 +955 913 -3460455.85927 +956 913 -2.60770320892e-8 +957 913 -5.58793544769e-9 +958 913 -1831641.90145 +959 913 755316.248019 +960 913 1300948.03845 +914 914 3355482.70929 +915 914 3.72529029846e-9 +916 914 -902841.927677 +917 914 372305.949557 +918 914 -2.79396772385e-9 +952 914 -755316.248019 +953 914 -311470.61774 +954 914 -536473.417918 +955 914 -2.23517417908e-8 +956 914 -588450.353367 +957 914 429178.734334 +958 914 755316.248019 +959 914 -311470.61774 +960 914 -536473.417918 +915 915 47437791.9854 +916 915 -3.72529029846e-9 +917 915 9.31322574615e-10 +918 915 10537577.5004 +952 915 -1300948.03845 +953 915 -536473.417918 +954 915 -3684182.88731 +955 915 3.72529029846e-9 +956 915 -429178.734334 +957 915 -12874095.835 +958 915 1300948.03845 +959 915 -536473.417918 +960 915 -3684182.88731 +916 916 13789595.5493 +917 916 -8526963.46189 +918 916 7.45058059692e-9 +919 916 -797223.130676 +920 916 905935.375768 +921 916 -1.86264514923e-9 +955 916 -1831641.90145 +956 916 755316.248019 +957 916 -1300948.03845 +958 916 -1698063.33368 +959 916 676945.37624 +960 916 148519.569575 +961 916 -955815.974241 +962 916 1086154.51618 +963 916 929649.114512 +917 917 6744151.21474 +918 917 7.45058059692e-9 +919 917 905935.375768 +920 917 -1029472.01792 +921 917 1.86264514923e-9 +955 917 755316.248019 +956 917 -311470.61774 +957 917 536473.417918 +958 917 676945.37624 +959 917 -252690.316209 +960 917 207978.412157 +961 917 1086154.51618 +962 917 -1234266.49566 +963 917 -1056419.44831 +918 918 37051709.8254 +919 918 -1.67638063431e-8 +920 918 1.86264514923e-8 +921 918 4063389.39899 +955 918 -1300948.03845 +956 918 536473.417918 +957 918 -3684182.88731 +958 918 -148519.569575 +959 918 -207978.412157 +960 918 -9252258.07493 +961 918 929649.114512 +962 918 -1056419.44831 +963 918 -2324577.161 +919 919 6563466.92161 +920 919 -8716568.29938 +921 919 5.58793544769e-8 +922 919 -4790.05038339 +923 919 7725.88771515 +924 919 -1.86264514923e-8 +958 919 -955815.974241 +959 919 1086154.51618 +960 919 -929649.114512 +961 919 -175770.085153 +962 919 298827.840693 +963 919 75251.5247453 +964 919 -570661.636677 +965 919 920421.994641 +966 919 741520.302649 +920 920 11934357.6383 +921 920 -6.70552253723e-8 +922 920 7725.88771515 +923 920 -12461.109218 +924 920 2.98023223877e-8 +958 920 1086154.51618 +959 920 -1234266.49566 +960 920 1056419.44831 +961 920 298827.840693 +962 920 -499398.232929 +963 920 55832.4159336 +964 920 920421.994641 +965 920 -1484551.60426 +966 920 -1196000.48814 +921 921 30008520.3637 +922 921 -1.11758708954e-8 +923 921 2.04890966415e-8 +924 921 6373223.13441 +958 921 -929649.114512 +959 921 1056419.44831 +960 921 -2324577.161 +961 921 -75251.5247453 +962 921 -55832.4159336 +963 921 -6893760.43768 +964 921 741520.302649 +965 921 -1196000.48814 +966 921 -2731360.75727 +922 922 4267595.81742 +923 922 -7796294.34309 +924 922 7.45058059692e-9 +925 922 -76937.634873 +926 922 167255.727985 +927 922 -1.30385160446e-8 +961 922 -570661.636677 +962 922 920421.994641 +963 922 -741520.302649 +964 922 -304176.348108 +965 922 544599.533137 +966 922 61373.985991 +967 922 -361895.781536 +968 922 786729.959862 +969 922 588085.337671 +923 923 14559614.5621 +924 923 -1.49011611938e-8 +925 923 167255.727985 +926 923 -363599.408663 +927 923 2.79396772385e-8 +961 923 920421.994641 +962 923 -1484551.60426 +963 923 1196000.48814 +964 923 544599.533137 +965 923 -995761.387374 +966 923 32978.3592395 +967 923 786729.959862 +968 923 -1710282.52144 +969 923 -1278446.38624 +924 924 32450763.4987 +925 924 -2.60770320892e-8 +926 924 5.58793544769e-8 +927 924 5787467.22392 +961 924 -741520.302649 +962 924 1196000.48814 +963 924 -2731360.75727 +964 924 -61373.985991 +965 924 -32978.3592395 +966 924 -7832517.9294 +967 924 588085.337671 +968 924 -1278446.38624 +969 924 -2616778.13471 +925 925 2722764.08058 +926 925 -6525095.96362 +927 925 4.842877388e-8 +928 925 -77037.3094959 +929 925 210101.75317 +930 925 -2.14204192162e-8 +964 925 -361895.781536 +965 925 786729.959862 +966 925 -588085.337671 +967 925 -150094.32553 +968 925 356091.902114 +969 925 41457.2556898 +970 925 -246970.319543 +971 925 673555.416935 +972 925 484442.198447 +926 926 15837837.4467 +927 926 -1.19209289551e-7 +928 926 210101.75317 +929 926 -573004.781374 +930 926 6.33299350738e-8 +964 926 786729.959862 +965 926 -1710282.52144 +966 926 1278446.38624 +967 926 356091.902114 +968 926 -855385.258209 +969 926 17103.8438088 +970 926 673555.416935 +971 926 -1836969.31891 +972 926 -1321205.99576 +927 927 31131706.3633 +928 927 -3.16649675369e-8 +929 927 8.56816768646e-8 +930 927 5508023.65257 +964 927 -588085.337671 +965 927 1278446.38624 +966 927 -2616778.13471 +967 927 -41457.2556898 +968 927 -17103.8438089 +969 927 -7353984.06298 +970 927 484442.198447 +971 927 -1321205.99576 +972 927 -2564546.07772 +928 928 1902223.18074 +929 928 -5590205.99024 +930 928 4.09781932831e-8 +967 928 -246970.319543 +968 928 673555.416935 +969 928 -484442.198447 +970 928 -89281.9039062 +971 928 261158.775213 +972 928 27106.375452 +973 928 -183325.791023 +974 928 591373.519429 +975 928 416676.259817 +929 929 16543837.5893 +930 929 -1.11758708954e-7 +967 929 673555.416935 +968 929 -1836969.31891 +969 929 1321205.99576 +970 929 261158.775213 +971 929 -769227.57174 +972 929 9164.38855485 +973 929 591373.519429 +974 929 -1907656.51429 +975 929 -1344116.96715 +930 930 30501291.4656 +967 930 -484442.198447 +968 930 1321205.99576 +969 930 -2564546.07772 +970 930 -27106.375452 +971 930 -9164.38855492 +972 930 -7121142.78387 +973 930 416676.259817 +974 930 -1344116.96715 +975 930 -2538358.87127 +931 931 983097.51761 +932 931 3993191.05725 +933 931 -4.842877388e-8 +934 931 -268071.177508 +935 931 -1017991.81332 +936 931 8.38190317154e-9 +982 931 22134.9913964 +983 931 90320.9940531 +984 931 -18909.0650259 +985 931 -144050.092155 +986 931 -547025.666412 +987 931 358351.461509 +932 932 16310675.5472 +933 932 -2.01165676117e-7 +934 932 -1017991.81332 +935 932 -3865791.69616 +936 932 2.60770320892e-8 +982 932 90320.9940531 +983 932 370626.696215 +984 932 4631.25243447 +985 932 -547025.666412 +986 932 -2077312.65726 +987 932 1360828.33484 +933 933 19175351.0344 +934 933 1.210719347e-8 +935 933 4.47034835815e-8 +936 933 1324183.34296 +982 933 18909.0650259 +983 933 -4631.25243456 +984 933 -5293544.01962 +985 933 358351.461509 +986 933 1360828.33484 +987 933 -2284273.33935 +934 934 2528206.57902 +935 934 4472508.77878 +936 934 -1.49011611938e-8 +937 934 161794.959894 +938 934 -1643725.47145 +940 934 -354732.167298 +941 934 -1144297.31386 +942 934 6.51925802231e-9 +982 934 -144050.092155 +983 934 -547025.666412 +984 934 -358351.461509 +985 934 -474420.971735 +986 934 131724.797328 +987 934 -23329.9193232 +988 934 800256.904552 +989 934 8988441.97885 +991 934 -193836.559394 +992 934 -625279.223852 +993 934 416676.259817 +935 935 16817023.5501 +936 935 -5.21540641785e-8 +937 935 2239699.42799 +938 935 -161794.959894 +940 935 -1144297.31386 +941 935 -3691281.65763 +942 935 1.86264514923e-8 +982 935 -547025.666412 +983 935 -2077312.65726 +984 935 -1360828.33484 +985 935 131724.797328 +986 935 -28174.6046026 +987 935 6684.54707693 +988 935 -6040684.68178 +989 935 -800256.904552 +991 935 -625279.223852 +992 935 -2017029.75436 +993 935 1344116.96715 +936 936 39001825.1235 +940 936 1.02445483208e-8 +941 936 3.35276126862e-8 +942 936 1397983.15475 +982 936 -358351.461509 +983 936 -1360828.33484 +984 936 -2284273.33935 +985 936 23329.9193232 +986 936 -6684.54707697 +987 936 -14301137.6219 +991 936 416676.259817 +992 936 1344116.96715 +993 936 -2296979.40748 +937 937 329627273.449 +938 937 65098710.708 +985 937 -800256.904552 +986 937 6040684.68178 +988 937 64525494.457 +989 937 -4237812.99363 +938 938 569419268.28 +985 938 -8988441.97885 +986 938 800256.904552 +988 938 -4237812.99363 +989 938 48915452.1011 +939 939 790573.476703 +990 939 -357777.777778 +940 940 1768961.88645 +941 940 5200497.8108 +942 940 -3.53902578354e-8 +943 940 -465133.879389 +944 940 -1268546.94379 +945 940 2.23517417908e-8 +985 940 -193836.559394 +986 940 -625279.223852 +987 940 -416676.259817 +991 940 24667.0078013 +992 940 73754.1287477 +993 940 -27106.375452 +994 940 -260348.273704 +995 940 -710040.746465 +996 940 484442.198447 +941 941 15396260.6937 +942 941 -1.19209289551e-7 +943 941 -1268546.94379 +944 941 -3459673.48306 +945 941 5.96046447754e-8 +985 941 -625279.223852 +986 941 -2017029.75436 +987 941 -1344116.96715 +991 941 73754.1287477 +992 941 222052.369366 +993 941 9164.38855485 +994 941 -710040.746465 +995 941 -1936474.76309 +996 941 1321205.99576 +942 942 19430253.1852 +943 942 2.14204192162e-8 +944 942 5.58793544769e-8 +945 942 1501024.18544 +985 942 -416676.259817 +986 942 -1344116.96715 +987 942 -2296979.40748 +991 942 27106.375452 +992 942 -9164.38855489 +993 942 -5492991.14412 +994 942 484442.198447 +995 942 1321205.99576 +996 942 -2315242.24089 +943 943 2497002.01247 +944 943 5989724.5569 +945 943 -5.58793544769e-8 +946 943 -645500.420471 +947 943 -1403261.78363 +948 943 2.79396772385e-8 +991 943 -260348.273704 +992 943 -710040.746465 +993 943 -484442.198447 +994 943 12227.6197724 +995 943 33038.3525625 +996 943 -41457.2556898 +997 943 -379286.632065 +998 943 -824536.156663 +999 943 588085.337671 +944 944 14552393.0113 +945 944 -1.34110450745e-7 +946 944 -1403261.78363 +947 944 -3050569.09485 +948 944 6.14672899246e-8 +991 944 -710040.746465 +992 944 -1936474.76309 +993 944 -1321205.99576 +994 944 33038.3525625 +995 944 89431.3340668 +996 944 17103.8438088 +997 944 -824536.156663 +998 944 -1792469.90579 +999 944 1278446.38624 +945 945 19697338.3132 +946 945 2.79396772385e-8 +947 945 6.14672899246e-8 +948 945 1699467.28443 +991 945 -484442.198447 +992 945 -1321205.99576 +993 945 -2315242.24089 +994 945 41457.2556898 +995 945 -17103.8438089 +996 945 -5695176.55864 +997 945 588085.337671 +998 945 1278446.38624 +999 945 -2352102.70813 +946 946 3805513.9161 +947 946 6969546.89216 +948 946 -5.21540641785e-8 +949 946 -900913.14915 +950 946 -1453085.72444 +951 946 2.23517417908e-8 +994 946 -379286.632065 +995 946 -824536.156663 +996 946 -588085.337671 +997 946 -52167.0393844 +998 946 -83826.4031559 +999 946 -61373.985991 +1000 946 -590912.90871 +1001 946 -953085.336629 +1002 946 741520.302649 +947 947 13049076.8844 +948 947 -1.04308128357e-7 +949 947 -1453085.72444 +950 947 -2343686.65232 +951 947 3.91155481339e-8 +994 947 -824536.156663 +995 947 -1792469.90579 +996 947 -1278446.38624 +997 947 -83826.4031559 +998 947 -134521.304432 +999 947 32978.3592395 +1000 947 -953085.336629 +1001 947 -1537234.41392 +1002 947 1196000.48814 +948 948 20269767.9526 +949 948 1.49011611938e-8 +950 948 2.421438694e-8 +951 948 2107431.22341 +994 948 -588085.337671 +995 948 -1278446.38624 +996 948 -2352102.70813 +997 948 61373.985991 +998 948 -32978.3592395 +999 948 -6109264.54972 +1000 948 741520.302649 +1001 948 1196000.48814 +1002 948 -2434642.38116 +949 949 6311393.43185 +950 949 8279110.50413 +951 949 -1.11758708954e-8 +952 949 -2301041.15788 +953 949 -2614819.49759 +954 949 9.31322574615e-9 +997 949 -590912.90871 +998 949 -953085.336629 +999 949 -741520.302649 +1000 949 222027.677811 +1001 949 227177.812693 +1002 949 -75251.5247453 +1003 949 -1038306.19785 +1004 949 -1179893.40665 +1005 949 929649.114512 +950 950 11193680.9464 +951 950 -1.49011611938e-8 +952 950 -2614819.49759 +953 950 -2971385.79271 +954 950 1.30385160446e-8 +997 950 -953085.336629 +998 950 -1537234.41392 +999 950 -1196000.48814 +1000 950 227177.812693 +1001 950 217630.207531 +1002 950 55832.4159336 +1003 950 -1179893.40665 +1004 950 -1340787.9621 +1005 950 1056419.44831 +951 951 19292741.7183 +952 951 1.49011611938e-8 +953 951 1.67638063431e-8 +954 951 427942.721781 +997 951 -741520.302649 +998 951 -1196000.48814 +999 951 -2434642.38116 +1000 951 75251.5247453 +1001 951 -55832.4159336 +1002 951 -5287984.36193 +1003 951 929649.114512 +1004 951 1056419.44831 +1005 951 -2155790.5645 +952 952 11173430.2083 +953 952 7495059.08719 +954 952 -3.72529029846e-8 +955 952 -678836.974673 +956 952 -279932.773061 +957 952 3.72529029846e-9 +1000 952 -1038306.19785 +1001 952 -1179893.40665 +1002 952 -929649.114512 +1003 952 -908773.635179 +1004 952 -175831.894858 +1005 952 -148519.569575 +1006 952 -1760951.03338 +1007 952 -726165.37459 +1008 952 1300948.03845 +953 953 6371949.42588 +954 953 -3.72529029846e-8 +955 953 -279932.773061 +956 953 -115436.195077 +957 953 2.79396772385e-9 +1000 953 -1179893.40665 +1001 953 -1340787.9621 +1002 953 -1056419.44831 +1003 953 -175831.894858 +1004 953 153537.59263 +1005 953 207978.412157 +1006 953 -726165.37459 +1007 953 -299449.639006 +1008 953 536473.417918 +954 954 22658086.7563 +955 954 -5.58793544769e-9 +956 954 -3.72529029846e-9 +957 954 4790763.62708 +1000 954 -929649.114512 +1001 954 -1056419.44831 +1002 954 -2155790.5645 +1003 954 148519.569575 +1004 954 -207978.412157 +1005 954 -7295491.64648 +1006 954 1300948.03845 +1007 954 536473.417918 +1008 954 -3164492.7709 +955 955 14370371.7322 +956 955 5.21540641785e-8 +957 955 -7.45058059692e-9 +958 955 -678836.974673 +959 955 279932.773061 +960 955 3.72529029846e-9 +1003 955 -1760951.03338 +1004 955 -726165.37459 +1005 955 -1300948.03845 +1006 955 -2367056.05396 +1007 955 -1.67638063431e-8 +1008 955 -5.58793544769e-9 +1009 955 -1760951.03338 +1010 955 726165.37459 +1011 955 1300948.03845 +956 956 2443681.02578 +957 956 1.49011611938e-8 +958 956 279932.773061 +959 956 -115436.195077 +960 956 -2.79396772385e-9 +1003 956 -726165.37459 +1004 956 -299449.639006 +1005 956 -536473.417918 +1006 956 -2.04890966415e-8 +1007 956 -402517.768767 +1008 956 429178.734334 +1009 956 726165.37459 +1010 956 -299449.639006 +1011 956 -536473.417918 +957 957 27394702.6193 +958 957 -5.58793544769e-9 +959 957 3.72529029846e-9 +960 957 4790763.62708 +1003 957 -1300948.03845 +1004 957 -536473.417918 +1005 957 -3164492.7709 +1007 957 -429178.734334 +1008 957 -10404782.7221 +1009 957 1300948.03845 +1010 957 -536473.417918 +1011 957 -3164492.7709 +958 958 11173430.2083 +959 958 -7495059.08719 +960 958 1.49011611938e-8 +961 958 -2301041.15788 +962 958 2614819.49759 +963 958 -7.45058059692e-9 +1006 958 -1760951.03338 +1007 958 726165.37459 +1008 958 -1300948.03845 +1009 958 -908773.635179 +1010 958 175831.894858 +1011 958 148519.569575 +1012 958 -1038306.19785 +1013 958 1179893.40665 +1014 958 929649.114512 +959 959 6371949.42588 +960 959 -1.49011611938e-8 +961 959 2614819.49759 +962 959 -2971385.79271 +963 959 7.45058059692e-9 +1006 959 726165.37459 +1007 959 -299449.639006 +1008 959 536473.417918 +1009 959 175831.894858 +1010 959 153537.59263 +1011 959 207978.412157 +1012 959 1179893.40665 +1013 959 -1340787.9621 +1014 959 -1056419.44831 +960 960 22658086.7563 +961 960 -7.45058059692e-9 +962 960 7.45058059692e-9 +963 960 427942.721781 +1006 960 -1300948.03845 +1007 960 536473.417918 +1008 960 -3164492.7709 +1009 960 -148519.569575 +1010 960 -207978.412157 +1011 960 -7295491.64648 +1012 960 929649.114512 +1013 960 -1056419.44831 +1014 960 -2155790.5645 +961 961 6311393.43185 +962 961 -8279110.50413 +964 961 -900913.14915 +965 961 1453085.72444 +966 961 -1.30385160446e-8 +1009 961 -1038306.19785 +1010 961 1179893.40665 +1011 961 -929649.114512 +1012 961 222027.677811 +1013 961 -227177.812693 +1014 961 75251.5247453 +1015 961 -590912.90871 +1016 961 953085.336629 +1017 961 741520.302649 +962 962 11193680.9464 +964 962 1453085.72444 +965 962 -2343686.65232 +966 962 1.86264514923e-8 +1009 962 1179893.40665 +1010 962 -1340787.9621 +1011 962 1056419.44831 +1012 962 -227177.812693 +1013 962 217630.207531 +1014 962 55832.4159336 +1015 962 953085.336629 +1016 962 -1537234.41392 +1017 962 -1196000.48814 +963 963 19292741.7183 +964 963 -2.60770320892e-8 +965 963 4.47034835815e-8 +966 963 2107431.22341 +1009 963 -929649.114512 +1010 963 1056419.44831 +1011 963 -2155790.5645 +1012 963 -75251.5247453 +1013 963 -55832.4159336 +1014 963 -5287984.36193 +1015 963 741520.302649 +1016 963 -1196000.48814 +1017 963 -2434642.38116 +964 964 3805513.9161 +965 964 -6969546.89216 +966 964 4.47034835815e-8 +967 964 -645500.420471 +968 964 1403261.78363 +969 964 -1.86264514923e-8 +1012 964 -590912.90871 +1013 964 953085.336629 +1014 964 -741520.302649 +1015 964 -52167.0393844 +1016 964 83826.4031559 +1017 964 61373.985991 +1018 964 -379286.632065 +1019 964 824536.156663 +1020 964 588085.337671 +965 965 13049076.8844 +966 965 -8.19563865662e-8 +967 965 1403261.78363 +968 965 -3050569.09485 +969 965 4.09781932831e-8 +1012 965 953085.336629 +1013 965 -1537234.41392 +1014 965 1196000.48814 +1015 965 83826.4031559 +1016 965 -134521.304432 +1017 965 32978.3592395 +1018 965 824536.156663 +1019 965 -1792469.90579 +1020 965 -1278446.38624 +966 966 20269767.9526 +967 966 -2.51457095146e-8 +968 966 5.40167093277e-8 +969 966 1699467.28443 +1012 966 -741520.302649 +1013 966 1196000.48814 +1014 966 -2434642.38116 +1015 966 -61373.985991 +1016 966 -32978.3592395 +1017 966 -6109264.54972 +1018 966 588085.337671 +1019 966 -1278446.38624 +1020 966 -2352102.70813 +967 967 2497002.01247 +968 967 -5989724.5569 +969 967 4.47034835815e-8 +970 967 -465133.879389 +971 967 1268546.94379 +972 967 -2.51457095146e-8 +1015 967 -379286.632065 +1016 967 824536.156663 +1017 967 -588085.337671 +1018 967 12227.6197724 +1019 967 -33038.3525625 +1020 967 41457.2556898 +1021 967 -260348.273704 +1022 967 710040.746465 +1023 967 484442.198447 +968 968 14552393.0113 +969 968 -1.19209289551e-7 +970 968 1268546.94379 +971 968 -3459673.48306 +972 968 7.07805156708e-8 +1015 968 824536.156663 +1016 968 -1792469.90579 +1017 968 1278446.38624 +1018 968 -33038.3525625 +1019 968 89431.3340668 +1020 968 17103.8438088 +1021 968 710040.746465 +1022 968 -1936474.76309 +1023 968 -1321205.99576 +969 969 19697338.3132 +970 969 -2.79396772385e-8 +971 969 7.82310962677e-8 +972 969 1501024.18544 +1015 969 -588085.337671 +1016 969 1278446.38624 +1017 969 -2352102.70813 +1018 969 -41457.2556898 +1019 969 -17103.8438089 +1020 969 -5695176.55864 +1021 969 484442.198447 +1022 969 -1321205.99576 +1023 969 -2315242.24089 +970 970 1768961.88645 +971 970 -5200497.8108 +972 970 4.28408384323e-8 +973 970 -354732.167298 +974 970 1144297.31386 +975 970 -1.02445483208e-8 +1018 970 -260348.273704 +1019 970 710040.746465 +1020 970 -484442.198447 +1021 970 24667.0078013 +1022 970 -73754.1287477 +1023 970 27106.375452 +1024 970 -193836.559394 +1025 970 625279.223852 +1026 970 416676.259817 +971 971 15396260.6937 +972 971 -1.26659870148e-7 +973 971 1144297.31386 +974 971 -3691281.65763 +975 971 4.09781932831e-8 +1018 971 710040.746465 +1019 971 -1936474.76309 +1020 971 1321205.99576 +1021 971 -73754.1287477 +1022 971 222052.369366 +1023 971 9164.38855486 +1024 971 625279.223852 +1025 971 -2017029.75436 +1026 971 -1344116.96715 +972 972 19430253.1852 +973 972 -7.45058059692e-9 +974 972 2.23517417908e-8 +975 972 1397983.15475 +1018 972 -484442.198447 +1019 972 1321205.99576 +1020 972 -2315242.24089 +1021 972 -27106.375452 +1022 972 -9164.38855491 +1023 972 -5492991.14412 +1024 972 416676.259817 +1025 972 -1344116.96715 +1026 972 -2296979.40748 +973 973 2528206.97157 +974 973 -4472509.50178 +975 973 1.86264514923e-8 +976 973 -161793.620953 +977 973 -1643724.74446 +979 973 -268071.177508 +980 973 1017991.81332 +981 973 -1.02445483208e-8 +1021 973 -193836.559394 +1022 973 625279.223852 +1023 973 -416676.259817 +1024 973 -474421.1715 +1025 973 -131724.429408 +1026 973 23329.9193232 +1027 973 -800250.281991 +1028 973 8988445.57462 +1030 973 -144050.092155 +1031 973 547025.666412 +1032 973 358351.461509 +974 974 16817023.1576 +975 974 -6.70552253723e-8 +976 974 2239700.15498 +977 974 161793.620953 +979 974 1017991.81332 +980 974 -3865791.69616 +981 974 4.09781932831e-8 +1021 974 625279.223852 +1022 974 -2017029.75436 +1023 974 1344116.96715 +1024 974 -131724.429408 +1025 974 -28174.4048378 +1026 974 6684.54707694 +1027 974 -6040681.08601 +1028 974 800250.281991 +1030 974 547025.666412 +1031 974 -2077312.65726 +1032 974 -1360828.33484 +975 975 39001825.1235 +979 975 -1.02445483208e-8 +980 975 3.35276126862e-8 +981 975 1324183.34296 +1021 975 -416676.259817 +1022 975 1344116.96715 +1023 975 -2296979.40748 +1024 975 -23329.9193232 +1025 975 -6684.54707699 +1026 975 -14301137.6219 +1030 975 358351.461509 +1031 975 -1360828.33484 +1032 975 -2284273.33935 +976 976 329626980.943 +977 976 -65098171.9808 +1024 976 800250.281991 +1025 976 6040681.08601 +1027 976 64525513.4986 +1028 976 4237777.92342 +977 977 569419560.785 +1024 977 -8988445.57462 +1025 977 -800250.281991 +1027 977 4237777.92342 +1028 977 48915433.0595 +978 978 790573.476703 +1029 978 -357777.777778 +979 979 983097.51761 +980 979 -3993191.05725 +981 979 6.14672899246e-8 +1024 979 -144050.092155 +1025 979 547025.666412 +1026 979 -358351.461509 +1030 979 22134.9913964 +1031 979 -90320.9940531 +1032 979 18909.0650259 +980 980 16310675.5472 +981 980 -2.53319740295e-7 +1024 980 547025.666412 +1025 980 -2077312.65726 +1026 980 1360828.33484 +1030 980 -90320.9940531 +1031 980 370626.696215 +1032 980 4631.25243446 +981 981 19175351.0344 +1024 981 -358351.461509 +1025 981 1360828.33484 +1026 981 -2284273.33935 +1030 981 -18909.0650259 +1031 981 -4631.25243459 +1032 981 -5293544.01962 +982 982 1011851.60912 +983 982 4110093.43272 +984 982 -4.65661287308e-8 +985 982 -311975.890718 +986 982 -1184718.57235 +987 982 1.02445483208e-8 +1033 982 22134.9913964 +1034 982 90320.9940531 +1035 982 -18909.0650259 +1036 982 -144050.092155 +1037 982 -547025.666412 +1038 982 358351.461509 +983 983 16788621.6609 +984 983 -2.01165676117e-7 +985 983 -1184718.57235 +986 983 -4498931.28739 +987 983 3.35276126862e-8 +1033 983 90320.9940531 +1034 983 370626.696215 +1035 983 4631.25243447 +1036 983 -547025.666412 +1037 983 -2077312.65726 +1038 983 1360828.33484 +984 984 18240145.4814 +985 984 1.02445483208e-8 +986 984 3.35276126862e-8 +987 984 758227.341129 +1033 984 18909.0650259 +1034 984 -4631.25243456 +1035 984 -5293544.01962 +1036 984 358351.461509 +1037 984 1360828.33484 +1038 984 -2284273.33935 +985 985 2350262.38096 +986 985 4600302.12491 +987 985 -2.421438694e-8 +988 985 2.98023223877e-8 +989 985 -1.19209289551e-7 +991 985 -413671.243673 +992 985 -1334423.36669 +993 985 1.58324837685e-8 +1033 985 -144050.092155 +1034 985 -547025.666412 +1035 985 -358351.461509 +1036 985 -474420.971735 +1037 985 131724.797328 +1038 985 -23329.9193232 +1039 985 800256.904552 +1040 985 8988441.97885 +1042 985 -193836.559394 +1043 985 -625279.223852 +1044 985 416676.259817 +986 986 17048556.8253 +987 986 -8.19563865662e-8 +988 986 5.96046447754e-8 +991 986 -1334423.36669 +992 986 -4304591.50545 +993 986 4.842877388e-8 +1033 986 -547025.666412 +1034 986 -2077312.65726 +1035 986 -1360828.33484 +1036 986 131724.797328 +1037 986 -28174.6046026 +1038 986 6684.54707693 +1039 986 -6040684.68178 +1040 986 -800256.904552 +1042 986 -625279.223852 +1043 986 -2017029.75436 +1044 986 1344116.96715 +987 987 36176122.0894 +991 987 1.39698386192e-8 +992 987 4.09781932831e-8 +993 987 830431.306933 +1033 987 -358351.461509 +1034 987 -1360828.33484 +1035 987 -2284273.33935 +1036 987 23329.9193232 +1037 987 -6684.54707697 +1038 987 -14301137.6219 +1042 987 416676.259817 +1043 987 1344116.96715 +1044 987 -2296979.40748 +988 988 305878308.174 +989 988 66094123.115 +1036 988 -800256.904552 +1037 988 6040684.68178 +1039 988 64525494.457 +1040 988 -4237812.99363 +989 989 549336918.275 +1036 989 -8988441.97885 +1037 989 800256.904552 +1039 989 -4237812.99363 +1040 989 48915452.1011 +990 990 715555.555556 +1041 990 -357777.777778 +991 991 1816739.3315 +992 991 5341279.87864 +993 991 -3.72529029846e-8 +994 991 -544032.43723 +995 991 -1483724.82881 +996 991 2.23517417908e-8 +1036 991 -193836.559394 +1037 991 -625279.223852 +1038 991 -416676.259817 +1042 991 24667.0078013 +1043 991 73754.1287477 +1044 991 -27106.375452 +1045 991 -260348.273704 +1046 991 -710040.746465 +1047 991 484442.198447 +992 992 15814018.062 +993 992 -1.19209289551e-7 +994 992 -1483724.82881 +995 992 -4046522.26039 +996 992 5.96046447754e-8 +1036 992 -625279.223852 +1037 992 -2017029.75436 +1038 992 -1344116.96715 +1042 992 73754.1287477 +1043 992 222052.369366 +1044 992 9164.38855485 +1045 992 -710040.746465 +1046 992 -1936474.76309 +1047 992 1321205.99576 +993 993 18448886.5844 +994 993 1.95577740669e-8 +995 993 5.21540641785e-8 +996 993 931107.693646 +1036 993 -416676.259817 +1037 993 -1344116.96715 +1038 993 -2296979.40748 +1042 993 27106.375452 +1043 993 -9164.38855489 +1044 993 -5492991.14412 +1045 993 484442.198447 +1046 993 1321205.99576 +1047 993 -2315242.24089 +994 994 2558539.62181 +995 994 6138307.60949 +996 994 -5.21540641785e-8 +997 994 -759692.612592 +998 994 -1651505.67955 +999 994 2.98023223877e-8 +1042 994 -260348.273704 +1043 994 -710040.746465 +1044 994 -484442.198447 +1045 994 12227.6197724 +1046 994 33038.3525625 +1047 994 -41457.2556898 +1048 994 -379286.632065 +1049 994 -824536.156663 +1050 994 588085.337671 +995 995 14915778.6682 +996 995 -1.26659870148e-7 +997 995 -1651505.67955 +998 995 -3590229.73815 +999 995 6.33299350738e-8 +1042 995 -710040.746465 +1043 995 -1936474.76309 +1044 995 -1321205.99576 +1045 995 33038.3525625 +1046 995 89431.3340668 +1047 995 17103.8438088 +1048 995 -824536.156663 +1049 995 -1792469.90579 +1050 995 1278446.38624 +996 996 18669379.7869 +997 996 2.79396772385e-8 +998 996 6.14672899246e-8 +999 996 1124555.53479 +1042 996 -484442.198447 +1043 996 -1321205.99576 +1044 996 -2315242.24089 +1045 996 41457.2556898 +1046 996 -17103.8438089 +1047 996 -5695176.55864 +1048 996 588085.337671 +1049 996 1278446.38624 +1050 996 -2352102.70813 +997 997 3880798.16119 +998 997 7110485.96967 +999 997 -4.842877388e-8 +1000 997 -1076372.38828 +1001 997 -1736084.49722 +1002 997 2.60770320892e-8 +1045 997 -379286.632065 +1046 997 -824536.156663 +1047 997 -588085.337671 +1048 997 -52167.0393844 +1049 997 -83826.403156 +1050 997 -61373.985991 +1051 997 -590912.90871 +1052 997 -953085.336629 +1053 997 741520.302649 +998 998 13318817.2723 +999 998 -9.685754776e-8 +1000 998 -1736084.49722 +1001 998 -2800136.28584 +1002 998 4.09781932831e-8 +1045 998 -824536.156663 +1046 998 -1792469.90579 +1047 998 -1278446.38624 +1048 998 -83826.403156 +1049 998 -134521.304432 +1050 998 32978.3592395 +1051 998 -953085.336629 +1052 998 -1537234.41392 +1053 998 1196000.48814 +999 999 19146980.3477 +1000 999 1.67638063431e-8 +1001 999 2.98023223877e-8 +1002 999 1520483.39551 +1045 999 -588085.337671 +1046 999 -1278446.38624 +1047 999 -2352102.70813 +1048 999 61373.985991 +1049 999 -32978.3592395 +1050 999 -6109264.54972 +1051 999 741520.302649 +1052 999 1196000.48814 +1053 999 -2434642.38116 +1000 1000 6516876.42304 +1001 1000 8531914.96891 +1002 1000 -1.11758708954e-8 +1003 1000 -2626121.17726 +1004 1000 -2984228.61052 +1005 1000 9.31322574615e-9 +1048 1000 -590912.90871 +1049 1000 -953085.336629 +1050 1000 -741520.302649 +1051 1000 222027.67781 +1052 1000 227177.812693 +1053 1000 -75251.5247453 +1054 1000 -1038306.19785 +1055 1000 -1179893.40665 +1056 1000 929649.114512 +1001 1001 11512089.4984 +1002 1001 -2.23517417908e-8 +1003 1001 -2984228.61052 +1004 1001 -3391168.87559 +1005 1001 1.11758708954e-8 +1048 1001 -953085.336629 +1049 1001 -1537234.41392 +1050 1001 -1196000.48814 +1051 1001 227177.812693 +1052 1001 217630.207531 +1053 1001 55832.4159336 +1054 1001 -1179893.40665 +1055 1001 -1340787.9621 +1056 1001 1056419.44831 +1002 1002 18361731.7736 +1003 1002 3.72529029846e-9 +1004 1002 5.58793544769e-9 +1005 1002 -125380.553908 +1048 1002 -741520.302649 +1049 1002 -1196000.48814 +1050 1002 -2434642.38116 +1051 1002 75251.5247453 +1052 1002 -55832.4159336 +1053 1002 -5287984.36193 +1054 1002 929649.114512 +1055 1002 1056419.44831 +1056 1002 -2155790.5645 +1003 1003 11197028.9194 +1004 1003 7624235.1212 +1005 1003 -1.49011611938e-8 +1006 1003 -1154846.00933 +1007 1003 -476225.158488 +1008 1003 9.31322574615e-9 +1051 1003 -1038306.19785 +1052 1003 -1179893.40665 +1053 1003 -929649.114512 +1054 1003 -908773.635179 +1055 1003 -175831.894858 +1056 1003 -148519.569575 +1057 1003 -1760951.03338 +1058 1003 -726165.37459 +1059 1003 1300948.03845 +1004 1004 6560950.4012 +1005 1004 -7.45058059692e-9 +1006 1004 -476225.158488 +1007 1004 -196381.508655 +1008 1004 3.72529029846e-9 +1051 1004 -1179893.40665 +1052 1004 -1340787.9621 +1053 1004 -1056419.44831 +1054 1004 -175831.894858 +1055 1004 153537.59263 +1056 1004 207978.412157 +1057 1004 -726165.37459 +1058 1004 -299449.639006 +1059 1004 536473.417918 +1005 1005 21281133.3311 +1006 1005 1.86264514923e-9 +1007 1005 9.31322574615e-10 +1008 1005 4075797.18655 +1051 1005 -929649.114512 +1052 1005 -1056419.44831 +1053 1005 -2155790.5645 +1054 1005 148519.569575 +1055 1005 -207978.412157 +1056 1005 -7295491.64648 +1057 1005 1300948.03845 +1058 1005 536473.417918 +1059 1005 -3164492.7709 +1006 1006 14087608.2601 +1007 1006 4.47034835815e-8 +1008 1006 -7.45058059692e-9 +1009 1006 -1154846.00933 +1010 1006 476225.158488 +1011 1006 9.31322574615e-9 +1054 1006 -1760951.03338 +1055 1006 -726165.37459 +1056 1006 -1300948.03845 +1057 1006 -2367056.05396 +1058 1006 -1.86264514923e-8 +1059 1006 -5.58793544769e-9 +1060 1006 -1760951.03338 +1061 1006 726165.37459 +1062 1006 1300948.03845 +1007 1007 2395597.11087 +1008 1007 7.45058059692e-9 +1009 1007 476225.158488 +1010 1007 -196381.508655 +1011 1007 -3.72529029846e-9 +1054 1007 -726165.37459 +1055 1007 -299449.639006 +1056 1007 -536473.417918 +1057 1007 -2.04890966415e-8 +1058 1007 -402517.768767 +1059 1007 429178.734334 +1060 1007 726165.37459 +1061 1007 -299449.639006 +1062 1007 -536473.417918 +1008 1008 25315942.1547 +1009 1008 1.86264514923e-9 +1010 1008 -9.31322574615e-10 +1011 1008 4075797.18655 +1054 1008 -1300948.03845 +1055 1008 -536473.417918 +1056 1008 -3164492.7709 +1057 1008 3.72529029846e-9 +1058 1008 -429178.734334 +1059 1008 -10404782.7221 +1060 1008 1300948.03845 +1061 1008 -536473.417918 +1062 1008 -3164492.7709 +1009 1009 11197028.9194 +1010 1009 -7624235.1212 +1011 1009 1.49011611938e-8 +1012 1009 -2626121.17726 +1013 1009 2984228.61052 +1014 1009 -9.31322574615e-9 +1057 1009 -1760951.03338 +1058 1009 726165.37459 +1059 1009 -1300948.03845 +1060 1009 -908773.635179 +1061 1009 175831.894858 +1062 1009 148519.569575 +1063 1009 -1038306.19785 +1064 1009 1179893.40665 +1065 1009 929649.114512 +1010 1010 6560950.4012 +1011 1010 -7.45058059692e-9 +1012 1010 2984228.61052 +1013 1010 -3391168.87559 +1014 1010 9.31322574615e-9 +1057 1010 726165.37459 +1058 1010 -299449.639006 +1059 1010 536473.417918 +1060 1010 175831.894858 +1061 1010 153537.59263 +1062 1010 207978.412157 +1063 1010 1179893.40665 +1064 1010 -1340787.9621 +1065 1010 -1056419.44831 +1011 1011 21281133.3311 +1012 1011 -1.67638063431e-8 +1013 1011 1.67638063431e-8 +1014 1011 -125380.553908 +1057 1011 -1300948.03845 +1058 1011 536473.417918 +1059 1011 -3164492.7709 +1060 1011 -148519.569575 +1061 1011 -207978.412157 +1062 1011 -7295491.64648 +1063 1011 929649.114512 +1064 1011 -1056419.44831 +1065 1011 -2155790.5645 +1012 1012 6516876.42304 +1013 1012 -8531914.96891 +1014 1012 2.98023223877e-8 +1015 1012 -1076372.38828 +1016 1012 1736084.49722 +1017 1012 -1.30385160446e-8 +1060 1012 -1038306.19785 +1061 1012 1179893.40665 +1062 1012 -929649.114512 +1063 1012 222027.677811 +1064 1012 -227177.812693 +1065 1012 75251.5247453 +1066 1012 -590912.90871 +1067 1012 953085.336629 +1068 1012 741520.302649 +1013 1013 11512089.4984 +1014 1013 -3.72529029846e-8 +1015 1013 1736084.49722 +1016 1013 -2800136.28584 +1017 1013 2.23517417908e-8 +1060 1013 1179893.40665 +1061 1013 -1340787.9621 +1062 1013 1056419.44831 +1063 1013 -227177.812693 +1064 1013 217630.207531 +1065 1013 55832.4159336 +1066 1013 953085.336629 +1067 1013 -1537234.41392 +1068 1013 -1196000.48814 +1014 1014 18361731.7736 +1015 1014 -2.23517417908e-8 +1016 1014 3.53902578354e-8 +1017 1014 1520483.39551 +1060 1014 -929649.114512 +1061 1014 1056419.44831 +1062 1014 -2155790.5645 +1063 1014 -75251.5247453 +1064 1014 -55832.4159336 +1065 1014 -5287984.36193 +1066 1014 741520.302649 +1067 1014 -1196000.48814 +1068 1014 -2434642.38116 +1015 1015 3880798.16119 +1016 1015 -7110485.96967 +1017 1015 4.09781932831e-8 +1018 1015 -759692.612592 +1019 1015 1651505.67955 +1020 1015 -1.86264514923e-8 +1063 1015 -590912.90871 +1064 1015 953085.336629 +1065 1015 -741520.302649 +1066 1015 -52167.0393844 +1067 1015 83826.403156 +1068 1015 61373.985991 +1069 1015 -379286.632065 +1070 1015 824536.156663 +1071 1015 588085.337671 +1016 1016 13318817.2723 +1017 1016 -7.45058059692e-8 +1018 1016 1651505.67955 +1019 1016 -3590229.73815 +1020 1016 4.09781932831e-8 +1063 1016 953085.336629 +1064 1016 -1537234.41392 +1065 1016 1196000.48814 +1066 1016 83826.403156 +1067 1016 -134521.304432 +1068 1016 32978.3592395 +1069 1016 824536.156663 +1070 1016 -1792469.90579 +1071 1016 -1278446.38624 +1017 1017 19146980.3477 +1018 1017 -2.32830643654e-8 +1019 1017 5.02914190292e-8 +1020 1017 1124555.53479 +1063 1017 -741520.302649 +1064 1017 1196000.48814 +1065 1017 -2434642.38116 +1066 1017 -61373.985991 +1067 1017 -32978.3592395 +1068 1017 -6109264.54972 +1069 1017 588085.337671 +1070 1017 -1278446.38624 +1071 1017 -2352102.70813 +1018 1018 2558539.62181 +1019 1018 -6138307.60949 +1020 1018 4.09781932831e-8 +1021 1018 -544032.43723 +1022 1018 1483724.82881 +1023 1018 -2.51457095146e-8 +1066 1018 -379286.632065 +1067 1018 824536.156663 +1068 1018 -588085.337671 +1069 1018 12227.6197724 +1070 1018 -33038.3525625 +1071 1018 41457.2556898 +1072 1018 -260348.273704 +1073 1018 710040.746465 +1074 1018 484442.198447 +1019 1019 14915778.6682 +1020 1019 -1.04308128357e-7 +1021 1019 1483724.82881 +1022 1019 -4046522.26039 +1023 1019 7.07805156708e-8 +1066 1019 824536.156663 +1067 1019 -1792469.90579 +1068 1019 1278446.38624 +1069 1019 -33038.3525625 +1070 1019 89431.3340668 +1071 1019 17103.8438088 +1072 1019 710040.746465 +1073 1019 -1936474.76309 +1074 1019 -1321205.99576 +1020 1020 18669379.7869 +1021 1020 -2.79396772385e-8 +1022 1020 7.82310962677e-8 +1023 1020 931107.693646 +1066 1020 -588085.337671 +1067 1020 1278446.38624 +1068 1020 -2352102.70813 +1069 1020 -41457.2556898 +1070 1020 -17103.8438089 +1071 1020 -5695176.55864 +1072 1020 484442.198447 +1073 1020 -1321205.99576 +1074 1020 -2315242.24089 +1021 1021 1816739.3315 +1022 1021 -5341279.87864 +1023 1021 3.72529029846e-8 +1024 1021 -413671.243673 +1025 1021 1334423.36669 +1026 1021 -6.51925802231e-9 +1069 1021 -260348.273704 +1070 1021 710040.746465 +1071 1021 -484442.198447 +1072 1021 24667.0078013 +1073 1021 -73754.1287477 +1074 1021 27106.375452 +1075 1021 -193836.559394 +1076 1021 625279.223852 +1077 1021 416676.259817 +1022 1022 15814018.062 +1023 1022 -1.11758708954e-7 +1024 1022 1334423.36669 +1025 1022 -4304591.50545 +1026 1022 2.60770320892e-8 +1069 1022 710040.746465 +1070 1022 -1936474.76309 +1071 1022 1321205.99576 +1072 1022 -73754.1287477 +1073 1022 222052.369366 +1074 1022 9164.38855486 +1075 1022 625279.223852 +1076 1022 -2017029.75436 +1077 1022 -1344116.96715 +1023 1023 18448886.5844 +1024 1023 -1.11758708954e-8 +1025 1023 3.35276126862e-8 +1026 1023 830431.306933 +1069 1023 -484442.198447 +1070 1023 1321205.99576 +1071 1023 -2315242.24089 +1072 1023 -27106.375452 +1073 1023 -9164.38855491 +1074 1023 -5492991.14412 +1075 1023 416676.259817 +1076 1023 -1344116.96715 +1077 1023 -2296979.40748 +1024 1024 2350262.78049 +1025 1024 -4600302.86075 +1026 1024 1.86264514923e-8 +1027 1024 -7.45058059692e-9 +1030 1024 -311975.890718 +1031 1024 1184718.57235 +1032 1024 -9.31322574615e-9 +1072 1024 -193836.559394 +1073 1024 625279.223852 +1074 1024 -416676.259817 +1075 1024 -474421.1715 +1076 1024 -131724.429408 +1077 1024 23329.9193232 +1078 1024 -800250.281991 +1079 1024 8988445.57462 +1081 1024 -144050.092155 +1082 1024 547025.666412 +1083 1024 358351.461509 +1025 1025 17048556.4258 +1026 1025 -6.70552253723e-8 +1027 1025 5.96046447754e-8 +1028 1025 7.45058059692e-9 +1030 1025 1184718.57235 +1031 1025 -4498931.28739 +1032 1025 3.35276126862e-8 +1072 1025 625279.223852 +1073 1025 -2017029.75436 +1074 1025 1344116.96715 +1075 1025 -131724.429408 +1076 1025 -28174.4048378 +1077 1025 6684.54707694 +1078 1025 -6040681.08601 +1079 1025 800250.281991 +1081 1025 547025.666412 +1082 1025 -2077312.65726 +1083 1025 -1360828.33484 +1026 1026 36176122.0894 +1030 1026 -1.30385160446e-8 +1031 1026 4.47034835815e-8 +1032 1026 758227.341129 +1072 1026 -416676.259817 +1073 1026 1344116.96715 +1074 1026 -2296979.40748 +1075 1026 -23329.9193232 +1076 1026 -6684.54707699 +1077 1026 -14301137.6219 +1081 1026 358351.461509 +1082 1026 -1360828.33484 +1083 1026 -2284273.33935 +1027 1027 305878011.196 +1028 1027 -66093576.1502 +1075 1027 800250.281991 +1076 1027 6040681.08601 +1078 1027 64525513.4986 +1079 1027 4237777.92342 +1028 1028 549337215.254 +1075 1028 -8988445.57462 +1076 1028 -800250.281991 +1078 1028 4237777.92342 +1079 1028 48915433.0595 +1029 1029 715555.555556 +1080 1029 -357777.777778 +1030 1030 1011851.60912 +1031 1030 -4110093.43272 +1032 1030 6.14672899246e-8 +1075 1030 -144050.092155 +1076 1030 547025.666412 +1077 1030 -358351.461509 +1081 1030 22134.9913964 +1082 1030 -90320.9940531 +1083 1030 18909.0650259 +1031 1031 16788621.6609 +1032 1031 -2.53319740295e-7 +1075 1031 547025.666412 +1076 1031 -2077312.65726 +1077 1031 1360828.33484 +1081 1031 -90320.9940531 +1082 1031 370626.696215 +1083 1031 4631.25243446 +1032 1032 18240145.4814 +1075 1032 -358351.461509 +1076 1032 1360828.33484 +1077 1032 -2284273.33935 +1081 1032 -18909.0650259 +1082 1032 -4631.25243459 +1083 1032 -5293544.01962 +1033 1033 1011851.60912 +1034 1033 4110093.43272 +1035 1033 -4.65661287308e-8 +1036 1033 -311975.890718 +1037 1033 -1184718.57235 +1038 1033 1.02445483208e-8 +1084 1033 22134.9913964 +1085 1033 90320.9940531 +1086 1033 -18909.0650259 +1087 1033 -144050.092155 +1088 1033 -547025.666412 +1089 1033 358351.461509 +1034 1034 16788621.6609 +1035 1034 -2.01165676117e-7 +1036 1034 -1184718.57235 +1037 1034 -4498931.28739 +1038 1034 3.35276126862e-8 +1084 1034 90320.9940531 +1085 1034 370626.696215 +1086 1034 4631.25243447 +1087 1034 -547025.666412 +1088 1034 -2077312.65726 +1089 1034 1360828.33484 +1035 1035 18240145.4814 +1036 1035 1.02445483208e-8 +1037 1035 3.35276126862e-8 +1038 1035 758227.341129 +1084 1035 18909.0650259 +1085 1035 -4631.25243456 +1086 1035 -5293544.01962 +1087 1035 358351.461509 +1088 1035 1360828.33484 +1089 1035 -2284273.33935 +1036 1036 2350262.38096 +1037 1036 4600302.12491 +1038 1036 -2.421438694e-8 +1039 1036 2.98023223877e-8 +1040 1036 -1.19209289551e-7 +1042 1036 -413671.243673 +1043 1036 -1334423.36669 +1044 1036 1.58324837685e-8 +1084 1036 -144050.092155 +1085 1036 -547025.666412 +1086 1036 -358351.461509 +1087 1036 -474420.971735 +1088 1036 131724.797328 +1089 1036 -23329.9193232 +1090 1036 800256.904552 +1091 1036 8988441.97885 +1093 1036 -193836.559394 +1094 1036 -625279.223852 +1095 1036 416676.259817 +1037 1037 17048556.8253 +1038 1037 -8.19563865662e-8 +1039 1037 5.96046447754e-8 +1042 1037 -1334423.36669 +1043 1037 -4304591.50545 +1044 1037 4.842877388e-8 +1084 1037 -547025.666412 +1085 1037 -2077312.65726 +1086 1037 -1360828.33484 +1087 1037 131724.797328 +1088 1037 -28174.6046026 +1089 1037 6684.54707693 +1090 1037 -6040684.68178 +1091 1037 -800256.904552 +1093 1037 -625279.223852 +1094 1037 -2017029.75436 +1095 1037 1344116.96715 +1038 1038 36176122.0894 +1042 1038 1.39698386192e-8 +1043 1038 4.09781932831e-8 +1044 1038 830431.306933 +1084 1038 -358351.461509 +1085 1038 -1360828.33484 +1086 1038 -2284273.33935 +1087 1038 23329.9193232 +1088 1038 -6684.54707697 +1089 1038 -14301137.6219 +1093 1038 416676.259817 +1094 1038 1344116.96715 +1095 1038 -2296979.40748 +1039 1039 305878308.174 +1040 1039 66094123.115 +1087 1039 -800256.904552 +1088 1039 6040684.68178 +1090 1039 64525494.457 +1091 1039 -4237812.99363 +1040 1040 549336918.275 +1087 1040 -8988441.97885 +1088 1040 800256.904552 +1090 1040 -4237812.99363 +1091 1040 48915452.1011 +1041 1041 715555.555556 +1092 1041 -357777.777778 +1042 1042 1816739.3315 +1043 1042 5341279.87864 +1044 1042 -3.72529029846e-8 +1045 1042 -544032.43723 +1046 1042 -1483724.82881 +1047 1042 2.23517417908e-8 +1087 1042 -193836.559394 +1088 1042 -625279.223852 +1089 1042 -416676.259817 +1093 1042 24667.0078013 +1094 1042 73754.1287477 +1095 1042 -27106.375452 +1096 1042 -260348.273704 +1097 1042 -710040.746465 +1098 1042 484442.198447 +1043 1043 15814018.062 +1044 1043 -1.19209289551e-7 +1045 1043 -1483724.82881 +1046 1043 -4046522.26039 +1047 1043 5.96046447754e-8 +1087 1043 -625279.223852 +1088 1043 -2017029.75436 +1089 1043 -1344116.96715 +1093 1043 73754.1287477 +1094 1043 222052.369366 +1095 1043 9164.38855485 +1096 1043 -710040.746465 +1097 1043 -1936474.76309 +1098 1043 1321205.99576 +1044 1044 18448886.5844 +1045 1044 1.95577740669e-8 +1046 1044 5.21540641785e-8 +1047 1044 931107.693646 +1087 1044 -416676.259817 +1088 1044 -1344116.96715 +1089 1044 -2296979.40748 +1093 1044 27106.375452 +1094 1044 -9164.38855489 +1095 1044 -5492991.14412 +1096 1044 484442.198447 +1097 1044 1321205.99576 +1098 1044 -2315242.24089 +1045 1045 2558539.62181 +1046 1045 6138307.60949 +1047 1045 -5.21540641785e-8 +1048 1045 -759692.612592 +1049 1045 -1651505.67955 +1050 1045 2.98023223877e-8 +1093 1045 -260348.273704 +1094 1045 -710040.746465 +1095 1045 -484442.198447 +1096 1045 12227.6197724 +1097 1045 33038.3525625 +1098 1045 -41457.2556898 +1099 1045 -379286.632065 +1100 1045 -824536.156663 +1101 1045 588085.337671 +1046 1046 14915778.6682 +1047 1046 -1.26659870148e-7 +1048 1046 -1651505.67955 +1049 1046 -3590229.73815 +1050 1046 6.33299350738e-8 +1093 1046 -710040.746465 +1094 1046 -1936474.76309 +1095 1046 -1321205.99576 +1096 1046 33038.3525625 +1097 1046 89431.3340668 +1098 1046 17103.8438088 +1099 1046 -824536.156663 +1100 1046 -1792469.90579 +1101 1046 1278446.38624 +1047 1047 18669379.7869 +1048 1047 2.88709998131e-8 +1049 1047 6.14672899246e-8 +1050 1047 1124555.53479 +1093 1047 -484442.198447 +1094 1047 -1321205.99576 +1095 1047 -2315242.24089 +1096 1047 41457.2556898 +1097 1047 -17103.8438089 +1098 1047 -5695176.55864 +1099 1047 588085.337671 +1100 1047 1278446.38624 +1101 1047 -2352102.70813 +1048 1048 3880798.16119 +1049 1048 7110485.96967 +1050 1048 -4.842877388e-8 +1051 1048 -1076372.38828 +1052 1048 -1736084.49722 +1053 1048 2.60770320892e-8 +1096 1048 -379286.632065 +1097 1048 -824536.156663 +1098 1048 -588085.337671 +1099 1048 -52167.0393844 +1100 1048 -83826.403156 +1101 1048 -61373.985991 +1102 1048 -590912.90871 +1103 1048 -953085.336629 +1104 1048 741520.302649 +1049 1049 13318817.2723 +1050 1049 -9.685754776e-8 +1051 1049 -1736084.49722 +1052 1049 -2800136.28584 +1053 1049 4.09781932831e-8 +1096 1049 -824536.156663 +1097 1049 -1792469.90579 +1098 1049 -1278446.38624 +1099 1049 -83826.403156 +1100 1049 -134521.304432 +1101 1049 32978.3592395 +1102 1049 -953085.336629 +1103 1049 -1537234.41392 +1104 1049 1196000.48814 +1050 1050 19146980.3477 +1051 1050 1.86264514923e-8 +1052 1050 3.16649675369e-8 +1053 1050 1520483.39551 +1096 1050 -588085.337671 +1097 1050 -1278446.38624 +1098 1050 -2352102.70813 +1099 1050 61373.985991 +1100 1050 -32978.3592395 +1101 1050 -6109264.54972 +1102 1050 741520.302649 +1103 1050 1196000.48814 +1104 1050 -2434642.38116 +1051 1051 6516876.42304 +1052 1051 8531914.96891 +1053 1051 -1.49011611938e-8 +1054 1051 -2626121.17726 +1055 1051 -2984228.61052 +1056 1051 9.31322574615e-9 +1099 1051 -590912.90871 +1100 1051 -953085.336629 +1101 1051 -741520.302649 +1102 1051 222027.67781 +1103 1051 227177.812693 +1104 1051 -75251.5247453 +1105 1051 -1038306.19785 +1106 1051 -1179893.40665 +1107 1051 929649.114512 +1052 1052 11512089.4984 +1053 1052 -2.23517417908e-8 +1054 1052 -2984228.61052 +1055 1052 -3391168.87559 +1056 1052 1.11758708954e-8 +1099 1052 -953085.336629 +1100 1052 -1537234.41392 +1101 1052 -1196000.48814 +1102 1052 227177.812693 +1103 1052 217630.207531 +1104 1052 55832.4159336 +1105 1052 -1179893.40665 +1106 1052 -1340787.9621 +1107 1052 1056419.44831 +1053 1053 18361731.7736 +1054 1053 3.72529029846e-9 +1055 1053 5.58793544769e-9 +1056 1053 -125380.553908 +1099 1053 -741520.302649 +1100 1053 -1196000.48814 +1101 1053 -2434642.38116 +1102 1053 75251.5247453 +1103 1053 -55832.4159336 +1104 1053 -5287984.36193 +1105 1053 929649.114512 +1106 1053 1056419.44831 +1107 1053 -2155790.5645 +1054 1054 11197028.9194 +1055 1054 7624235.1212 +1056 1054 -1.49011611938e-8 +1057 1054 -1154846.00933 +1058 1054 -476225.158488 +1059 1054 7.45058059692e-9 +1102 1054 -1038306.19785 +1103 1054 -1179893.40665 +1104 1054 -929649.114512 +1105 1054 -908773.635179 +1106 1054 -175831.894858 +1107 1054 -148519.569575 +1108 1054 -1760951.03338 +1109 1054 -726165.37459 +1110 1054 1300948.03845 +1055 1055 6560950.4012 +1056 1055 -7.45058059692e-9 +1057 1055 -476225.158488 +1058 1055 -196381.508655 +1059 1055 2.79396772385e-9 +1102 1055 -1179893.40665 +1103 1055 -1340787.9621 +1104 1055 -1056419.44831 +1105 1055 -175831.894858 +1106 1055 153537.59263 +1107 1055 207978.412157 +1108 1055 -726165.37459 +1109 1055 -299449.639006 +1110 1055 536473.417918 +1056 1056 21281133.3311 +1059 1056 4075797.18655 +1102 1056 -929649.114512 +1103 1056 -1056419.44831 +1104 1056 -2155790.5645 +1105 1056 148519.569575 +1106 1056 -207978.412157 +1107 1056 -7295491.64648 +1108 1056 1300948.03845 +1109 1056 536473.417918 +1110 1056 -3164492.7709 +1057 1057 14087608.2601 +1058 1057 2.98023223877e-8 +1059 1057 -7.45058059692e-9 +1060 1057 -1154846.00933 +1061 1057 476225.158488 +1062 1057 7.45058059692e-9 +1105 1057 -1760951.03338 +1106 1057 -726165.37459 +1107 1057 -1300948.03845 +1108 1057 -2367056.05396 +1109 1057 -9.31322574615e-9 +1110 1057 -5.58793544769e-9 +1111 1057 -1760951.03338 +1112 1057 726165.37459 +1113 1057 1300948.03845 +1058 1058 2395597.11087 +1059 1058 7.45058059692e-9 +1060 1058 476225.158488 +1061 1058 -196381.508655 +1062 1058 -2.79396772385e-9 +1105 1058 -726165.37459 +1106 1058 -299449.639006 +1107 1058 -536473.417918 +1108 1058 -7.45058059692e-9 +1109 1058 -402517.768767 +1110 1058 429178.734334 +1111 1058 726165.37459 +1112 1058 -299449.639006 +1113 1058 -536473.417918 +1059 1059 25315942.1547 +1062 1059 4075797.18655 +1105 1059 -1300948.03845 +1106 1059 -536473.417918 +1107 1059 -3164492.7709 +1108 1059 3.72529029846e-9 +1109 1059 -429178.734334 +1110 1059 -10404782.7221 +1111 1059 1300948.03845 +1112 1059 -536473.417918 +1113 1059 -3164492.7709 +1060 1060 11197028.9194 +1061 1060 -7624235.1212 +1062 1060 1.49011611938e-8 +1063 1060 -2626121.17726 +1064 1060 2984228.61052 +1065 1060 -9.31322574615e-9 +1108 1060 -1760951.03338 +1109 1060 726165.37459 +1110 1060 -1300948.03845 +1111 1060 -908773.635179 +1112 1060 175831.894858 +1113 1060 148519.569575 +1114 1060 -1038306.19785 +1115 1060 1179893.40665 +1116 1060 929649.114512 +1061 1061 6560950.4012 +1062 1061 -7.45058059692e-9 +1063 1061 2984228.61052 +1064 1061 -3391168.87559 +1065 1061 9.31322574615e-9 +1108 1061 726165.37459 +1109 1061 -299449.639006 +1110 1061 536473.417918 +1111 1061 175831.894858 +1112 1061 153537.59263 +1113 1061 207978.412157 +1114 1061 1179893.40665 +1115 1061 -1340787.9621 +1116 1061 -1056419.44831 +1062 1062 21281133.3311 +1063 1062 -1.67638063431e-8 +1064 1062 1.67638063431e-8 +1065 1062 -125380.553908 +1108 1062 -1300948.03845 +1109 1062 536473.417918 +1110 1062 -3164492.7709 +1111 1062 -148519.569575 +1112 1062 -207978.412157 +1113 1062 -7295491.64648 +1114 1062 929649.114512 +1115 1062 -1056419.44831 +1116 1062 -2155790.5645 +1063 1063 6516876.42304 +1064 1063 -8531914.96891 +1065 1063 2.98023223877e-8 +1066 1063 -1076372.38828 +1067 1063 1736084.49722 +1068 1063 -1.49011611938e-8 +1111 1063 -1038306.19785 +1112 1063 1179893.40665 +1113 1063 -929649.114512 +1114 1063 222027.677811 +1115 1063 -227177.812693 +1116 1063 75251.5247453 +1117 1063 -590912.90871 +1118 1063 953085.336629 +1119 1063 741520.302649 +1064 1064 11512089.4984 +1065 1064 -3.72529029846e-8 +1066 1064 1736084.49722 +1067 1064 -2800136.28584 +1068 1064 2.421438694e-8 +1111 1064 1179893.40665 +1112 1064 -1340787.9621 +1113 1064 1056419.44831 +1114 1064 -227177.812693 +1115 1064 217630.207531 +1116 1064 55832.4159336 +1117 1064 953085.336629 +1118 1064 -1537234.41392 +1119 1064 -1196000.48814 +1065 1065 18361731.7736 +1066 1065 -2.23517417908e-8 +1067 1065 3.53902578354e-8 +1068 1065 1520483.39551 +1111 1065 -929649.114512 +1112 1065 1056419.44831 +1113 1065 -2155790.5645 +1114 1065 -75251.5247453 +1115 1065 -55832.4159336 +1116 1065 -5287984.36193 +1117 1065 741520.302649 +1118 1065 -1196000.48814 +1119 1065 -2434642.38116 +1066 1066 3880798.16119 +1067 1066 -7110485.96967 +1068 1066 4.09781932831e-8 +1069 1066 -759692.612592 +1070 1066 1651505.67955 +1071 1066 -1.86264514923e-8 +1114 1066 -590912.90871 +1115 1066 953085.336629 +1116 1066 -741520.302649 +1117 1066 -52167.0393844 +1118 1066 83826.403156 +1119 1066 61373.985991 +1120 1066 -379286.632065 +1121 1066 824536.156663 +1122 1066 588085.337671 +1067 1067 13318817.2723 +1068 1067 -7.45058059692e-8 +1069 1067 1651505.67955 +1070 1067 -3590229.73815 +1071 1067 4.09781932831e-8 +1114 1067 953085.336629 +1115 1067 -1537234.41392 +1116 1067 1196000.48814 +1117 1067 83826.403156 +1118 1067 -134521.304432 +1119 1067 32978.3592395 +1120 1067 824536.156663 +1121 1067 -1792469.90579 +1122 1067 -1278446.38624 +1068 1068 19146980.3477 +1069 1068 -2.51457095146e-8 +1070 1068 5.40167093277e-8 +1071 1068 1124555.53479 +1114 1068 -741520.302649 +1115 1068 1196000.48814 +1116 1068 -2434642.38116 +1117 1068 -61373.985991 +1118 1068 -32978.3592395 +1119 1068 -6109264.54972 +1120 1068 588085.337671 +1121 1068 -1278446.38624 +1122 1068 -2352102.70813 +1069 1069 2558539.62181 +1070 1069 -6138307.60949 +1071 1069 4.09781932831e-8 +1072 1069 -544032.43723 +1073 1069 1483724.82881 +1074 1069 -2.51457095146e-8 +1117 1069 -379286.632065 +1118 1069 824536.156663 +1119 1069 -588085.337671 +1120 1069 12227.6197724 +1121 1069 -33038.3525625 +1122 1069 41457.2556898 +1123 1069 -260348.273704 +1124 1069 710040.746465 +1125 1069 484442.198447 +1070 1070 14915778.6682 +1071 1070 -1.11758708954e-7 +1072 1070 1483724.82881 +1073 1070 -4046522.26039 +1074 1070 7.07805156708e-8 +1117 1070 824536.156663 +1118 1070 -1792469.90579 +1119 1070 1278446.38624 +1120 1070 -33038.3525625 +1121 1070 89431.3340668 +1122 1070 17103.8438088 +1123 1070 710040.746465 +1124 1070 -1936474.76309 +1125 1070 -1321205.99576 +1071 1071 18669379.7869 +1072 1071 -2.79396772385e-8 +1073 1071 7.82310962677e-8 +1074 1071 931107.693646 +1117 1071 -588085.337671 +1118 1071 1278446.38624 +1119 1071 -2352102.70813 +1120 1071 -41457.2556898 +1121 1071 -17103.8438089 +1122 1071 -5695176.55864 +1123 1071 484442.198447 +1124 1071 -1321205.99576 +1125 1071 -2315242.24089 +1072 1072 1816739.3315 +1073 1072 -5341279.87864 +1074 1072 3.72529029846e-8 +1075 1072 -413671.243673 +1076 1072 1334423.36669 +1077 1072 -6.51925802231e-9 +1120 1072 -260348.273704 +1121 1072 710040.746465 +1122 1072 -484442.198447 +1123 1072 24667.0078013 +1124 1072 -73754.1287477 +1125 1072 27106.375452 +1126 1072 -193836.559394 +1127 1072 625279.223852 +1128 1072 416676.259817 +1073 1073 15814018.062 +1074 1073 -1.11758708954e-7 +1075 1073 1334423.36669 +1076 1073 -4304591.50545 +1077 1073 2.60770320892e-8 +1120 1073 710040.746465 +1121 1073 -1936474.76309 +1122 1073 1321205.99576 +1123 1073 -73754.1287477 +1124 1073 222052.369366 +1125 1073 9164.38855486 +1126 1073 625279.223852 +1127 1073 -2017029.75436 +1128 1073 -1344116.96715 +1074 1074 18448886.5844 +1075 1074 -1.11758708954e-8 +1076 1074 3.35276126862e-8 +1077 1074 830431.306933 +1120 1074 -484442.198447 +1121 1074 1321205.99576 +1122 1074 -2315242.24089 +1123 1074 -27106.375452 +1124 1074 -9164.38855491 +1125 1074 -5492991.14412 +1126 1074 416676.259817 +1127 1074 -1344116.96715 +1128 1074 -2296979.40748 +1075 1075 2350262.78049 +1076 1075 -4600302.86075 +1077 1075 1.86264514923e-8 +1078 1075 -7.45058059692e-9 +1081 1075 -311975.890718 +1082 1075 1184718.57235 +1083 1075 -9.31322574615e-9 +1123 1075 -193836.559394 +1124 1075 625279.223852 +1125 1075 -416676.259817 +1126 1075 -474421.1715 +1127 1075 -131724.429408 +1128 1075 23329.9193232 +1129 1075 -800250.281991 +1130 1075 8988445.57462 +1132 1075 -144050.092155 +1133 1075 547025.666412 +1134 1075 358351.461509 +1076 1076 17048556.4258 +1077 1076 -6.70552253723e-8 +1078 1076 5.96046447754e-8 +1079 1076 7.45058059692e-9 +1081 1076 1184718.57235 +1082 1076 -4498931.28739 +1083 1076 3.35276126862e-8 +1123 1076 625279.223852 +1124 1076 -2017029.75436 +1125 1076 1344116.96715 +1126 1076 -131724.429408 +1127 1076 -28174.4048378 +1128 1076 6684.54707694 +1129 1076 -6040681.08601 +1130 1076 800250.281991 +1132 1076 547025.666412 +1133 1076 -2077312.65726 +1134 1076 -1360828.33484 +1077 1077 36176122.0894 +1081 1077 -1.30385160446e-8 +1082 1077 4.47034835815e-8 +1083 1077 758227.341129 +1123 1077 -416676.259817 +1124 1077 1344116.96715 +1125 1077 -2296979.40748 +1126 1077 -23329.9193232 +1127 1077 -6684.54707699 +1128 1077 -14301137.6219 +1132 1077 358351.461509 +1133 1077 -1360828.33484 +1134 1077 -2284273.33935 +1078 1078 305878011.196 +1079 1078 -66093576.1502 +1126 1078 800250.281991 +1127 1078 6040681.08601 +1129 1078 64525513.4986 +1130 1078 4237777.92342 +1079 1079 549337215.254 +1126 1079 -8988445.57462 +1127 1079 -800250.281991 +1129 1079 4237777.92342 +1130 1079 48915433.0595 +1080 1080 715555.555556 +1131 1080 -357777.777778 +1081 1081 1011851.60912 +1082 1081 -4110093.43272 +1083 1081 6.14672899246e-8 +1126 1081 -144050.092155 +1127 1081 547025.666412 +1128 1081 -358351.461509 +1132 1081 22134.9913964 +1133 1081 -90320.9940531 +1134 1081 18909.0650259 +1082 1082 16788621.6609 +1083 1082 -2.53319740295e-7 +1126 1082 547025.666412 +1127 1082 -2077312.65726 +1128 1082 1360828.33484 +1132 1082 -90320.9940531 +1133 1082 370626.696215 +1134 1082 4631.25243446 +1083 1083 18240145.4814 +1126 1083 -358351.461509 +1127 1083 1360828.33484 +1128 1083 -2284273.33935 +1132 1083 -18909.0650259 +1133 1083 -4631.25243459 +1134 1083 -5293544.01962 +1084 1084 1011851.60912 +1085 1084 4110093.43272 +1086 1084 -4.65661287308e-8 +1087 1084 -311975.890718 +1088 1084 -1184718.57235 +1089 1084 9.31322574615e-9 +1135 1084 22134.9913963 +1136 1084 90320.994053 +1137 1084 -18909.0650259 +1138 1084 -144050.092155 +1139 1084 -547025.666412 +1140 1084 358351.461509 +1085 1085 16788621.6609 +1086 1085 -2.01165676117e-7 +1087 1085 -1184718.57235 +1088 1085 -4498931.28739 +1089 1085 2.98023223877e-8 +1135 1085 90320.994053 +1136 1085 370626.696215 +1137 1085 4631.25243448 +1138 1085 -547025.666412 +1139 1085 -2077312.65726 +1140 1085 1360828.33484 +1086 1086 18240145.4814 +1087 1086 1.02445483208e-8 +1088 1086 3.35276126862e-8 +1089 1086 758227.341129 +1135 1086 18909.0650259 +1136 1086 -4631.25243456 +1137 1086 -5293544.01962 +1138 1086 358351.461509 +1139 1086 1360828.33484 +1140 1086 -2284273.33935 +1087 1087 2350262.38096 +1088 1087 4600302.12491 +1089 1087 -2.98023223877e-8 +1090 1087 2.98023223877e-8 +1091 1087 -1.19209289551e-7 +1093 1087 -413671.243673 +1094 1087 -1334423.36669 +1095 1087 1.58324837685e-8 +1135 1087 -144050.092155 +1136 1087 -547025.666412 +1137 1087 -358351.461509 +1138 1087 -474420.971735 +1139 1087 131724.797327 +1140 1087 -23329.9193232 +1141 1087 800256.904552 +1142 1087 8988441.97885 +1144 1087 -193836.559394 +1145 1087 -625279.223852 +1146 1087 416676.259817 +1088 1088 17048556.8253 +1089 1088 -9.685754776e-8 +1090 1088 5.96046447754e-8 +1093 1088 -1334423.36669 +1094 1088 -4304591.50545 +1095 1088 4.842877388e-8 +1135 1088 -547025.666412 +1136 1088 -2077312.65726 +1137 1088 -1360828.33484 +1138 1088 131724.797327 +1139 1088 -28174.604603 +1140 1088 6684.54707694 +1141 1088 -6040684.68178 +1142 1088 -800256.904552 +1144 1088 -625279.223852 +1145 1088 -2017029.75436 +1146 1088 1344116.96715 +1089 1089 36176122.0894 +1093 1089 1.210719347e-8 +1094 1089 3.35276126862e-8 +1095 1089 830431.306934 +1135 1089 -358351.461509 +1136 1089 -1360828.33484 +1137 1089 -2284273.33935 +1138 1089 23329.9193232 +1139 1089 -6684.54707697 +1140 1089 -14301137.6219 +1144 1089 416676.259817 +1145 1089 1344116.96715 +1146 1089 -2296979.40748 +1090 1090 305878308.174 +1091 1090 66094123.115 +1138 1090 -800256.904552 +1139 1090 6040684.68178 +1141 1090 64525494.457 +1142 1090 -4237812.99363 +1091 1091 549336918.275 +1138 1091 -8988441.97885 +1139 1091 800256.904552 +1141 1091 -4237812.99363 +1142 1091 48915452.1011 +1092 1092 715555.555556 +1143 1092 -357777.777778 +1093 1093 1816739.3315 +1094 1093 5341279.87864 +1095 1093 -3.53902578354e-8 +1096 1093 -544032.43723 +1097 1093 -1483724.82881 +1098 1093 2.23517417908e-8 +1138 1093 -193836.559394 +1139 1093 -625279.223852 +1140 1093 -416676.259817 +1144 1093 24667.0078013 +1145 1093 73754.1287476 +1146 1093 -27106.375452 +1147 1093 -260348.273704 +1148 1093 -710040.746465 +1149 1093 484442.198447 +1094 1094 15814018.062 +1095 1094 -1.11758708954e-7 +1096 1094 -1483724.82881 +1097 1094 -4046522.26039 +1098 1094 5.96046447754e-8 +1138 1094 -625279.223852 +1139 1094 -2017029.75436 +1140 1094 -1344116.96715 +1144 1094 73754.1287476 +1145 1094 222052.369366 +1146 1094 9164.38855485 +1147 1094 -710040.746465 +1148 1094 -1936474.76309 +1149 1094 1321205.99576 +1095 1095 18448886.5844 +1096 1095 1.95577740669e-8 +1097 1095 5.21540641785e-8 +1098 1095 931107.693646 +1138 1095 -416676.259817 +1139 1095 -1344116.96715 +1140 1095 -2296979.40748 +1144 1095 27106.375452 +1145 1095 -9164.3885549 +1146 1095 -5492991.14412 +1147 1095 484442.198447 +1148 1095 1321205.99576 +1149 1095 -2315242.24089 +1096 1096 2558539.62181 +1097 1096 6138307.60949 +1098 1096 -5.21540641785e-8 +1099 1096 -759692.612592 +1100 1096 -1651505.67955 +1101 1096 2.98023223877e-8 +1144 1096 -260348.273704 +1145 1096 -710040.746465 +1146 1096 -484442.198447 +1147 1096 12227.6197724 +1148 1096 33038.3525624 +1149 1096 -41457.2556898 +1150 1096 -379286.632065 +1151 1096 -824536.156663 +1152 1096 588085.337671 +1097 1097 14915778.6682 +1098 1097 -1.26659870148e-7 +1099 1097 -1651505.67955 +1100 1097 -3590229.73815 +1101 1097 6.33299350738e-8 +1144 1097 -710040.746465 +1145 1097 -1936474.76309 +1146 1097 -1321205.99576 +1147 1097 33038.3525624 +1148 1097 89431.3340665 +1149 1097 17103.8438088 +1150 1097 -824536.156663 +1151 1097 -1792469.90579 +1152 1097 1278446.38624 +1098 1098 18669379.7869 +1099 1098 2.88709998131e-8 +1100 1098 6.14672899246e-8 +1101 1098 1124555.53479 +1144 1098 -484442.198447 +1145 1098 -1321205.99576 +1146 1098 -2315242.24089 +1147 1098 41457.2556898 +1148 1098 -17103.8438089 +1149 1098 -5695176.55864 +1150 1098 588085.337671 +1151 1098 1278446.38624 +1152 1098 -2352102.70813 +1099 1099 3880798.16119 +1100 1099 7110485.96967 +1101 1099 -4.842877388e-8 +1102 1099 -1076372.38828 +1103 1099 -1736084.49722 +1104 1099 2.60770320892e-8 +1147 1099 -379286.632065 +1148 1099 -824536.156663 +1149 1099 -588085.337671 +1150 1099 -52167.0393844 +1151 1099 -83826.403156 +1152 1099 -61373.985991 +1153 1099 -590912.90871 +1154 1099 -953085.336629 +1155 1099 741520.302649 +1100 1100 13318817.2723 +1101 1100 -9.685754776e-8 +1102 1100 -1736084.49722 +1103 1100 -2800136.28584 +1104 1100 4.09781932831e-8 +1147 1100 -824536.156663 +1148 1100 -1792469.90579 +1149 1100 -1278446.38624 +1150 1100 -83826.403156 +1151 1100 -134521.304432 +1152 1100 32978.3592395 +1153 1100 -953085.336629 +1154 1100 -1537234.41392 +1155 1100 1196000.48814 +1101 1101 19146980.3477 +1102 1101 1.86264514923e-8 +1103 1101 3.16649675369e-8 +1104 1101 1520483.39551 +1147 1101 -588085.337671 +1148 1101 -1278446.38624 +1149 1101 -2352102.70813 +1150 1101 61373.985991 +1151 1101 -32978.3592395 +1152 1101 -6109264.54972 +1153 1101 741520.302649 +1154 1101 1196000.48814 +1155 1101 -2434642.38116 +1102 1102 6516876.42304 +1103 1102 8531914.96891 +1104 1102 -1.49011611938e-8 +1105 1102 -2626121.17726 +1106 1102 -2984228.61052 +1107 1102 9.31322574615e-9 +1150 1102 -590912.90871 +1151 1102 -953085.336629 +1152 1102 -741520.302649 +1153 1102 222027.67781 +1154 1102 227177.812693 +1155 1102 -75251.5247453 +1156 1102 -1038306.19785 +1157 1102 -1179893.40665 +1158 1102 929649.114512 +1103 1103 11512089.4984 +1104 1103 -2.23517417908e-8 +1105 1103 -2984228.61052 +1106 1103 -3391168.87559 +1107 1103 1.11758708954e-8 +1150 1103 -953085.336629 +1151 1103 -1537234.41392 +1152 1103 -1196000.48814 +1153 1103 227177.812693 +1154 1103 217630.207531 +1155 1103 55832.4159336 +1156 1103 -1179893.40665 +1157 1103 -1340787.9621 +1158 1103 1056419.44831 +1104 1104 18361731.7736 +1105 1104 3.72529029846e-9 +1106 1104 5.58793544769e-9 +1107 1104 -125380.553907 +1150 1104 -741520.302649 +1151 1104 -1196000.48814 +1152 1104 -2434642.38116 +1153 1104 75251.5247453 +1154 1104 -55832.4159336 +1155 1104 -5287984.36193 +1156 1104 929649.114512 +1157 1104 1056419.44831 +1158 1104 -2155790.5645 +1105 1105 11197028.9194 +1106 1105 7624235.1212 +1107 1105 -2.23517417908e-8 +1108 1105 -1154846.00933 +1109 1105 -476225.158488 +1110 1105 7.45058059692e-9 +1153 1105 -1038306.19785 +1154 1105 -1179893.40665 +1155 1105 -929649.114512 +1156 1105 -908773.635179 +1157 1105 -175831.894858 +1158 1105 -148519.569575 +1159 1105 -1760951.03338 +1160 1105 -726165.37459 +1161 1105 1300948.03845 +1106 1106 6560950.4012 +1107 1106 -1.49011611938e-8 +1108 1106 -476225.158488 +1109 1106 -196381.508655 +1110 1106 2.79396772385e-9 +1153 1106 -1179893.40665 +1154 1106 -1340787.9621 +1155 1106 -1056419.44831 +1156 1106 -175831.894858 +1157 1106 153537.59263 +1158 1106 207978.412157 +1159 1106 -726165.37459 +1160 1106 -299449.639006 +1161 1106 536473.417918 +1107 1107 21281133.3311 +1110 1107 4075797.18655 +1153 1107 -929649.114512 +1154 1107 -1056419.44831 +1155 1107 -2155790.5645 +1156 1107 148519.569575 +1157 1107 -207978.412157 +1158 1107 -7295491.64648 +1159 1107 1300948.03845 +1160 1107 536473.417918 +1161 1107 -3164492.7709 +1108 1108 14087608.2601 +1109 1108 7.45058059692e-9 +1110 1108 -7.45058059692e-9 +1111 1108 -1154846.00933 +1112 1108 476225.158488 +1113 1108 7.45058059692e-9 +1156 1108 -1760951.03338 +1157 1108 -726165.37459 +1158 1108 -1300948.03845 +1159 1108 -2367056.05396 +1160 1108 -9.31322574615e-9 +1161 1108 -5.58793544769e-9 +1162 1108 -1760951.03338 +1163 1108 726165.37459 +1164 1108 1300948.03845 +1109 1109 2395597.11087 +1110 1109 7.45058059692e-9 +1111 1109 476225.158488 +1112 1109 -196381.508655 +1113 1109 -2.79396772385e-9 +1156 1109 -726165.37459 +1157 1109 -299449.639006 +1158 1109 -536473.417918 +1159 1109 -7.45058059692e-9 +1160 1109 -402517.768767 +1161 1109 429178.734334 +1162 1109 726165.37459 +1163 1109 -299449.639006 +1164 1109 -536473.417918 +1110 1110 25315942.1547 +1113 1110 4075797.18655 +1156 1110 -1300948.03845 +1157 1110 -536473.417918 +1158 1110 -3164492.7709 +1159 1110 3.72529029846e-9 +1160 1110 -429178.734334 +1161 1110 -10404782.7221 +1162 1110 1300948.03845 +1163 1110 -536473.417918 +1164 1110 -3164492.7709 +1111 1111 11197028.9194 +1112 1111 -7624235.1212 +1113 1111 1.86264514923e-8 +1114 1111 -2626121.17726 +1115 1111 2984228.61052 +1116 1111 -9.31322574615e-9 +1159 1111 -1760951.03338 +1160 1111 726165.37459 +1161 1111 -1300948.03845 +1162 1111 -908773.635179 +1163 1111 175831.894858 +1164 1111 148519.569575 +1165 1111 -1038306.19785 +1166 1111 1179893.40665 +1167 1111 929649.114512 +1112 1112 6560950.4012 +1113 1112 -1.49011611938e-8 +1114 1112 2984228.61052 +1115 1112 -3391168.87559 +1116 1112 9.31322574615e-9 +1159 1112 726165.37459 +1160 1112 -299449.639006 +1161 1112 536473.417918 +1162 1112 175831.894858 +1163 1112 153537.59263 +1164 1112 207978.412157 +1165 1112 1179893.40665 +1166 1112 -1340787.9621 +1167 1112 -1056419.44831 +1113 1113 21281133.3311 +1114 1113 -1.86264514923e-8 +1115 1113 2.04890966415e-8 +1116 1113 -125380.553907 +1159 1113 -1300948.03845 +1160 1113 536473.417918 +1161 1113 -3164492.7709 +1162 1113 -148519.569575 +1163 1113 -207978.412157 +1164 1113 -7295491.64648 +1165 1113 929649.114512 +1166 1113 -1056419.44831 +1167 1113 -2155790.5645 +1114 1114 6516876.42304 +1115 1114 -8531914.96891 +1116 1114 2.98023223877e-8 +1117 1114 -1076372.38828 +1118 1114 1736084.49722 +1119 1114 -1.49011611938e-8 +1162 1114 -1038306.19785 +1163 1114 1179893.40665 +1164 1114 -929649.114512 +1165 1114 222027.67781 +1166 1114 -227177.812693 +1167 1114 75251.5247453 +1168 1114 -590912.90871 +1169 1114 953085.336629 +1170 1114 741520.302649 +1115 1115 11512089.4984 +1116 1115 -3.72529029846e-8 +1117 1115 1736084.49722 +1118 1115 -2800136.28584 +1119 1115 2.421438694e-8 +1162 1115 1179893.40665 +1163 1115 -1340787.9621 +1164 1115 1056419.44831 +1165 1115 -227177.812693 +1166 1115 217630.207531 +1167 1115 55832.4159336 +1168 1115 953085.336629 +1169 1115 -1537234.41392 +1170 1115 -1196000.48814 +1116 1116 18361731.7736 +1117 1116 -2.23517417908e-8 +1118 1116 3.53902578354e-8 +1119 1116 1520483.39551 +1162 1116 -929649.114512 +1163 1116 1056419.44831 +1164 1116 -2155790.5645 +1165 1116 -75251.5247453 +1166 1116 -55832.4159336 +1167 1116 -5287984.36193 +1168 1116 741520.302649 +1169 1116 -1196000.48814 +1170 1116 -2434642.38116 +1117 1117 3880798.16119 +1118 1117 -7110485.96967 +1119 1117 4.09781932831e-8 +1120 1117 -759692.612592 +1121 1117 1651505.67955 +1122 1117 -1.86264514923e-8 +1165 1117 -590912.90871 +1166 1117 953085.336629 +1167 1117 -741520.302649 +1168 1117 -52167.0393844 +1169 1117 83826.403156 +1170 1117 61373.985991 +1171 1117 -379286.632065 +1172 1117 824536.156663 +1173 1117 588085.337671 +1118 1118 13318817.2723 +1119 1118 -7.45058059692e-8 +1120 1118 1651505.67955 +1121 1118 -3590229.73815 +1122 1118 4.09781932831e-8 +1165 1118 953085.336629 +1166 1118 -1537234.41392 +1167 1118 1196000.48814 +1168 1118 83826.403156 +1169 1118 -134521.304432 +1170 1118 32978.3592395 +1171 1118 824536.156663 +1172 1118 -1792469.90579 +1173 1118 -1278446.38624 +1119 1119 19146980.3477 +1120 1119 -2.51457095146e-8 +1121 1119 5.40167093277e-8 +1122 1119 1124555.53479 +1165 1119 -741520.302649 +1166 1119 1196000.48814 +1167 1119 -2434642.38116 +1168 1119 -61373.985991 +1169 1119 -32978.3592395 +1170 1119 -6109264.54972 +1171 1119 588085.337671 +1172 1119 -1278446.38624 +1173 1119 -2352102.70813 +1120 1120 2558539.62181 +1121 1120 -6138307.60949 +1122 1120 4.28408384323e-8 +1123 1120 -544032.43723 +1124 1120 1483724.82881 +1125 1120 -2.51457095146e-8 +1168 1120 -379286.632065 +1169 1120 824536.156663 +1170 1120 -588085.337671 +1171 1120 12227.6197724 +1172 1120 -33038.3525625 +1173 1120 41457.2556898 +1174 1120 -260348.273704 +1175 1120 710040.746465 +1176 1120 484442.198447 +1121 1121 14915778.6682 +1122 1121 -1.19209289551e-7 +1123 1121 1483724.82881 +1124 1121 -4046522.26039 +1125 1121 7.07805156708e-8 +1168 1121 824536.156663 +1169 1121 -1792469.90579 +1170 1121 1278446.38624 +1171 1121 -33038.3525625 +1172 1121 89431.3340666 +1173 1121 17103.8438088 +1174 1121 710040.746465 +1175 1121 -1936474.76309 +1176 1121 -1321205.99576 +1122 1122 18669379.7869 +1123 1122 -2.79396772385e-8 +1124 1122 7.82310962677e-8 +1125 1122 931107.693646 +1168 1122 -588085.337671 +1169 1122 1278446.38624 +1170 1122 -2352102.70813 +1171 1122 -41457.2556898 +1172 1122 -17103.8438089 +1173 1122 -5695176.55864 +1174 1122 484442.198447 +1175 1122 -1321205.99576 +1176 1122 -2315242.24089 +1123 1123 1816739.3315 +1124 1123 -5341279.87864 +1125 1123 3.91155481339e-8 +1126 1123 -413671.243673 +1127 1123 1334423.36669 +1128 1123 -8.38190317154e-9 +1171 1123 -260348.273704 +1172 1123 710040.746465 +1173 1123 -484442.198447 +1174 1123 24667.0078013 +1175 1123 -73754.1287476 +1176 1123 27106.375452 +1177 1123 -193836.559394 +1178 1123 625279.223852 +1179 1123 416676.259817 +1124 1124 15814018.062 +1125 1124 -1.19209289551e-7 +1126 1124 1334423.36669 +1127 1124 -4304591.50545 +1128 1124 2.98023223877e-8 +1171 1124 710040.746465 +1172 1124 -1936474.76309 +1173 1124 1321205.99576 +1174 1124 -73754.1287476 +1175 1124 222052.369366 +1176 1124 9164.38855486 +1177 1124 625279.223852 +1178 1124 -2017029.75436 +1179 1124 -1344116.96715 +1125 1125 18448886.5844 +1126 1125 -1.210719347e-8 +1127 1125 3.72529029846e-8 +1128 1125 830431.306933 +1171 1125 -484442.198447 +1172 1125 1321205.99576 +1173 1125 -2315242.24089 +1174 1125 -27106.375452 +1175 1125 -9164.38855491 +1176 1125 -5492991.14412 +1177 1125 416676.259817 +1178 1125 -1344116.96715 +1179 1125 -2296979.40748 +1126 1126 2350262.78049 +1127 1126 -4600302.86075 +1128 1126 1.86264514923e-8 +1129 1126 -7.45058059692e-9 +1132 1126 -311975.890718 +1133 1126 1184718.57235 +1134 1126 -9.31322574615e-9 +1174 1126 -193836.559394 +1175 1126 625279.223852 +1176 1126 -416676.259817 +1177 1126 -474421.1715 +1178 1126 -131724.429407 +1179 1126 23329.9193232 +1180 1126 -800250.281991 +1181 1126 8988445.57462 +1183 1126 -144050.092155 +1184 1126 547025.666412 +1185 1126 358351.461509 +1127 1127 17048556.4258 +1128 1127 -6.70552253723e-8 +1129 1127 5.96046447754e-8 +1130 1127 7.45058059692e-9 +1132 1127 1184718.57235 +1133 1127 -4498931.28739 +1134 1127 3.35276126862e-8 +1174 1127 625279.223852 +1175 1127 -2017029.75436 +1176 1127 1344116.96715 +1177 1127 -131724.429407 +1178 1127 -28174.4048382 +1179 1127 6684.54707695 +1180 1127 -6040681.08601 +1181 1127 800250.281991 +1183 1127 547025.666412 +1184 1127 -2077312.65726 +1185 1127 -1360828.33484 +1128 1128 36176122.0894 +1132 1128 -1.39698386192e-8 +1133 1128 4.842877388e-8 +1134 1128 758227.341129 +1174 1128 -416676.259817 +1175 1128 1344116.96715 +1176 1128 -2296979.40748 +1177 1128 -23329.9193232 +1178 1128 -6684.54707698 +1179 1128 -14301137.6219 +1183 1128 358351.461509 +1184 1128 -1360828.33484 +1185 1128 -2284273.33935 +1129 1129 305878011.196 +1130 1129 -66093576.1502 +1177 1129 800250.281991 +1178 1129 6040681.08601 +1180 1129 64525513.4986 +1181 1129 4237777.92342 +1130 1130 549337215.254 +1177 1130 -8988445.57462 +1178 1130 -800250.281991 +1180 1130 4237777.92342 +1181 1130 48915433.0595 +1131 1131 715555.555556 +1182 1131 -357777.777778 +1132 1132 1011851.60912 +1133 1132 -4110093.43272 +1134 1132 6.14672899246e-8 +1177 1132 -144050.092155 +1178 1132 547025.666412 +1179 1132 -358351.461509 +1183 1132 22134.9913963 +1184 1132 -90320.994053 +1185 1132 18909.0650259 +1133 1133 16788621.6609 +1134 1133 -2.53319740295e-7 +1177 1133 547025.666412 +1178 1133 -2077312.65726 +1179 1133 1360828.33484 +1183 1133 -90320.994053 +1184 1133 370626.696215 +1185 1133 4631.25243446 +1134 1134 18240145.4814 +1177 1134 -358351.461509 +1178 1134 1360828.33484 +1179 1134 -2284273.33935 +1183 1134 -18909.0650259 +1184 1134 -4631.25243459 +1185 1134 -5293544.01962 +1135 1135 1011851.60912 +1136 1135 4110093.43272 +1137 1135 -5.21540641785e-8 +1138 1135 -311975.890718 +1139 1135 -1184718.57235 +1140 1135 1.02445483208e-8 +1186 1135 22134.9913963 +1187 1135 90320.994053 +1188 1135 -18909.0650259 +1189 1135 -144050.092155 +1190 1135 -547025.666412 +1191 1135 358351.461509 +1136 1136 16788621.6609 +1137 1136 -2.16066837311e-7 +1138 1136 -1184718.57235 +1139 1136 -4498931.28739 +1140 1136 3.35276126862e-8 +1186 1136 90320.994053 +1187 1136 370626.696215 +1188 1136 4631.25243448 +1189 1136 -547025.666412 +1190 1136 -2077312.65726 +1191 1136 1360828.33484 +1137 1137 18240145.4814 +1138 1137 9.31322574615e-9 +1139 1137 2.98023223877e-8 +1140 1137 758227.34113 +1186 1137 18909.0650259 +1187 1137 -4631.25243456 +1188 1137 -5293544.01962 +1189 1137 358351.461509 +1190 1137 1360828.33484 +1191 1137 -2284273.33935 +1138 1138 2350262.38096 +1139 1138 4600302.12491 +1140 1138 -2.79396772385e-8 +1141 1138 2.98023223877e-8 +1142 1138 -1.19209289551e-7 +1144 1138 -413671.243673 +1145 1138 -1334423.36669 +1146 1138 1.58324837685e-8 +1186 1138 -144050.092155 +1187 1138 -547025.666412 +1188 1138 -358351.461509 +1189 1138 -474420.971735 +1190 1138 131724.797327 +1191 1138 -23329.9193232 +1192 1138 800256.904552 +1193 1138 8988441.97885 +1195 1138 -193836.559394 +1196 1138 -625279.223852 +1197 1138 416676.259817 +1139 1139 17048556.8253 +1140 1139 -8.94069671631e-8 +1141 1139 5.96046447754e-8 +1144 1139 -1334423.36669 +1145 1139 -4304591.50545 +1146 1139 4.842877388e-8 +1186 1139 -547025.666412 +1187 1139 -2077312.65726 +1188 1139 -1360828.33484 +1189 1139 131724.797327 +1190 1139 -28174.604603 +1191 1139 6684.54707694 +1192 1139 -6040684.68178 +1193 1139 -800256.904552 +1195 1139 -625279.223852 +1196 1139 -2017029.75436 +1197 1139 1344116.96715 +1140 1140 36176122.0894 +1144 1140 1.210719347e-8 +1145 1140 3.35276126862e-8 +1146 1140 830431.306934 +1186 1140 -358351.461509 +1187 1140 -1360828.33484 +1188 1140 -2284273.33935 +1189 1140 23329.9193232 +1190 1140 -6684.54707697 +1191 1140 -14301137.6219 +1195 1140 416676.259817 +1196 1140 1344116.96715 +1197 1140 -2296979.40748 +1141 1141 305878308.174 +1142 1141 66094123.115 +1189 1141 -800256.904552 +1190 1141 6040684.68178 +1192 1141 64525494.457 +1193 1141 -4237812.99363 +1142 1142 549336918.275 +1189 1142 -8988441.97885 +1190 1142 800256.904552 +1192 1142 -4237812.99363 +1193 1142 48915452.1011 +1143 1143 715555.555556 +1194 1143 -357777.777778 +1144 1144 1816739.3315 +1145 1144 5341279.87864 +1146 1144 -3.53902578354e-8 +1147 1144 -544032.43723 +1148 1144 -1483724.82881 +1149 1144 2.04890966415e-8 +1189 1144 -193836.559394 +1190 1144 -625279.223852 +1191 1144 -416676.259817 +1195 1144 24667.0078013 +1196 1144 73754.1287476 +1197 1144 -27106.375452 +1198 1144 -260348.273704 +1199 1144 -710040.746465 +1200 1144 484442.198447 +1145 1145 15814018.062 +1146 1145 -1.11758708954e-7 +1147 1145 -1483724.82881 +1148 1145 -4046522.26039 +1149 1145 5.58793544769e-8 +1189 1145 -625279.223852 +1190 1145 -2017029.75436 +1191 1145 -1344116.96715 +1195 1145 73754.1287476 +1196 1145 222052.369366 +1197 1145 9164.38855485 +1198 1145 -710040.746465 +1199 1145 -1936474.76309 +1200 1145 1321205.99576 +1146 1146 18448886.5844 +1147 1146 1.95577740669e-8 +1148 1146 5.21540641785e-8 +1149 1146 931107.693646 +1189 1146 -416676.259817 +1190 1146 -1344116.96715 +1191 1146 -2296979.40748 +1195 1146 27106.375452 +1196 1146 -9164.3885549 +1197 1146 -5492991.14412 +1198 1146 484442.198447 +1199 1146 1321205.99576 +1200 1146 -2315242.24089 +1147 1147 2558539.62181 +1148 1147 6138307.60949 +1149 1147 -5.58793544769e-8 +1150 1147 -759692.612592 +1151 1147 -1651505.67955 +1152 1147 2.98023223877e-8 +1195 1147 -260348.273704 +1196 1147 -710040.746465 +1197 1147 -484442.198447 +1198 1147 12227.6197724 +1199 1147 33038.3525624 +1200 1147 -41457.2556898 +1201 1147 -379286.632065 +1202 1147 -824536.156663 +1203 1147 588085.337671 +1148 1148 14915778.6682 +1149 1148 -1.34110450745e-7 +1150 1148 -1651505.67955 +1151 1148 -3590229.73815 +1152 1148 6.33299350738e-8 +1195 1148 -710040.746465 +1196 1148 -1936474.76309 +1197 1148 -1321205.99576 +1198 1148 33038.3525624 +1199 1148 89431.3340665 +1200 1148 17103.8438088 +1201 1148 -824536.156663 +1202 1148 -1792469.90579 +1203 1148 1278446.38624 +1149 1149 18669379.7869 +1150 1149 2.88709998131e-8 +1151 1149 6.14672899246e-8 +1152 1149 1124555.53479 +1195 1149 -484442.198447 +1196 1149 -1321205.99576 +1197 1149 -2315242.24089 +1198 1149 41457.2556898 +1199 1149 -17103.8438089 +1200 1149 -5695176.55864 +1201 1149 588085.337671 +1202 1149 1278446.38624 +1203 1149 -2352102.70813 +1150 1150 3880798.16119 +1151 1150 7110485.96967 +1152 1150 -4.842877388e-8 +1153 1150 -1076372.38828 +1154 1150 -1736084.49722 +1155 1150 2.60770320892e-8 +1198 1150 -379286.632065 +1199 1150 -824536.156663 +1200 1150 -588085.337671 +1201 1150 -52167.0393844 +1202 1150 -83826.403156 +1203 1150 -61373.985991 +1204 1150 -590912.90871 +1205 1150 -953085.336629 +1206 1150 741520.302649 +1151 1151 13318817.2723 +1152 1151 -9.685754776e-8 +1153 1151 -1736084.49722 +1154 1151 -2800136.28584 +1155 1151 4.09781932831e-8 +1198 1151 -824536.156663 +1199 1151 -1792469.90579 +1200 1151 -1278446.38624 +1201 1151 -83826.403156 +1202 1151 -134521.304432 +1203 1151 32978.3592395 +1204 1151 -953085.336629 +1205 1151 -1537234.41392 +1206 1151 1196000.48814 +1152 1152 19146980.3477 +1153 1152 1.86264514923e-8 +1154 1152 3.16649675369e-8 +1155 1152 1520483.39551 +1198 1152 -588085.337671 +1199 1152 -1278446.38624 +1200 1152 -2352102.70813 +1201 1152 61373.985991 +1202 1152 -32978.3592395 +1203 1152 -6109264.54972 +1204 1152 741520.302649 +1205 1152 1196000.48814 +1206 1152 -2434642.38116 +1153 1153 6516876.42304 +1154 1153 8531914.96891 +1155 1153 -2.60770320892e-8 +1156 1153 -2626121.17726 +1157 1153 -2984228.61052 +1158 1153 9.31322574615e-9 +1201 1153 -590912.90871 +1202 1153 -953085.336629 +1203 1153 -741520.302649 +1204 1153 222027.67781 +1205 1153 227177.812693 +1206 1153 -75251.5247453 +1207 1153 -1038306.19785 +1208 1153 -1179893.40665 +1209 1153 929649.114512 +1154 1154 11512089.4984 +1155 1154 -3.72529029846e-8 +1156 1154 -2984228.61052 +1157 1154 -3391168.87559 +1158 1154 1.11758708954e-8 +1201 1154 -953085.336629 +1202 1154 -1537234.41392 +1203 1154 -1196000.48814 +1204 1154 227177.812693 +1205 1154 217630.207531 +1206 1154 55832.4159336 +1207 1154 -1179893.40665 +1208 1154 -1340787.9621 +1209 1154 1056419.44831 +1155 1155 18361731.7736 +1156 1155 1.86264514923e-9 +1157 1155 3.72529029846e-9 +1158 1155 -125380.553906 +1201 1155 -741520.302649 +1202 1155 -1196000.48814 +1203 1155 -2434642.38116 +1204 1155 75251.5247453 +1205 1155 -55832.4159336 +1206 1155 -5287984.36193 +1207 1155 929649.114512 +1208 1155 1056419.44831 +1209 1155 -2155790.5645 +1156 1156 11197028.9194 +1157 1156 7624235.1212 +1158 1156 -1.49011611938e-8 +1159 1156 -1154846.00933 +1160 1156 -476225.158488 +1161 1156 7.45058059692e-9 +1204 1156 -1038306.19785 +1205 1156 -1179893.40665 +1206 1156 -929649.114512 +1207 1156 -908773.635179 +1208 1156 -175831.894858 +1209 1156 -148519.569575 +1210 1156 -1760951.03338 +1211 1156 -726165.37459 +1212 1156 1300948.03845 +1157 1157 6560950.4012 +1159 1157 -476225.158488 +1160 1157 -196381.508655 +1161 1157 2.79396772385e-9 +1204 1157 -1179893.40665 +1205 1157 -1340787.9621 +1206 1157 -1056419.44831 +1207 1157 -175831.894858 +1208 1157 153537.59263 +1209 1157 207978.412157 +1210 1157 -726165.37459 +1211 1157 -299449.639006 +1212 1157 536473.417918 +1158 1158 21281133.3311 +1161 1158 4075797.18655 +1204 1158 -929649.114512 +1205 1158 -1056419.44831 +1206 1158 -2155790.5645 +1207 1158 148519.569575 +1208 1158 -207978.412157 +1209 1158 -7295491.64648 +1210 1158 1300948.03845 +1211 1158 536473.417918 +1212 1158 -3164492.7709 +1159 1159 14087608.2601 +1160 1159 7.45058059692e-9 +1161 1159 -7.45058059692e-9 +1162 1159 -1154846.00933 +1163 1159 476225.158488 +1164 1159 7.45058059692e-9 +1207 1159 -1760951.03338 +1208 1159 -726165.37459 +1209 1159 -1300948.03845 +1210 1159 -2367056.05396 +1211 1159 -9.31322574615e-9 +1212 1159 -5.58793544769e-9 +1213 1159 -1760951.03338 +1214 1159 726165.37459 +1215 1159 1300948.03845 +1160 1160 2395597.11087 +1161 1160 7.45058059692e-9 +1162 1160 476225.158488 +1163 1160 -196381.508655 +1164 1160 -2.79396772385e-9 +1207 1160 -726165.37459 +1208 1160 -299449.639006 +1209 1160 -536473.417918 +1210 1160 -7.45058059692e-9 +1211 1160 -402517.768767 +1212 1160 429178.734334 +1213 1160 726165.37459 +1214 1160 -299449.639006 +1215 1160 -536473.417918 +1161 1161 25315942.1547 +1164 1161 4075797.18655 +1207 1161 -1300948.03845 +1208 1161 -536473.417918 +1209 1161 -3164492.7709 +1210 1161 3.72529029846e-9 +1211 1161 -429178.734334 +1212 1161 -10404782.7221 +1213 1161 1300948.03845 +1214 1161 -536473.417918 +1215 1161 -3164492.7709 +1162 1162 11197028.9194 +1163 1162 -7624235.1212 +1164 1162 3.72529029846e-9 +1165 1162 -2626121.17726 +1166 1162 2984228.61052 +1167 1162 -7.45058059692e-9 +1210 1162 -1760951.03338 +1211 1162 726165.37459 +1212 1162 -1300948.03845 +1213 1162 -908773.635179 +1214 1162 175831.894858 +1215 1162 148519.569575 +1216 1162 -1038306.19785 +1217 1162 1179893.40665 +1218 1162 929649.114512 +1163 1163 6560950.4012 +1165 1163 2984228.61052 +1166 1163 -3391168.87559 +1167 1163 7.45058059692e-9 +1210 1163 726165.37459 +1211 1163 -299449.639006 +1212 1163 536473.417918 +1213 1163 175831.894858 +1214 1163 153537.59263 +1215 1163 207978.412157 +1216 1163 1179893.40665 +1217 1163 -1340787.9621 +1218 1163 -1056419.44831 +1164 1164 21281133.3311 +1165 1164 -1.30385160446e-8 +1166 1164 1.30385160446e-8 +1167 1164 -125380.553906 +1210 1164 -1300948.03845 +1211 1164 536473.417918 +1212 1164 -3164492.7709 +1213 1164 -148519.569575 +1214 1164 -207978.412157 +1215 1164 -7295491.64648 +1216 1164 929649.114512 +1217 1164 -1056419.44831 +1218 1164 -2155790.5645 +1165 1165 6516876.42304 +1166 1165 -8531914.96891 +1167 1165 3.72529029846e-8 +1168 1165 -1076372.38828 +1169 1165 1736084.49722 +1170 1165 -1.49011611938e-8 +1213 1165 -1038306.19785 +1214 1165 1179893.40665 +1215 1165 -929649.114512 +1216 1165 222027.67781 +1217 1165 -227177.812693 +1218 1165 75251.5247453 +1219 1165 -590912.90871 +1220 1165 953085.336629 +1221 1165 741520.302649 +1166 1166 11512089.4984 +1167 1166 -4.47034835815e-8 +1168 1166 1736084.49722 +1169 1166 -2800136.28584 +1170 1166 2.421438694e-8 +1213 1166 1179893.40665 +1214 1166 -1340787.9621 +1215 1166 1056419.44831 +1216 1166 -227177.812693 +1217 1166 217630.207531 +1218 1166 55832.4159336 +1219 1166 953085.336629 +1220 1166 -1537234.41392 +1221 1166 -1196000.48814 +1167 1167 18361731.7736 +1168 1167 -2.23517417908e-8 +1169 1167 3.53902578354e-8 +1170 1167 1520483.39551 +1213 1167 -929649.114512 +1214 1167 1056419.44831 +1215 1167 -2155790.5645 +1216 1167 -75251.5247453 +1217 1167 -55832.4159336 +1218 1167 -5287984.36193 +1219 1167 741520.302649 +1220 1167 -1196000.48814 +1221 1167 -2434642.38116 +1168 1168 3880798.16119 +1169 1168 -7110485.96967 +1170 1168 4.09781932831e-8 +1171 1168 -759692.612592 +1172 1168 1651505.67955 +1173 1168 -1.86264514923e-8 +1216 1168 -590912.90871 +1217 1168 953085.336629 +1218 1168 -741520.302649 +1219 1168 -52167.0393844 +1220 1168 83826.403156 +1221 1168 61373.985991 +1222 1168 -379286.632065 +1223 1168 824536.156663 +1224 1168 588085.337671 +1169 1169 13318817.2723 +1170 1169 -7.45058059692e-8 +1171 1169 1651505.67955 +1172 1169 -3590229.73815 +1173 1169 4.09781932831e-8 +1216 1169 953085.336629 +1217 1169 -1537234.41392 +1218 1169 1196000.48814 +1219 1169 83826.403156 +1220 1169 -134521.304432 +1221 1169 32978.3592395 +1222 1169 824536.156663 +1223 1169 -1792469.90579 +1224 1169 -1278446.38624 +1170 1170 19146980.3477 +1171 1170 -2.51457095146e-8 +1172 1170 5.40167093277e-8 +1173 1170 1124555.53479 +1216 1170 -741520.302649 +1217 1170 1196000.48814 +1218 1170 -2434642.38116 +1219 1170 -61373.985991 +1220 1170 -32978.3592395 +1221 1170 -6109264.54972 +1222 1170 588085.337671 +1223 1170 -1278446.38624 +1224 1170 -2352102.70813 +1171 1171 2558539.62181 +1172 1171 -6138307.60949 +1173 1171 4.28408384323e-8 +1174 1171 -544032.43723 +1175 1171 1483724.82881 +1176 1171 -2.51457095146e-8 +1219 1171 -379286.632065 +1220 1171 824536.156663 +1221 1171 -588085.337671 +1222 1171 12227.6197724 +1223 1171 -33038.3525625 +1224 1171 41457.2556898 +1225 1171 -260348.273704 +1226 1171 710040.746465 +1227 1171 484442.198447 +1172 1172 14915778.6682 +1173 1172 -1.19209289551e-7 +1174 1172 1483724.82881 +1175 1172 -4046522.26039 +1176 1172 7.07805156708e-8 +1219 1172 824536.156663 +1220 1172 -1792469.90579 +1221 1172 1278446.38624 +1222 1172 -33038.3525625 +1223 1172 89431.3340666 +1224 1172 17103.8438088 +1225 1172 710040.746465 +1226 1172 -1936474.76309 +1227 1172 -1321205.99576 +1173 1173 18669379.7869 +1174 1173 -2.79396772385e-8 +1175 1173 7.82310962677e-8 +1176 1173 931107.693646 +1219 1173 -588085.337671 +1220 1173 1278446.38624 +1221 1173 -2352102.70813 +1222 1173 -41457.2556898 +1223 1173 -17103.8438089 +1224 1173 -5695176.55864 +1225 1173 484442.198447 +1226 1173 -1321205.99576 +1227 1173 -2315242.24089 +1174 1174 1816739.3315 +1175 1174 -5341279.87864 +1176 1174 3.72529029846e-8 +1177 1174 -413671.243673 +1178 1174 1334423.36669 +1179 1174 -7.45058059692e-9 +1222 1174 -260348.273704 +1223 1174 710040.746465 +1224 1174 -484442.198447 +1225 1174 24667.0078013 +1226 1174 -73754.1287476 +1227 1174 27106.375452 +1228 1174 -193836.559394 +1229 1174 625279.223852 +1230 1174 416676.259817 +1175 1175 15814018.062 +1176 1175 -1.11758708954e-7 +1177 1175 1334423.36669 +1178 1175 -4304591.50545 +1179 1175 2.60770320892e-8 +1222 1175 710040.746465 +1223 1175 -1936474.76309 +1224 1175 1321205.99576 +1225 1175 -73754.1287476 +1226 1175 222052.369366 +1227 1175 9164.38855486 +1228 1175 625279.223852 +1229 1175 -2017029.75436 +1230 1175 -1344116.96715 +1176 1176 18448886.5844 +1177 1176 -1.02445483208e-8 +1178 1176 3.35276126862e-8 +1179 1176 830431.306934 +1222 1176 -484442.198447 +1223 1176 1321205.99576 +1224 1176 -2315242.24089 +1225 1176 -27106.375452 +1226 1176 -9164.38855491 +1227 1176 -5492991.14412 +1228 1176 416676.259817 +1229 1176 -1344116.96715 +1230 1176 -2296979.40748 +1177 1177 2350262.78049 +1178 1177 -4600302.86075 +1179 1177 1.86264514923e-8 +1180 1177 -7.45058059692e-9 +1183 1177 -311975.890718 +1184 1177 1184718.57235 +1185 1177 -9.31322574615e-9 +1225 1177 -193836.559394 +1226 1177 625279.223852 +1227 1177 -416676.259817 +1228 1177 -474421.1715 +1229 1177 -131724.429407 +1230 1177 23329.9193232 +1231 1177 -800250.281991 +1232 1177 8988445.57462 +1234 1177 -144050.092155 +1235 1177 547025.666412 +1236 1177 358351.461509 +1178 1178 17048556.4258 +1179 1178 -6.70552253723e-8 +1180 1178 5.96046447754e-8 +1181 1178 7.45058059692e-9 +1183 1178 1184718.57235 +1184 1178 -4498931.28739 +1185 1178 3.35276126862e-8 +1225 1178 625279.223852 +1226 1178 -2017029.75436 +1227 1178 1344116.96715 +1228 1178 -131724.429407 +1229 1178 -28174.4048382 +1230 1178 6684.54707695 +1231 1178 -6040681.08601 +1232 1178 800250.281991 +1234 1178 547025.666412 +1235 1178 -2077312.65726 +1236 1178 -1360828.33484 +1179 1179 36176122.0894 +1183 1179 -1.30385160446e-8 +1184 1179 4.47034835815e-8 +1185 1179 758227.34113 +1225 1179 -416676.259817 +1226 1179 1344116.96715 +1227 1179 -2296979.40748 +1228 1179 -23329.9193232 +1229 1179 -6684.54707698 +1230 1179 -14301137.6219 +1234 1179 358351.461509 +1235 1179 -1360828.33484 +1236 1179 -2284273.33935 +1180 1180 305878011.196 +1181 1180 -66093576.1502 +1228 1180 800250.281991 +1229 1180 6040681.08601 +1231 1180 64525513.4986 +1232 1180 4237777.92342 +1181 1181 549337215.254 +1228 1181 -8988445.57462 +1229 1181 -800250.281991 +1231 1181 4237777.92342 +1232 1181 48915433.0595 +1182 1182 715555.555556 +1233 1182 -357777.777778 +1183 1183 1011851.60912 +1184 1183 -4110093.43272 +1185 1183 6.14672899246e-8 +1228 1183 -144050.092155 +1229 1183 547025.666412 +1230 1183 -358351.461509 +1234 1183 22134.9913963 +1235 1183 -90320.994053 +1236 1183 18909.0650259 +1184 1184 16788621.6609 +1185 1184 -2.53319740295e-7 +1228 1184 547025.666412 +1229 1184 -2077312.65726 +1230 1184 1360828.33484 +1234 1184 -90320.994053 +1235 1184 370626.696215 +1236 1184 4631.25243446 +1185 1185 18240145.4814 +1228 1185 -358351.461509 +1229 1185 1360828.33484 +1230 1185 -2284273.33935 +1234 1185 -18909.0650259 +1235 1185 -4631.25243459 +1236 1185 -5293544.01962 +1186 1186 983097.517609 +1187 1186 3993191.05725 +1188 1186 -5.02914190292e-8 +1189 1186 -268071.177508 +1190 1186 -1017991.81332 +1191 1186 1.30385160446e-8 +1187 1187 16310675.5472 +1188 1187 -2.08616256714e-7 +1189 1187 -1017991.81332 +1190 1187 -3865791.69616 +1191 1187 4.842877388e-8 +1188 1188 19175351.0344 +1189 1188 8.38190317154e-9 +1190 1188 2.60770320892e-8 +1191 1188 1324183.34296 +1189 1189 2528206.57902 +1190 1189 4472508.77878 +1191 1189 -2.421438694e-8 +1192 1189 -161794.959894 +1193 1189 1643725.47145 +1195 1189 -354732.167298 +1196 1189 -1144297.31386 +1197 1189 1.49011611938e-8 +1237 1189 -183325.791023 +1238 1189 -591373.519429 +1239 1189 416676.259817 +1190 1190 16817023.5501 +1191 1190 -7.45058059692e-8 +1192 1190 -2239699.42799 +1193 1190 161794.959894 +1195 1190 -1144297.31386 +1196 1190 -3691281.65763 +1197 1190 4.842877388e-8 +1237 1190 -591373.519429 +1238 1190 -1907656.51429 +1239 1190 1344116.96715 +1191 1191 39001825.1235 +1195 1191 7.45058059692e-9 +1196 1191 1.86264514923e-8 +1197 1191 1397983.15475 +1237 1191 416676.259817 +1238 1191 1344116.96715 +1239 1191 -2538358.87127 +1192 1192 329627273.449 +1193 1192 65098710.708 +1193 1193 569419268.28 +1194 1194 790573.476703 +1195 1195 1768961.88645 +1196 1195 5200497.8108 +1197 1195 -2.23517417908e-8 +1198 1195 -465133.879389 +1199 1195 -1268546.94379 +1200 1195 2.04890966415e-8 +1237 1195 -89281.9039061 +1238 1195 -261158.775213 +1239 1195 -27106.375452 +1240 1195 -246970.319543 +1241 1195 -673555.416935 +1242 1195 484442.198447 +1196 1196 15396260.6937 +1197 1196 -5.96046447754e-8 +1198 1196 -1268546.94379 +1199 1196 -3459673.48306 +1200 1196 5.58793544769e-8 +1237 1196 -261158.775213 +1238 1196 -769227.571739 +1239 1196 9164.38855487 +1240 1196 -673555.416935 +1241 1196 -1836969.31891 +1242 1196 1321205.99576 +1197 1197 19430253.1852 +1198 1197 1.95577740669e-8 +1199 1197 5.21540641785e-8 +1200 1197 1501024.18544 +1237 1197 27106.375452 +1238 1197 -9164.3885549 +1239 1197 -7121142.78387 +1240 1197 484442.198447 +1241 1197 1321205.99576 +1242 1197 -2564546.07772 +1198 1198 2497002.01247 +1199 1198 5989724.5569 +1200 1198 -4.842877388e-8 +1201 1198 -645500.420471 +1202 1198 -1403261.78363 +1203 1198 2.79396772385e-8 +1237 1198 -246970.319543 +1238 1198 -673555.416935 +1239 1198 -484442.198447 +1240 1198 -150094.325529 +1241 1198 -356091.902114 +1242 1198 -41457.2556898 +1243 1198 -361895.781536 +1244 1198 -786729.959862 +1245 1198 588085.337671 +1199 1199 14552393.0113 +1200 1199 -1.19209289551e-7 +1201 1199 -1403261.78363 +1202 1199 -3050569.09485 +1203 1199 5.96046447754e-8 +1237 1199 -673555.416935 +1238 1199 -1836969.31891 +1239 1199 -1321205.99576 +1240 1199 -356091.902114 +1241 1199 -855385.258208 +1242 1199 17103.8438088 +1243 1199 -786729.959862 +1244 1199 -1710282.52144 +1245 1199 1278446.38624 +1200 1200 19697338.3132 +1201 1200 2.98023223877e-8 +1202 1200 6.51925802231e-8 +1203 1200 1699467.28443 +1237 1200 -484442.198447 +1238 1200 -1321205.99576 +1239 1200 -2564546.07772 +1240 1200 41457.2556898 +1241 1200 -17103.8438089 +1242 1200 -7353984.06298 +1243 1200 588085.337671 +1244 1200 1278446.38624 +1245 1200 -2616778.13471 +1201 1201 3805513.9161 +1202 1201 6969546.89216 +1203 1201 -4.09781932831e-8 +1204 1201 -900913.14915 +1205 1201 -1453085.72444 +1206 1201 2.23517417908e-8 +1240 1201 -361895.781536 +1241 1201 -786729.959862 +1242 1201 -588085.337671 +1243 1201 -304176.348108 +1244 1201 -544599.533137 +1245 1201 -61373.985991 +1246 1201 -570661.636677 +1247 1201 -920421.994641 +1248 1201 741520.302649 +1202 1202 13049076.8844 +1203 1202 -8.19563865662e-8 +1204 1202 -1453085.72444 +1205 1202 -2343686.65232 +1206 1202 3.35276126862e-8 +1240 1202 -786729.959862 +1241 1202 -1710282.52144 +1242 1202 -1278446.38624 +1243 1202 -544599.533137 +1244 1202 -995761.387374 +1245 1202 32978.3592395 +1246 1202 -920421.994641 +1247 1202 -1484551.60426 +1248 1202 1196000.48814 +1203 1203 20269767.9526 +1204 1203 2.04890966415e-8 +1205 1203 3.72529029846e-8 +1206 1203 2107431.22341 +1240 1203 -588085.337671 +1241 1203 -1278446.38624 +1242 1203 -2616778.13471 +1243 1203 61373.985991 +1244 1203 -32978.3592395 +1245 1203 -7832517.9294 +1246 1203 741520.302649 +1247 1203 1196000.48814 +1248 1203 -2731360.75727 +1204 1204 6311393.43185 +1205 1204 8279110.50413 +1206 1204 -5.21540641785e-8 +1207 1204 -2301041.15788 +1208 1204 -2614819.49759 +1209 1204 2.60770320892e-8 +1243 1204 -570661.636677 +1244 1204 -920421.994641 +1245 1204 -741520.302649 +1246 1204 -175770.085153 +1247 1204 -298827.840693 +1248 1204 -75251.5247454 +1249 1204 -955815.974241 +1250 1204 -1086154.51618 +1251 1204 929649.114512 +1205 1205 11193680.9464 +1206 1205 -7.45058059692e-8 +1207 1205 -2614819.49759 +1208 1205 -2971385.79271 +1209 1205 2.79396772385e-8 +1243 1205 -920421.994641 +1244 1205 -1484551.60426 +1245 1205 -1196000.48814 +1246 1205 -298827.840693 +1247 1205 -499398.232929 +1248 1205 55832.4159336 +1249 1205 -1086154.51618 +1250 1205 -1234266.49566 +1251 1205 1056419.44831 +1206 1206 19292741.7183 +1207 1206 3.72529029846e-9 +1208 1206 5.58793544769e-9 +1209 1206 427942.721782 +1243 1206 -741520.302649 +1244 1206 -1196000.48814 +1245 1206 -2731360.75727 +1246 1206 75251.5247453 +1247 1206 -55832.4159336 +1248 1206 -6893760.43768 +1249 1206 929649.114512 +1250 1206 1056419.44831 +1251 1206 -2324577.161 +1207 1207 11173430.2083 +1208 1207 7495059.08719 +1209 1207 -7.45058059692e-9 +1210 1207 -678836.974673 +1211 1207 -279932.773061 +1212 1207 7.45058059692e-9 +1246 1207 -955815.974241 +1247 1207 -1086154.51618 +1248 1207 -929649.114512 +1249 1207 -1698063.33368 +1250 1207 -676945.37624 +1251 1207 -148519.569575 +1252 1207 -1831641.90145 +1253 1207 -755316.248019 +1254 1207 1300948.03845 +1208 1208 6371949.42588 +1209 1208 1.11758708954e-8 +1210 1208 -279932.773061 +1211 1208 -115436.195077 +1212 1208 1.86264514923e-9 +1246 1208 -1086154.51618 +1247 1208 -1234266.49566 +1248 1208 -1056419.44831 +1249 1208 -676945.37624 +1250 1208 -252690.316209 +1251 1208 207978.412157 +1252 1208 -755316.248019 +1253 1208 -311470.61774 +1254 1208 536473.417918 +1209 1209 22658086.7563 +1211 1209 9.31322574615e-10 +1212 1209 4790763.62708 +1246 1209 -929649.114512 +1247 1209 -1056419.44831 +1248 1209 -2324577.161 +1249 1209 148519.569575 +1250 1209 -207978.412157 +1251 1209 -9252258.07493 +1252 1209 1300948.03845 +1253 1209 536473.417918 +1254 1209 -3684182.88731 +1210 1210 14370371.7322 +1211 1210 5.96046447754e-8 +1212 1210 -7.45058059692e-9 +1213 1210 -678836.974673 +1214 1210 279932.773061 +1215 1210 7.45058059692e-9 +1249 1210 -1831641.90145 +1250 1210 -755316.248019 +1251 1210 -1300948.03845 +1252 1210 -3460455.85927 +1253 1210 -6.33299350738e-8 +1254 1210 -5.58793544769e-9 +1255 1210 -1831641.90145 +1256 1210 755316.248019 +1257 1210 1300948.03845 +1211 1211 2443681.02578 +1212 1211 -3.72529029846e-9 +1213 1211 279932.773061 +1214 1211 -115436.195077 +1215 1211 -1.86264514923e-9 +1249 1211 -755316.248019 +1250 1211 -311470.61774 +1251 1211 -536473.417918 +1252 1211 -6.33299350738e-8 +1253 1211 -588450.353367 +1254 1211 429178.734334 +1255 1211 755316.248019 +1256 1211 -311470.61774 +1257 1211 -536473.417918 +1212 1212 27394702.6193 +1214 1212 -9.31322574615e-10 +1215 1212 4790763.62708 +1249 1212 -1300948.03845 +1250 1212 -536473.417918 +1251 1212 -3684182.88731 +1252 1212 3.72529029846e-9 +1253 1212 -429178.734334 +1254 1212 -12874095.835 +1255 1212 1300948.03845 +1256 1212 -536473.417918 +1257 1212 -3684182.88731 +1213 1213 11173430.2083 +1214 1213 -7495059.08719 +1215 1213 -7.45058059692e-9 +1216 1213 -2301041.15788 +1217 1213 2614819.49759 +1252 1213 -1831641.90145 +1253 1213 755316.248019 +1254 1213 -1300948.03845 +1255 1213 -1698063.33368 +1256 1213 676945.37624 +1257 1213 148519.569575 +1258 1213 -955815.974241 +1259 1213 1086154.51618 +1260 1213 929649.114512 +1214 1214 6371949.42588 +1215 1214 1.49011611938e-8 +1216 1214 2614819.49759 +1217 1214 -2971385.79271 +1252 1214 755316.248019 +1253 1214 -311470.61774 +1254 1214 536473.417918 +1255 1214 676945.37624 +1256 1214 -252690.316209 +1257 1214 207978.412157 +1258 1214 1086154.51618 +1259 1214 -1234266.49566 +1260 1214 -1056419.44831 +1215 1215 22658086.7563 +1216 1215 -1.67638063431e-8 +1217 1215 1.86264514923e-8 +1218 1215 427942.721782 +1252 1215 -1300948.03845 +1253 1215 536473.417918 +1254 1215 -3684182.88731 +1255 1215 -148519.569575 +1256 1215 -207978.412157 +1257 1215 -9252258.07493 +1258 1215 929649.114512 +1259 1215 -1056419.44831 +1260 1215 -2324577.161 +1216 1216 6311393.43185 +1217 1216 -8279110.50413 +1218 1216 4.47034835815e-8 +1219 1216 -900913.14915 +1220 1216 1453085.72444 +1221 1216 -1.86264514923e-8 +1255 1216 -955815.974241 +1256 1216 1086154.51618 +1257 1216 -929649.114512 +1258 1216 -175770.085153 +1259 1216 298827.840692 +1260 1216 75251.5247453 +1261 1216 -570661.636677 +1262 1216 920421.994641 +1263 1216 741520.302649 +1217 1217 11193680.9464 +1218 1217 -6.70552253723e-8 +1219 1217 1453085.72444 +1220 1217 -2343686.65232 +1221 1217 3.16649675369e-8 +1255 1217 1086154.51618 +1256 1217 -1234266.49566 +1257 1217 1056419.44831 +1258 1217 298827.840692 +1259 1217 -499398.232929 +1260 1217 55832.4159336 +1261 1217 920421.994641 +1262 1217 -1484551.60426 +1263 1217 -1196000.48814 +1218 1218 19292741.7183 +1219 1218 -1.67638063431e-8 +1220 1218 2.421438694e-8 +1221 1218 2107431.22341 +1255 1218 -929649.114512 +1256 1218 1056419.44831 +1257 1218 -2324577.161 +1258 1218 -75251.5247453 +1259 1218 -55832.4159336 +1260 1218 -6893760.43768 +1261 1218 741520.302649 +1262 1218 -1196000.48814 +1263 1218 -2731360.75727 +1219 1219 3805513.9161 +1220 1219 -6969546.89216 +1221 1219 2.98023223877e-8 +1222 1219 -645500.420471 +1223 1219 1403261.78363 +1224 1219 -2.04890966415e-8 +1258 1219 -570661.636677 +1259 1219 920421.994641 +1260 1219 -741520.302649 +1261 1219 -304176.348108 +1262 1219 544599.533137 +1263 1219 61373.985991 +1264 1219 -361895.781536 +1265 1219 786729.959862 +1266 1219 588085.337671 +1220 1220 13049076.8844 +1221 1220 -5.21540641785e-8 +1222 1220 1403261.78363 +1223 1220 -3050569.09485 +1224 1220 4.47034835815e-8 +1258 1220 920421.994641 +1259 1220 -1484551.60426 +1260 1220 1196000.48814 +1261 1220 544599.533137 +1262 1220 -995761.387374 +1263 1220 32978.3592395 +1264 1220 786729.959862 +1265 1220 -1710282.52144 +1266 1220 -1278446.38624 +1221 1221 20269767.9526 +1222 1221 -2.32830643654e-8 +1223 1221 5.02914190292e-8 +1224 1221 1699467.28443 +1258 1221 -741520.302649 +1259 1221 1196000.48814 +1260 1221 -2731360.75727 +1261 1221 -61373.985991 +1262 1221 -32978.3592395 +1263 1221 -7832517.9294 +1264 1221 588085.337671 +1265 1221 -1278446.38624 +1266 1221 -2616778.13471 +1222 1222 2497002.01247 +1223 1222 -5989724.5569 +1224 1222 3.91155481339e-8 +1225 1222 -465133.879389 +1226 1222 1268546.94379 +1227 1222 -2.60770320892e-8 +1261 1222 -361895.781536 +1262 1222 786729.959862 +1263 1222 -588085.337671 +1264 1222 -150094.32553 +1265 1222 356091.902114 +1266 1222 41457.2556898 +1267 1222 -246970.319543 +1268 1222 673555.416935 +1269 1222 484442.198447 +1223 1223 14552393.0113 +1224 1223 -1.04308128357e-7 +1225 1223 1268546.94379 +1226 1223 -3459673.48306 +1227 1223 7.45058059692e-8 +1261 1223 786729.959862 +1262 1223 -1710282.52144 +1263 1223 1278446.38624 +1264 1223 356091.902114 +1265 1223 -855385.258208 +1266 1223 17103.8438088 +1267 1223 673555.416935 +1268 1223 -1836969.31891 +1269 1223 -1321205.99576 +1224 1224 19697338.3132 +1225 1224 -2.88709998131e-8 +1226 1224 8.19563865662e-8 +1227 1224 1501024.18544 +1261 1224 -588085.337671 +1262 1224 1278446.38624 +1263 1224 -2616778.13471 +1264 1224 -41457.2556898 +1265 1224 -17103.8438089 +1266 1224 -7353984.06298 +1267 1224 484442.198447 +1268 1224 -1321205.99576 +1269 1224 -2564546.07772 +1225 1225 1768961.88645 +1226 1225 -5200497.8108 +1227 1225 3.16649675369e-8 +1228 1225 -354732.167298 +1229 1225 1144297.31386 +1230 1225 -6.51925802231e-9 +1264 1225 -246970.319543 +1265 1225 673555.416935 +1266 1225 -484442.198447 +1267 1225 -89281.9039061 +1268 1225 261158.775213 +1269 1225 27106.375452 +1226 1226 15396260.6937 +1227 1226 -8.94069671631e-8 +1228 1226 1144297.31386 +1229 1226 -3691281.65763 +1230 1226 1.86264514923e-8 +1264 1226 673555.416935 +1265 1226 -1836969.31891 +1266 1226 1321205.99576 +1267 1226 261158.775213 +1268 1226 -769227.571739 +1269 1226 9164.38855486 +1227 1227 19430253.1852 +1228 1227 -1.58324837685e-8 +1229 1227 5.58793544769e-8 +1230 1227 1397983.15475 +1264 1227 -484442.198447 +1265 1227 1321205.99576 +1266 1227 -2564546.07772 +1267 1227 -27106.375452 +1268 1227 -9164.38855492 +1269 1227 -7121142.78387 +1228 1228 2528206.97157 +1229 1228 -4472509.50178 +1230 1228 2.421438694e-8 +1231 1228 161793.620953 +1232 1228 1643724.74446 +1234 1228 -268071.177508 +1235 1228 1017991.81332 +1236 1228 -7.45058059692e-9 +1267 1228 -183325.791023 +1268 1228 591373.519429 +1269 1228 -416676.259817 +1229 1229 16817023.1576 +1230 1229 -7.45058059692e-8 +1231 1229 -2239700.15498 +1232 1229 -161793.620953 +1234 1229 1017991.81332 +1235 1229 -3865791.69616 +1236 1229 2.23517417908e-8 +1267 1229 591373.519429 +1268 1229 -1907656.51429 +1269 1229 1344116.96715 +1230 1230 39001825.1235 +1234 1230 -1.39698386192e-8 +1235 1230 5.21540641785e-8 +1236 1230 1324183.34296 +1267 1230 -416676.259817 +1268 1230 1344116.96715 +1269 1230 -2538358.87127 +1231 1231 329626980.943 +1232 1231 -65098171.9808 +1232 1232 569419560.785 +1233 1233 790573.476703 +1234 1234 983097.517609 +1235 1234 -3993191.05725 +1236 1234 5.58793544769e-8 +1235 1235 16310675.5472 +1236 1235 -2.30967998505e-7 +1236 1236 19175351.0344 +1237 1237 1902223.18074 +1238 1237 5590205.99024 +1239 1237 -2.98023223877e-8 +1240 1237 -77037.309496 +1241 1237 -210101.753171 +1242 1237 2.04890966415e-8 +1270 1237 -718178.246541 +1271 1237 -2108958.17516 +1272 1237 -27106.375452 +1273 1237 -300633.375282 +1274 1237 -819909.205315 +1275 1237 484442.198447 +1238 1238 16543837.5893 +1239 1238 -1.11758708954e-7 +1240 1238 -210101.753171 +1241 1238 -573004.781375 +1242 1238 5.58793544769e-8 +1270 1238 -2108958.17516 +1271 1238 -6236511.30779 +1272 1238 9164.38855486 +1273 1238 -819909.205315 +1274 1238 -2236116.0145 +1275 1238 1321205.99576 +1239 1239 30501291.4656 +1240 1239 1.95577740669e-8 +1241 1239 4.842877388e-8 +1242 1239 5508023.65257 +1270 1239 27106.375452 +1271 1239 -9164.3885549 +1272 1239 -19001911.4947 +1273 1239 484442.198447 +1274 1239 1321205.99576 +1275 1239 -5113778.6239 +1240 1240 2722764.08058 +1241 1240 6525095.96362 +1242 1240 -3.35276126862e-8 +1243 1240 -76937.6348731 +1244 1240 -167255.727985 +1245 1240 2.04890966415e-8 +1270 1240 -300633.375282 +1271 1240 -819909.205315 +1272 1240 -484442.198447 +1273 1240 -1057312.76972 +1274 1240 -2529098.59693 +1275 1240 -41457.2556898 +1276 1240 -451882.564601 +1277 1240 -982353.401306 +1278 1240 588085.337671 +1241 1241 15837837.4467 +1242 1241 -8.19563865662e-8 +1243 1241 -167255.727985 +1244 1241 -363599.408663 +1245 1241 4.65661287308e-8 +1270 1241 -819909.205315 +1271 1241 -2236116.0145 +1272 1241 -1321205.99576 +1273 1241 -2529098.59693 +1274 1241 -6126929.2712 +1275 1241 17103.8438088 +1276 1241 -982353.401306 +1277 1241 -2135550.8724 +1278 1241 1278446.38624 +1242 1242 31131706.3633 +1243 1242 2.88709998131e-8 +1244 1242 6.33299350738e-8 +1245 1242 5787467.22392 +1270 1242 -484442.198447 +1271 1242 -1321205.99576 +1272 1242 -5113778.6239 +1273 1242 41457.2556898 +1274 1242 -17103.8438089 +1275 1242 -19507359.9875 +1276 1242 588085.337671 +1277 1242 1278446.38624 +1278 1242 -5270750.35301 +1243 1243 4267595.81742 +1244 1243 7796294.34309 +1245 1243 -7.45058059692e-8 +1246 1243 -4790.05038357 +1247 1243 -7725.88771543 +1248 1243 2.60770320892e-8 +1273 1243 -451882.564601 +1274 1243 -982353.401306 +1275 1243 -588085.337671 +1276 1243 -1747893.8743 +1277 1243 -3178566.02079 +1278 1243 -61373.985991 +1279 1243 -749357.926948 +1280 1243 -1208641.81766 +1281 1243 741520.302649 +1244 1244 14559614.5621 +1245 1244 -1.34110450745e-7 +1246 1244 -7725.88771543 +1247 1244 -12461.1092184 +1248 1244 3.91155481339e-8 +1273 1244 -982353.401306 +1274 1244 -2135550.8724 +1275 1244 -1278446.38624 +1276 1244 -3178566.02079 +1277 1244 -5907985.37222 +1278 1244 32978.3592395 +1279 1244 -1208641.81766 +1280 1244 -1949422.28655 +1281 1244 1196000.48814 +1245 1245 32450763.4987 +1246 1245 1.67638063431e-8 +1247 1245 2.79396772385e-8 +1248 1245 6373223.13441 +1273 1245 -588085.337671 +1274 1245 -1278446.38624 +1275 1245 -5270750.35301 +1276 1245 61373.985991 +1277 1245 -32978.3592395 +1278 1245 -20553554.1703 +1279 1245 741520.302649 +1280 1245 1196000.48814 +1281 1245 -5606492.51233 +1246 1246 6563466.92161 +1247 1246 8716568.29938 +1248 1246 3.72529029846e-9 +1249 1246 -797223.130676 +1250 1246 -905935.375768 +1251 1246 9.31322574615e-9 +1276 1246 -749357.926948 +1277 1246 -1208641.81766 +1278 1246 -741520.302649 +1279 1246 -2303950.19297 +1280 1246 -3145795.04337 +1281 1246 -75251.5247453 +1282 1246 -1005897.92455 +1283 1246 -1143065.82336 +1284 1246 929649.114512 +1247 1247 11934357.6383 +1248 1247 7.45058059692e-9 +1249 1247 -905935.375768 +1250 1247 -1029472.01792 +1251 1247 1.11758708954e-8 +1276 1247 -1208641.81766 +1277 1247 -1949422.28655 +1278 1247 -1196000.48814 +1279 1247 -3145795.04337 +1280 1247 -4425847.45616 +1281 1247 55832.4159336 +1282 1247 -1143065.82336 +1283 1247 -1298938.43563 +1284 1247 1056419.44831 +1248 1248 30008520.3637 +1249 1248 1.49011611938e-8 +1250 1248 1.67638063431e-8 +1251 1248 4063389.39899 +1276 1248 -741520.302649 +1277 1248 -1196000.48814 +1278 1248 -5606492.51233 +1279 1248 75251.5247453 +1280 1248 -55832.4159336 +1281 1248 -18547112.2702 +1282 1248 929649.114512 +1283 1248 1056419.44831 +1284 1248 -4341829.75862 +1249 1249 13789595.5493 +1250 1249 8526963.46189 +1251 1249 -2.98023223877e-8 +1252 1249 2189391.67461 +1253 1249 902841.927676 +1254 1249 3.72529029846e-9 +1279 1249 -1005897.92455 +1280 1249 -1143065.82336 +1281 1249 -929649.114512 +1282 1249 -6588902.98151 +1283 1249 -3583442.90451 +1284 1249 -148519.569575 +1285 1249 -3101441.97781 +1286 1249 -1278945.14549 +1287 1249 1300948.03845 +1250 1250 6744151.21474 +1251 1250 -4.47034835815e-8 +1252 1250 902841.927676 +1253 1250 372305.949557 +1254 1250 2.79396772385e-9 +1279 1250 -1143065.82336 +1280 1250 -1298938.43563 +1281 1250 -1056419.44831 +1282 1250 -3583442.90451 +1283 1250 -2462219.22114 +1284 1250 207978.412157 +1285 1250 -1278945.14549 +1286 1250 -527400.059995 +1287 1250 536473.417918 +1251 1251 37051709.8254 +1253 1251 -9.31322574615e-10 +1254 1251 10537577.5004 +1279 1251 -929649.114512 +1280 1251 -1056419.44831 +1281 1251 -4341829.75862 +1282 1251 148519.569575 +1283 1251 -207978.412157 +1284 1251 -23874563.7281 +1285 1251 1300948.03845 +1286 1251 536473.417918 +1287 1251 -8175265.11488 +1252 1252 19732335.5073 +1253 1252 5.06639480591e-7 +1254 1252 7.45058059692e-9 +1255 1252 2189391.67461 +1256 1252 -902841.927676 +1257 1252 3.72529029846e-9 +1282 1252 -3101441.97781 +1283 1252 -1278945.14549 +1284 1252 -1300948.03845 +1285 1252 -10784495.2387 +1286 1252 -4.61935997009e-7 +1287 1252 -1.86264514923e-9 +1288 1252 -3101441.97781 +1289 1252 1278945.14549 +1290 1252 1300948.03845 +1253 1253 3355482.70929 +1254 1253 7.45058059692e-9 +1255 1253 -902841.927676 +1256 1253 372305.949557 +1257 1253 -2.79396772385e-9 +1282 1253 -1278945.14549 +1283 1253 -527400.059995 +1284 1253 -536473.417918 +1285 1253 -4.61935997009e-7 +1286 1253 -1833902.89956 +1287 1253 429178.734334 +1288 1253 1278945.14549 +1289 1253 -527400.059995 +1290 1253 -536473.417918 +1254 1254 47437791.9854 +1256 1254 9.31322574615e-10 +1257 1254 10537577.5004 +1282 1254 -1300948.03845 +1283 1254 -536473.417918 +1284 1254 -8175265.11488 +1286 1254 -429178.734334 +1287 1254 -31919955.1469 +1288 1254 1300948.03845 +1289 1254 -536473.417918 +1290 1254 -8175265.11488 +1255 1255 13789595.5493 +1256 1255 -8526963.46189 +1257 1255 1.86264514923e-8 +1258 1255 -797223.130676 +1259 1255 905935.375768 +1260 1255 -9.31322574615e-9 +1285 1255 -3101441.97781 +1286 1255 1278945.14549 +1287 1255 -1300948.03845 +1288 1255 -6588902.98151 +1289 1255 3583442.90451 +1290 1255 148519.569575 +1291 1255 -1005897.92455 +1292 1255 1143065.82336 +1293 1255 929649.114512 +1256 1256 6744151.21474 +1257 1256 -2.23517417908e-8 +1258 1256 905935.375768 +1259 1256 -1029472.01792 +1260 1256 9.31322574615e-9 +1285 1256 1278945.14549 +1286 1256 -527400.059995 +1287 1256 536473.417918 +1288 1256 3583442.90451 +1289 1256 -2462219.22114 +1290 1256 207978.412157 +1291 1256 1143065.82336 +1292 1256 -1298938.43563 +1293 1256 -1056419.44831 +1257 1257 37051709.8254 +1258 1257 -1.11758708954e-8 +1259 1257 1.30385160446e-8 +1260 1257 4063389.39899 +1285 1257 -1300948.03845 +1286 1257 536473.417918 +1287 1257 -8175265.11488 +1288 1257 -148519.569575 +1289 1257 -207978.412157 +1290 1257 -23874563.7281 +1291 1257 929649.114512 +1292 1257 -1056419.44831 +1293 1257 -4341829.75862 +1258 1258 6563466.92161 +1259 1258 -8716568.29938 +1260 1258 7.45058059692e-9 +1261 1258 -4790.05038359 +1262 1258 7725.88771547 +1263 1258 -7.45058059692e-9 +1288 1258 -1005897.92455 +1289 1258 1143065.82336 +1290 1258 -929649.114512 +1291 1258 -2303950.19297 +1292 1258 3145795.04337 +1293 1258 75251.5247453 +1294 1258 -749357.926948 +1295 1258 1208641.81766 +1296 1258 741520.302649 +1259 1259 11934357.6383 +1260 1259 -7.45058059692e-9 +1261 1259 7725.88771547 +1262 1259 -12461.1092185 +1263 1259 1.30385160446e-8 +1288 1259 1143065.82336 +1289 1259 -1298938.43563 +1290 1259 1056419.44831 +1291 1259 3145795.04337 +1292 1259 -4425847.45616 +1293 1259 55832.4159336 +1294 1259 1208641.81766 +1295 1259 -1949422.28655 +1296 1259 -1196000.48814 +1260 1260 30008520.3637 +1261 1260 -2.23517417908e-8 +1262 1260 3.53902578354e-8 +1263 1260 6373223.13441 +1288 1260 -929649.114512 +1289 1260 1056419.44831 +1290 1260 -4341829.75862 +1291 1260 -75251.5247453 +1292 1260 -55832.4159336 +1293 1260 -18547112.2702 +1294 1260 741520.302649 +1295 1260 -1196000.48814 +1296 1260 -5606492.51233 +1261 1261 4267595.81742 +1262 1261 -7796294.34309 +1263 1261 6.70552253723e-8 +1264 1261 -76937.6348731 +1265 1261 167255.727985 +1266 1261 -2.51457095146e-8 +1291 1261 -749357.926948 +1292 1261 1208641.81766 +1293 1261 -741520.302649 +1294 1261 -1747893.8743 +1295 1261 3178566.02079 +1296 1261 61373.985991 +1297 1261 -451882.564601 +1298 1261 982353.401306 +1299 1261 588085.337671 +1262 1262 14559614.5621 +1263 1262 -1.19209289551e-7 +1264 1262 167255.727985 +1265 1262 -363599.408663 +1266 1262 5.40167093277e-8 +1291 1262 1208641.81766 +1292 1262 -1949422.28655 +1293 1262 1196000.48814 +1294 1262 3178566.02079 +1295 1262 -5907985.37222 +1296 1262 32978.3592395 +1297 1262 982353.401306 +1298 1262 -2135550.8724 +1299 1262 -1278446.38624 +1263 1263 32450763.4987 +1264 1263 -1.86264514923e-8 +1265 1263 4.28408384323e-8 +1266 1263 5787467.22392 +1291 1263 -741520.302649 +1292 1263 1196000.48814 +1293 1263 -5606492.51233 +1294 1263 -61373.985991 +1295 1263 -32978.3592395 +1296 1263 -20553554.1703 +1297 1263 588085.337671 +1298 1263 -1278446.38624 +1299 1263 -5270750.35301 +1264 1264 2722764.08058 +1265 1264 -6525095.96362 +1266 1264 5.02914190292e-8 +1267 1264 -77037.309496 +1268 1264 210101.753171 +1269 1264 -2.70083546639e-8 +1294 1264 -451882.564601 +1295 1264 982353.401306 +1296 1264 -588085.337671 +1297 1264 -1057312.76972 +1298 1264 2529098.59693 +1299 1264 41457.2556898 +1300 1264 -300633.375282 +1301 1264 819909.205315 +1302 1264 484442.198447 +1265 1265 15837837.4467 +1266 1265 -1.26659870148e-7 +1267 1265 210101.753171 +1268 1265 -573004.781375 +1269 1265 7.45058059692e-8 +1294 1265 982353.401306 +1295 1265 -2135550.8724 +1296 1265 1278446.38624 +1297 1265 2529098.59693 +1298 1265 -6126929.2712 +1299 1265 17103.8438089 +1300 1265 819909.205315 +1301 1265 -2236116.0145 +1302 1265 -1321205.99576 +1266 1266 31131706.3633 +1267 1266 -2.60770320892e-8 +1268 1266 7.82310962677e-8 +1269 1266 5508023.65257 +1294 1266 -588085.337671 +1295 1266 1278446.38624 +1296 1266 -5270750.35301 +1297 1266 -41457.2556898 +1298 1266 -17103.8438089 +1299 1266 -19507359.9875 +1300 1266 484442.198447 +1301 1266 -1321205.99576 +1302 1266 -5113778.6239 +1267 1267 1902223.18074 +1268 1267 -5590205.99024 +1269 1267 4.28408384323e-8 +1297 1267 -300633.375282 +1298 1267 819909.205315 +1299 1267 -484442.198447 +1300 1267 -718178.246541 +1301 1267 2108958.17516 +1302 1267 27106.375452 +1268 1268 16543837.5893 +1269 1268 -1.41561031342e-7 +1297 1268 819909.205315 +1298 1268 -2236116.0145 +1299 1268 1321205.99576 +1300 1268 2108958.17516 +1301 1268 -6236511.30779 +1302 1268 9164.38855485 +1269 1269 30501291.4656 +1297 1269 -484442.198447 +1298 1269 1321205.99576 +1299 1269 -5113778.6239 +1300 1269 -27106.375452 +1301 1269 -9164.38855491 +1302 1269 -19001911.4947 +1270 1270 1902223.18074 +1271 1270 5590205.99024 +1272 1270 -5.40167093277e-8 +1273 1270 -77037.309496 +1274 1270 -210101.753171 +1275 1270 2.79396772385e-8 +1306 1270 -183325.791023 +1307 1270 -591373.519429 +1308 1270 -416676.259817 +1312 1270 -89281.9039061 +1313 1270 -261158.775213 +1314 1270 -27106.375452 +1315 1270 -246970.319543 +1316 1270 -673555.416935 +1317 1270 484442.198447 +1271 1271 16543837.5893 +1272 1271 -1.56462192535e-7 +1273 1271 -210101.753171 +1274 1271 -573004.781375 +1275 1271 7.07805156708e-8 +1306 1271 -591373.519429 +1307 1271 -1907656.51429 +1308 1271 -1344116.96715 +1312 1271 -261158.775213 +1313 1271 -769227.571739 +1314 1271 9164.38855484 +1315 1271 -673555.416935 +1316 1271 -1836969.31891 +1317 1271 1321205.99576 +1272 1272 30501291.4656 +1273 1272 1.95577740669e-8 +1274 1272 5.21540641785e-8 +1275 1272 5508023.65257 +1306 1272 -416676.259817 +1307 1272 -1344116.96715 +1308 1272 -2538358.87127 +1312 1272 27106.375452 +1313 1272 -9164.3885549 +1314 1272 -7121142.78386 +1315 1272 484442.198447 +1316 1272 1321205.99576 +1317 1272 -2564546.07772 +1273 1273 2722764.08058 +1274 1273 6525095.96362 +1275 1273 -5.96046447754e-8 +1276 1273 -76937.6348732 +1277 1273 -167255.727985 +1278 1273 3.81842255592e-8 +1312 1273 -246970.319543 +1313 1273 -673555.416935 +1314 1273 -484442.198447 +1315 1273 -150094.325529 +1316 1273 -356091.902113 +1317 1273 -41457.2556898 +1318 1273 -361895.781536 +1319 1273 -786729.959862 +1320 1273 588085.337671 +1274 1274 15837837.4467 +1275 1274 -1.41561031342e-7 +1276 1274 -167255.727985 +1277 1274 -363599.408663 +1278 1274 8.38190317154e-8 +1312 1274 -673555.416935 +1313 1274 -1836969.31891 +1314 1274 -1321205.99576 +1315 1274 -356091.902113 +1316 1274 -855385.258208 +1317 1274 17103.8438088 +1318 1274 -786729.959862 +1319 1274 -1710282.52144 +1320 1274 1278446.38624 +1275 1275 31131706.3633 +1276 1275 2.51457095146e-8 +1277 1275 5.40167093277e-8 +1278 1275 5787467.22392 +1312 1275 -484442.198447 +1313 1275 -1321205.99576 +1314 1275 -2564546.07772 +1315 1275 41457.2556898 +1316 1275 -17103.8438089 +1317 1275 -7353984.06298 +1318 1275 588085.337671 +1319 1275 1278446.38624 +1320 1275 -2616778.13471 +1276 1276 4267595.81742 +1277 1276 7796294.34309 +1278 1276 -1.49011611938e-8 +1279 1276 -4790.05038368 +1280 1276 -7725.88771562 +1281 1276 1.49011611938e-8 +1315 1276 -361895.781536 +1316 1276 -786729.959862 +1317 1276 -588085.337671 +1318 1276 -304176.348108 +1319 1276 -544599.533136 +1320 1276 -61373.985991 +1321 1276 -570661.636677 +1322 1276 -920421.994641 +1323 1276 741520.302649 +1277 1277 14559614.5621 +1278 1277 -3.72529029846e-8 +1279 1277 -7725.88771562 +1280 1277 -12461.1092187 +1281 1277 2.421438694e-8 +1315 1277 -786729.959862 +1316 1277 -1710282.52144 +1317 1277 -1278446.38624 +1318 1277 -544599.533136 +1319 1277 -995761.387374 +1320 1277 32978.3592395 +1321 1277 -920421.994641 +1322 1277 -1484551.60426 +1323 1277 1196000.48814 +1278 1278 32450763.4987 +1279 1278 1.30385160446e-8 +1280 1278 2.04890966415e-8 +1281 1278 6373223.13441 +1315 1278 -588085.337671 +1316 1278 -1278446.38624 +1317 1278 -2616778.13471 +1318 1278 61373.985991 +1319 1278 -32978.3592395 +1320 1278 -7832517.9294 +1321 1278 741520.302649 +1322 1278 1196000.48814 +1323 1278 -2731360.75727 +1279 1279 6563466.92161 +1280 1279 8716568.29938 +1281 1279 -4.842877388e-8 +1282 1279 -797223.130676 +1283 1279 -905935.375769 +1284 1279 2.421438694e-8 +1318 1279 -570661.636677 +1319 1279 -920421.994641 +1320 1279 -741520.302649 +1321 1279 -175770.085153 +1322 1279 -298827.840692 +1323 1279 -75251.5247454 +1324 1279 -955815.974241 +1325 1279 -1086154.51618 +1326 1279 929649.114512 +1280 1280 11934357.6383 +1281 1280 -7.45058059692e-8 +1282 1280 -905935.375769 +1283 1280 -1029472.01792 +1284 1280 2.79396772385e-8 +1318 1280 -920421.994641 +1319 1280 -1484551.60426 +1320 1280 -1196000.48814 +1321 1280 -298827.840692 +1322 1280 -499398.232928 +1323 1280 55832.4159336 +1324 1280 -1086154.51618 +1325 1280 -1234266.49566 +1326 1280 1056419.44831 +1281 1281 30008520.3637 +1282 1281 3.72529029846e-9 +1283 1281 5.58793544769e-9 +1284 1281 4063389.39899 +1318 1281 -741520.302649 +1319 1281 -1196000.48814 +1320 1281 -2731360.75727 +1321 1281 75251.5247453 +1322 1281 -55832.4159336 +1323 1281 -6893760.43768 +1324 1281 929649.114512 +1325 1281 1056419.44831 +1326 1281 -2324577.161 +1282 1282 13789595.5493 +1283 1282 8526963.46189 +1284 1282 -7.45058059692e-9 +1285 1282 2189391.67461 +1286 1282 902841.927676 +1287 1282 1.30385160446e-8 +1321 1282 -955815.974241 +1322 1282 -1086154.51618 +1323 1282 -929649.114512 +1324 1282 -1698063.33368 +1325 1282 -676945.37624 +1326 1282 -148519.569575 +1327 1282 -1831641.90145 +1328 1282 -755316.248019 +1329 1282 1300948.03845 +1283 1283 6744151.21474 +1284 1283 1.49011611938e-8 +1285 1283 902841.927676 +1286 1283 372305.949557 +1287 1283 5.58793544769e-9 +1321 1283 -1086154.51618 +1322 1283 -1234266.49566 +1323 1283 -1056419.44831 +1324 1283 -676945.37624 +1325 1283 -252690.316209 +1326 1283 207978.412157 +1327 1283 -755316.248019 +1328 1283 -311470.61774 +1329 1283 536473.417918 +1284 1284 37051709.8254 +1285 1284 3.72529029846e-9 +1286 1284 1.86264514923e-9 +1287 1284 10537577.5004 +1321 1284 -929649.114512 +1322 1284 -1056419.44831 +1323 1284 -2324577.161 +1324 1284 148519.569575 +1325 1284 -207978.412157 +1326 1284 -9252258.07493 +1327 1284 1300948.03845 +1328 1284 536473.417918 +1329 1284 -3684182.88731 +1285 1285 19732335.5073 +1286 1285 4.54485416412e-7 +1287 1285 -1.49011611938e-8 +1288 1285 2189391.67461 +1289 1285 -902841.927676 +1290 1285 1.30385160446e-8 +1324 1285 -1831641.90145 +1325 1285 -755316.248019 +1326 1285 -1300948.03845 +1327 1285 -3460455.85927 +1328 1285 -1.11758708954e-8 +1329 1285 -5.58793544769e-9 +1330 1285 -1831641.90145 +1331 1285 755316.248019 +1332 1285 1300948.03845 +1286 1286 3355482.70929 +1287 1286 1.49011611938e-8 +1288 1286 -902841.927676 +1289 1286 372305.949557 +1290 1286 -5.58793544769e-9 +1324 1286 -755316.248019 +1325 1286 -311470.61774 +1326 1286 -536473.417918 +1327 1286 -1.11758708954e-8 +1328 1286 -588450.353367 +1329 1286 429178.734334 +1330 1286 755316.248019 +1331 1286 -311470.61774 +1332 1286 -536473.417918 +1287 1287 47437791.9854 +1288 1287 3.72529029846e-9 +1289 1287 -1.86264514923e-9 +1290 1287 10537577.5004 +1324 1287 -1300948.03845 +1325 1287 -536473.417918 +1326 1287 -3684182.88731 +1327 1287 3.72529029846e-9 +1328 1287 -429178.734334 +1329 1287 -12874095.835 +1330 1287 1300948.03845 +1331 1287 -536473.417918 +1332 1287 -3684182.88731 +1288 1288 13789595.5493 +1289 1288 -8526963.46189 +1290 1288 -1.49011611938e-8 +1291 1288 -797223.130677 +1292 1288 905935.375769 +1293 1288 1.86264514923e-9 +1327 1288 -1831641.90145 +1328 1288 755316.248019 +1329 1288 -1300948.03845 +1330 1288 -1698063.33368 +1331 1288 676945.37624 +1332 1288 148519.569575 +1333 1288 -955815.974241 +1334 1288 1086154.51618 +1335 1288 929649.114512 +1289 1289 6744151.21474 +1290 1289 2.23517417908e-8 +1291 1289 905935.375769 +1292 1289 -1029472.01792 +1293 1289 -1.86264514923e-9 +1327 1289 755316.248019 +1328 1289 -311470.61774 +1329 1289 536473.417918 +1330 1289 676945.37624 +1331 1289 -252690.316209 +1332 1289 207978.412157 +1333 1289 1086154.51618 +1334 1289 -1234266.49566 +1335 1289 -1056419.44831 +1290 1290 37051709.8254 +1291 1290 -9.31322574615e-9 +1292 1290 1.11758708954e-8 +1293 1290 4063389.39899 +1327 1290 -1300948.03845 +1328 1290 536473.417918 +1329 1290 -3684182.88731 +1330 1290 -148519.569575 +1331 1290 -207978.412157 +1332 1290 -9252258.07493 +1333 1290 929649.114512 +1334 1290 -1056419.44831 +1335 1290 -2324577.161 +1291 1291 6563466.92161 +1292 1291 -8716568.29938 +1293 1291 4.47034835815e-8 +1294 1291 -4790.05038369 +1295 1291 7725.88771563 +1296 1291 -1.11758708954e-8 +1330 1291 -955815.974241 +1331 1291 1086154.51618 +1332 1291 -929649.114512 +1333 1291 -175770.085153 +1334 1291 298827.840692 +1335 1291 75251.5247453 +1336 1291 -570661.636677 +1337 1291 920421.994641 +1338 1291 741520.302649 +1292 1292 11934357.6383 +1293 1292 -5.96046447754e-8 +1294 1292 7725.88771563 +1295 1292 -12461.1092188 +1296 1292 1.67638063431e-8 +1330 1292 1086154.51618 +1331 1292 -1234266.49566 +1332 1292 1056419.44831 +1333 1292 298827.840692 +1334 1292 -499398.232928 +1335 1292 55832.4159336 +1336 1292 920421.994641 +1337 1292 -1484551.60426 +1338 1292 -1196000.48814 +1293 1293 30008520.3637 +1294 1293 -1.11758708954e-8 +1295 1293 2.04890966415e-8 +1296 1293 6373223.13441 +1330 1293 -929649.114512 +1331 1293 1056419.44831 +1332 1293 -2324577.161 +1333 1293 -75251.5247453 +1334 1293 -55832.4159336 +1335 1293 -6893760.43768 +1336 1293 741520.302649 +1337 1293 -1196000.48814 +1338 1293 -2731360.75727 +1294 1294 4267595.81742 +1295 1294 -7796294.34309 +1296 1294 7.45058059692e-9 +1297 1294 -76937.6348732 +1298 1294 167255.727985 +1299 1294 -1.11758708954e-8 +1333 1294 -570661.636677 +1334 1294 920421.994641 +1335 1294 -741520.302649 +1336 1294 -304176.348108 +1337 1294 544599.533136 +1338 1294 61373.985991 +1339 1294 -361895.781536 +1340 1294 786729.959862 +1341 1294 588085.337671 +1295 1295 14559614.5621 +1296 1295 -1.49011611938e-8 +1297 1295 167255.727985 +1298 1295 -363599.408663 +1299 1295 2.421438694e-8 +1333 1295 920421.994641 +1334 1295 -1484551.60426 +1335 1295 1196000.48814 +1336 1295 544599.533136 +1337 1295 -995761.387374 +1338 1295 32978.3592395 +1339 1295 786729.959862 +1340 1295 -1710282.52144 +1341 1295 -1278446.38624 +1296 1296 32450763.4987 +1297 1296 -2.23517417908e-8 +1298 1296 5.02914190292e-8 +1299 1296 5787467.22392 +1333 1296 -741520.302649 +1334 1296 1196000.48814 +1335 1296 -2731360.75727 +1336 1296 -61373.985991 +1337 1296 -32978.3592395 +1338 1296 -7832517.9294 +1339 1296 588085.337671 +1340 1296 -1278446.38624 +1341 1296 -2616778.13471 +1297 1297 2722764.08058 +1298 1297 -6525095.96362 +1299 1297 4.47034835815e-8 +1300 1297 -77037.309496 +1301 1297 210101.753171 +1302 1297 -2.04890966415e-8 +1336 1297 -361895.781536 +1337 1297 786729.959862 +1338 1297 -588085.337671 +1339 1297 -150094.325529 +1340 1297 356091.902113 +1341 1297 41457.2556898 +1342 1297 -246970.319543 +1343 1297 673555.416935 +1344 1297 484442.198447 +1298 1298 15837837.4467 +1299 1298 -1.04308128357e-7 +1300 1298 210101.753171 +1301 1298 -573004.781375 +1302 1298 5.96046447754e-8 +1336 1298 786729.959862 +1337 1298 -1710282.52144 +1338 1298 1278446.38624 +1339 1298 356091.902113 +1340 1298 -855385.258208 +1341 1298 17103.8438088 +1342 1298 673555.416935 +1343 1298 -1836969.31891 +1344 1298 -1321205.99576 +1299 1299 31131706.3633 +1300 1299 -2.88709998131e-8 +1301 1299 7.82310962677e-8 +1302 1299 5508023.65257 +1336 1299 -588085.337671 +1337 1299 1278446.38624 +1338 1299 -2616778.13471 +1339 1299 -41457.2556898 +1340 1299 -17103.8438089 +1341 1299 -7353984.06298 +1342 1299 484442.198447 +1343 1299 -1321205.99576 +1344 1299 -2564546.07772 +1300 1300 1902223.18074 +1301 1300 -5590205.99024 +1302 1300 5.21540641785e-8 +1339 1300 -246970.319543 +1340 1300 673555.416935 +1341 1300 -484442.198447 +1342 1300 -89281.9039061 +1343 1300 261158.775213 +1344 1300 27106.375452 +1345 1300 -183325.791023 +1346 1300 591373.519429 +1347 1300 416676.259817 +1301 1301 16543837.5893 +1302 1301 -1.41561031342e-7 +1339 1301 673555.416935 +1340 1301 -1836969.31891 +1341 1301 1321205.99576 +1342 1301 261158.775213 +1343 1301 -769227.571739 +1344 1301 9164.38855483 +1345 1301 591373.519429 +1346 1301 -1907656.51429 +1347 1301 -1344116.96715 +1302 1302 30501291.4656 +1339 1302 -484442.198447 +1340 1302 1321205.99576 +1341 1302 -2564546.07772 +1342 1302 -27106.375452 +1343 1302 -9164.38855491 +1344 1302 -7121142.78387 +1345 1302 416676.259817 +1346 1302 -1344116.96715 +1347 1302 -2538358.87127 +1303 1303 983097.51761 +1304 1303 3993191.05725 +1305 1303 -5.96046447754e-8 +1306 1303 -268071.177508 +1307 1303 -1017991.81332 +1308 1303 9.31322574615e-9 +1354 1303 22134.9913963 +1355 1303 90320.994053 +1356 1303 -18909.0650259 +1357 1303 -144050.092155 +1358 1303 -547025.666412 +1359 1303 358351.461509 +1304 1304 16310675.5472 +1305 1304 -2.68220901489e-7 +1306 1304 -1017991.81332 +1307 1304 -3865791.69616 +1308 1304 2.98023223877e-8 +1354 1304 90320.994053 +1355 1304 370626.696215 +1356 1304 4631.25243448 +1357 1304 -547025.666412 +1358 1304 -2077312.65726 +1359 1304 1360828.33484 +1305 1305 19175351.0344 +1306 1305 9.31322574615e-9 +1307 1305 2.98023223877e-8 +1308 1305 1324183.34296 +1354 1305 18909.0650259 +1355 1305 -4631.25243456 +1356 1305 -5293544.01962 +1357 1305 358351.461509 +1358 1305 1360828.33484 +1359 1305 -2284273.33935 +1306 1306 2528206.57902 +1307 1306 4472508.77878 +1308 1306 -3.72529029846e-8 +1309 1306 161794.959894 +1310 1306 -1643725.47145 +1312 1306 -354732.167298 +1313 1306 -1144297.31386 +1314 1306 2.23517417908e-8 +1354 1306 -144050.092155 +1355 1306 -547025.666412 +1356 1306 -358351.461509 +1357 1306 -474420.971735 +1358 1306 131724.797328 +1359 1306 -23329.9193232 +1360 1306 800256.904552 +1361 1306 8988441.97885 +1363 1306 -193836.559394 +1364 1306 -625279.223852 +1365 1306 416676.259817 +1307 1307 16817023.5501 +1308 1307 -1.19209289551e-7 +1309 1307 2239699.42799 +1310 1307 -161794.959894 +1312 1307 -1144297.31386 +1313 1307 -3691281.65763 +1314 1307 7.07805156708e-8 +1354 1307 -547025.666412 +1355 1307 -2077312.65726 +1356 1307 -1360828.33484 +1357 1307 131724.797328 +1358 1307 -28174.604603 +1359 1307 6684.54707694 +1360 1307 -6040684.68178 +1361 1307 -800256.904552 +1363 1307 -625279.223852 +1364 1307 -2017029.75436 +1365 1307 1344116.96715 +1308 1308 39001825.1235 +1312 1308 1.86264514923e-8 +1313 1308 5.58793544769e-8 +1314 1308 1397983.15475 +1354 1308 -358351.461509 +1355 1308 -1360828.33484 +1356 1308 -2284273.33935 +1357 1308 23329.9193232 +1358 1308 -6684.54707697 +1359 1308 -14301137.6219 +1363 1308 416676.259817 +1364 1308 1344116.96715 +1365 1308 -2296979.40748 +1309 1309 329627273.449 +1310 1309 65098710.708 +1357 1309 -800256.904552 +1358 1309 6040684.68178 +1360 1309 64525494.457 +1361 1309 -4237812.99363 +1310 1310 569419268.28 +1357 1310 -8988441.97885 +1358 1310 800256.904552 +1360 1310 -4237812.99363 +1361 1310 48915452.1011 +1311 1311 790573.476703 +1362 1311 -357777.777778 +1312 1312 1768961.88645 +1313 1312 5200497.8108 +1314 1312 -3.53902578354e-8 +1315 1312 -465133.879389 +1316 1312 -1268546.94379 +1317 1312 2.23517417908e-8 +1357 1312 -193836.559394 +1358 1312 -625279.223852 +1359 1312 -416676.259817 +1363 1312 24667.0078013 +1364 1312 73754.1287476 +1365 1312 -27106.375452 +1366 1312 -260348.273704 +1367 1312 -710040.746465 +1368 1312 484442.198447 +1313 1313 15396260.6937 +1314 1313 -1.11758708954e-7 +1315 1313 -1268546.94379 +1316 1313 -3459673.48306 +1317 1313 5.96046447754e-8 +1357 1313 -625279.223852 +1358 1313 -2017029.75436 +1359 1313 -1344116.96715 +1363 1313 73754.1287476 +1364 1313 222052.369366 +1365 1313 9164.38855485 +1366 1313 -710040.746465 +1367 1313 -1936474.76309 +1368 1313 1321205.99576 +1314 1314 19430253.1852 +1315 1314 1.95577740669e-8 +1316 1314 5.21540641785e-8 +1317 1314 1501024.18544 +1357 1314 -416676.259817 +1358 1314 -1344116.96715 +1359 1314 -2296979.40748 +1363 1314 27106.375452 +1364 1314 -9164.3885549 +1365 1314 -5492991.14412 +1366 1314 484442.198447 +1367 1314 1321205.99576 +1368 1314 -2315242.24089 +1315 1315 2497002.01247 +1316 1315 5989724.5569 +1317 1315 -5.21540641785e-8 +1318 1315 -645500.420471 +1319 1315 -1403261.78363 +1320 1315 2.98023223877e-8 +1363 1315 -260348.273704 +1364 1315 -710040.746465 +1365 1315 -484442.198447 +1366 1315 12227.6197724 +1367 1315 33038.3525625 +1368 1315 -41457.2556898 +1369 1315 -379286.632065 +1370 1315 -824536.156663 +1371 1315 588085.337671 +1316 1316 14552393.0113 +1317 1316 -1.26659870148e-7 +1318 1316 -1403261.78363 +1319 1316 -3050569.09485 +1320 1316 6.33299350738e-8 +1363 1316 -710040.746465 +1364 1316 -1936474.76309 +1365 1316 -1321205.99576 +1366 1316 33038.3525625 +1367 1316 89431.3340666 +1368 1316 17103.8438088 +1369 1316 -824536.156663 +1370 1316 -1792469.90579 +1371 1316 1278446.38624 +1317 1317 19697338.3132 +1318 1317 2.70083546639e-8 +1319 1317 5.77419996262e-8 +1320 1317 1699467.28443 +1363 1317 -484442.198447 +1364 1317 -1321205.99576 +1365 1317 -2315242.24089 +1366 1317 41457.2556898 +1367 1317 -17103.8438089 +1368 1317 -5695176.55864 +1369 1317 588085.337671 +1370 1317 1278446.38624 +1371 1317 -2352102.70813 +1318 1318 3805513.9161 +1319 1318 6969546.89216 +1320 1318 -4.842877388e-8 +1321 1318 -900913.14915 +1322 1318 -1453085.72444 +1323 1318 2.04890966415e-8 +1366 1318 -379286.632065 +1367 1318 -824536.156663 +1368 1318 -588085.337671 +1369 1318 -52167.0393844 +1370 1318 -83826.403156 +1371 1318 -61373.985991 +1372 1318 -590912.90871 +1373 1318 -953085.336629 +1374 1318 741520.302649 +1319 1319 13049076.8844 +1320 1319 -1.04308128357e-7 +1321 1319 -1453085.72444 +1322 1319 -2343686.65232 +1323 1319 3.53902578354e-8 +1366 1319 -824536.156663 +1367 1319 -1792469.90579 +1368 1319 -1278446.38624 +1369 1319 -83826.403156 +1370 1319 -134521.304432 +1371 1319 32978.3592395 +1372 1319 -953085.336629 +1373 1319 -1537234.41392 +1374 1319 1196000.48814 +1320 1320 20269767.9526 +1321 1320 1.67638063431e-8 +1322 1320 2.60770320892e-8 +1323 1320 2107431.22341 +1366 1320 -588085.337671 +1367 1320 -1278446.38624 +1368 1320 -2352102.70813 +1369 1320 61373.985991 +1370 1320 -32978.3592395 +1371 1320 -6109264.54972 +1372 1320 741520.302649 +1373 1320 1196000.48814 +1374 1320 -2434642.38116 +1321 1321 6311393.43185 +1322 1321 8279110.50413 +1323 1321 -1.49011611938e-8 +1324 1321 -2301041.15788 +1325 1321 -2614819.49759 +1326 1321 1.49011611938e-8 +1369 1321 -590912.90871 +1370 1321 -953085.336629 +1371 1321 -741520.302649 +1372 1321 222027.67781 +1373 1321 227177.812693 +1374 1321 -75251.5247453 +1375 1321 -1038306.19785 +1376 1321 -1179893.40665 +1377 1321 929649.114512 +1322 1322 11193680.9464 +1323 1322 -1.49011611938e-8 +1324 1322 -2614819.49759 +1325 1322 -2971385.79271 +1326 1322 1.67638063431e-8 +1369 1322 -953085.336629 +1370 1322 -1537234.41392 +1371 1322 -1196000.48814 +1372 1322 227177.812693 +1373 1322 217630.207531 +1374 1322 55832.4159336 +1375 1322 -1179893.40665 +1376 1322 -1340787.9621 +1377 1322 1056419.44831 +1323 1323 19292741.7183 +1324 1323 1.49011611938e-8 +1325 1323 1.67638063431e-8 +1326 1323 427942.721781 +1369 1323 -741520.302649 +1370 1323 -1196000.48814 +1371 1323 -2434642.38116 +1372 1323 75251.5247453 +1373 1323 -55832.4159336 +1374 1323 -5287984.36193 +1375 1323 929649.114512 +1376 1323 1056419.44831 +1377 1323 -2155790.5645 +1324 1324 11173430.2083 +1325 1324 7495059.08719 +1326 1324 -2.23517417908e-8 +1327 1324 -678836.974673 +1328 1324 -279932.773061 +1329 1324 3.72529029846e-9 +1372 1324 -1038306.19785 +1373 1324 -1179893.40665 +1374 1324 -929649.114512 +1375 1324 -908773.635179 +1376 1324 -175831.894858 +1377 1324 -148519.569575 +1378 1324 -1760951.03338 +1379 1324 -726165.37459 +1380 1324 1300948.03845 +1325 1325 6371949.42588 +1326 1325 -2.23517417908e-8 +1327 1325 -279932.773061 +1328 1325 -115436.195077 +1329 1325 9.31322574615e-10 +1372 1325 -1179893.40665 +1373 1325 -1340787.9621 +1374 1325 -1056419.44831 +1375 1325 -175831.894858 +1376 1325 153537.59263 +1377 1325 207978.412157 +1378 1325 -726165.37459 +1379 1325 -299449.639006 +1380 1325 536473.417918 +1326 1326 22658086.7563 +1327 1326 -3.72529029846e-9 +1328 1326 -1.86264514923e-9 +1329 1326 4790763.62708 +1372 1326 -929649.114512 +1373 1326 -1056419.44831 +1374 1326 -2155790.5645 +1375 1326 148519.569575 +1376 1326 -207978.412157 +1377 1326 -7295491.64648 +1378 1326 1300948.03845 +1379 1326 536473.417918 +1380 1326 -3164492.7709 +1327 1327 14370371.7322 +1328 1327 1.49011611938e-8 +1330 1327 -678836.974673 +1331 1327 279932.773061 +1332 1327 3.72529029846e-9 +1375 1327 -1760951.03338 +1376 1327 -726165.37459 +1377 1327 -1300948.03845 +1378 1327 -2367056.05396 +1379 1327 -9.31322574615e-9 +1380 1327 -5.58793544769e-9 +1381 1327 -1760951.03338 +1382 1327 726165.37459 +1383 1327 1300948.03845 +1328 1328 2443681.02578 +1329 1328 7.45058059692e-9 +1330 1328 279932.773061 +1331 1328 -115436.195077 +1332 1328 -9.31322574615e-10 +1375 1328 -726165.37459 +1376 1328 -299449.639006 +1377 1328 -536473.417918 +1378 1328 -7.45058059692e-9 +1379 1328 -402517.768767 +1380 1328 429178.734334 +1381 1328 726165.37459 +1382 1328 -299449.639006 +1383 1328 -536473.417918 +1329 1329 27394702.6193 +1330 1329 -3.72529029846e-9 +1331 1329 1.86264514923e-9 +1332 1329 4790763.62708 +1375 1329 -1300948.03845 +1376 1329 -536473.417918 +1377 1329 -3164492.7709 +1378 1329 3.72529029846e-9 +1379 1329 -429178.734334 +1380 1329 -10404782.7221 +1381 1329 1300948.03845 +1382 1329 -536473.417918 +1383 1329 -3164492.7709 +1330 1330 11173430.2083 +1331 1330 -7495059.08719 +1332 1330 2.98023223877e-8 +1333 1330 -2301041.15788 +1334 1330 2614819.49759 +1335 1330 -1.11758708954e-8 +1378 1330 -1760951.03338 +1379 1330 726165.37459 +1380 1330 -1300948.03845 +1381 1330 -908773.635179 +1382 1330 175831.894858 +1383 1330 148519.569575 +1384 1330 -1038306.19785 +1385 1330 1179893.40665 +1386 1330 929649.114512 +1331 1331 6371949.42588 +1332 1331 -2.23517417908e-8 +1333 1331 2614819.49759 +1334 1331 -2971385.79271 +1335 1331 1.11758708954e-8 +1378 1331 726165.37459 +1379 1331 -299449.639006 +1380 1331 536473.417918 +1381 1331 175831.894858 +1382 1331 153537.59263 +1383 1331 207978.412157 +1384 1331 1179893.40665 +1385 1331 -1340787.9621 +1386 1331 -1056419.44831 +1332 1332 22658086.7563 +1333 1332 -7.45058059692e-9 +1334 1332 9.31322574615e-9 +1335 1332 427942.721781 +1378 1332 -1300948.03845 +1379 1332 536473.417918 +1380 1332 -3164492.7709 +1381 1332 -148519.569575 +1382 1332 -207978.412157 +1383 1332 -7295491.64648 +1384 1332 929649.114512 +1385 1332 -1056419.44831 +1386 1332 -2155790.5645 +1333 1333 6311393.43185 +1334 1333 -8279110.50413 +1335 1333 7.45058059692e-9 +1336 1333 -900913.14915 +1337 1333 1453085.72444 +1338 1333 -1.67638063431e-8 +1381 1333 -1038306.19785 +1382 1333 1179893.40665 +1383 1333 -929649.114512 +1384 1333 222027.67781 +1385 1333 -227177.812693 +1386 1333 75251.5247453 +1387 1333 -590912.90871 +1388 1333 953085.336629 +1389 1333 741520.302649 +1334 1334 11193680.9464 +1335 1334 -7.45058059692e-9 +1336 1334 1453085.72444 +1337 1334 -2343686.65232 +1338 1334 2.421438694e-8 +1381 1334 1179893.40665 +1382 1334 -1340787.9621 +1383 1334 1056419.44831 +1384 1334 -227177.812693 +1385 1334 217630.207531 +1386 1334 55832.4159336 +1387 1334 953085.336629 +1388 1334 -1537234.41392 +1389 1334 -1196000.48814 +1335 1335 19292741.7183 +1336 1335 -1.86264514923e-8 +1337 1335 3.35276126862e-8 +1338 1335 2107431.22341 +1381 1335 -929649.114512 +1382 1335 1056419.44831 +1383 1335 -2155790.5645 +1384 1335 -75251.5247453 +1385 1335 -55832.4159336 +1386 1335 -5287984.36193 +1387 1335 741520.302649 +1388 1335 -1196000.48814 +1389 1335 -2434642.38116 +1336 1336 3805513.9161 +1337 1336 -6969546.89216 +1338 1336 3.35276126862e-8 +1339 1336 -645500.420471 +1340 1336 1403261.78363 +1341 1336 -2.04890966415e-8 +1384 1336 -590912.90871 +1385 1336 953085.336629 +1386 1336 -741520.302649 +1387 1336 -52167.0393844 +1388 1336 83826.403156 +1389 1336 61373.985991 +1390 1336 -379286.632065 +1391 1336 824536.156663 +1392 1336 588085.337671 +1337 1337 13049076.8844 +1338 1337 -6.70552253723e-8 +1339 1337 1403261.78363 +1340 1337 -3050569.09485 +1341 1337 4.47034835815e-8 +1384 1337 953085.336629 +1385 1337 -1537234.41392 +1386 1337 1196000.48814 +1387 1337 83826.403156 +1388 1337 -134521.304432 +1389 1337 32978.3592395 +1390 1337 824536.156663 +1391 1337 -1792469.90579 +1392 1337 -1278446.38624 +1338 1338 20269767.9526 +1339 1338 -2.60770320892e-8 +1340 1338 5.77419996262e-8 +1341 1338 1699467.28443 +1384 1338 -741520.302649 +1385 1338 1196000.48814 +1386 1338 -2434642.38116 +1387 1338 -61373.985991 +1388 1338 -32978.3592395 +1389 1338 -6109264.54972 +1390 1338 588085.337671 +1391 1338 -1278446.38624 +1392 1338 -2352102.70813 +1339 1339 2497002.01247 +1340 1339 -5989724.5569 +1341 1339 4.65661287308e-8 +1342 1339 -465133.879389 +1343 1339 1268546.94379 +1344 1339 -2.51457095146e-8 +1387 1339 -379286.632065 +1388 1339 824536.156663 +1389 1339 -588085.337671 +1390 1339 12227.6197724 +1391 1339 -33038.3525625 +1392 1339 41457.2556898 +1393 1339 -260348.273704 +1394 1339 710040.746465 +1395 1339 484442.198447 +1340 1340 14552393.0113 +1341 1340 -1.26659870148e-7 +1342 1340 1268546.94379 +1343 1340 -3459673.48306 +1344 1340 7.07805156708e-8 +1387 1340 824536.156663 +1388 1340 -1792469.90579 +1389 1340 1278446.38624 +1390 1340 -33038.3525625 +1391 1340 89431.3340666 +1392 1340 17103.8438088 +1393 1340 710040.746465 +1394 1340 -1936474.76309 +1395 1340 -1321205.99576 +1341 1341 19697338.3132 +1342 1341 -2.79396772385e-8 +1343 1341 7.82310962677e-8 +1344 1341 1501024.18544 +1387 1341 -588085.337671 +1388 1341 1278446.38624 +1389 1341 -2352102.70813 +1390 1341 -41457.2556898 +1391 1341 -17103.8438089 +1392 1341 -5695176.55864 +1393 1341 484442.198447 +1394 1341 -1321205.99576 +1395 1341 -2315242.24089 +1342 1342 1768961.88645 +1343 1342 -5200497.8108 +1344 1342 3.53902578354e-8 +1345 1342 -354732.167298 +1346 1342 1144297.31386 +1347 1342 -1.210719347e-8 +1390 1342 -260348.273704 +1391 1342 710040.746465 +1392 1342 -484442.198447 +1393 1342 24667.0078013 +1394 1342 -73754.1287476 +1395 1342 27106.375452 +1396 1342 -193836.559394 +1397 1342 625279.223852 +1398 1342 416676.259817 +1343 1343 15396260.6937 +1344 1343 -1.04308128357e-7 +1345 1343 1144297.31386 +1346 1343 -3691281.65763 +1347 1343 4.09781932831e-8 +1390 1343 710040.746465 +1391 1343 -1936474.76309 +1392 1343 1321205.99576 +1393 1343 -73754.1287476 +1394 1343 222052.369366 +1395 1343 9164.38855486 +1396 1343 625279.223852 +1397 1343 -2017029.75436 +1398 1343 -1344116.96715 +1344 1344 19430253.1852 +1345 1344 -1.95577740669e-8 +1346 1344 6.70552253723e-8 +1347 1344 1397983.15475 +1390 1344 -484442.198447 +1391 1344 1321205.99576 +1392 1344 -2315242.24089 +1393 1344 -27106.375452 +1394 1344 -9164.38855491 +1395 1344 -5492991.14412 +1396 1344 416676.259817 +1397 1344 -1344116.96715 +1398 1344 -2296979.40748 +1345 1345 2528206.97157 +1346 1345 -4472509.50178 +1347 1345 3.72529029846e-8 +1348 1345 -161793.620953 +1349 1345 -1643724.74446 +1351 1345 -268071.177508 +1352 1345 1017991.81332 +1353 1345 -1.02445483208e-8 +1393 1345 -193836.559394 +1394 1345 625279.223852 +1395 1345 -416676.259817 +1396 1345 -474421.1715 +1397 1345 -131724.429407 +1398 1345 23329.9193232 +1399 1345 -800250.281991 +1400 1345 8988445.57462 +1402 1345 -144050.092155 +1403 1345 547025.666412 +1404 1345 358351.461509 +1346 1346 16817023.1576 +1347 1346 -1.19209289551e-7 +1348 1346 2239700.15498 +1349 1346 161793.620953 +1351 1346 1017991.81332 +1352 1346 -3865791.69616 +1353 1346 3.35276126862e-8 +1393 1346 625279.223852 +1394 1346 -2017029.75436 +1395 1346 1344116.96715 +1396 1346 -131724.429407 +1397 1346 -28174.4048381 +1398 1346 6684.54707694 +1399 1346 -6040681.08601 +1400 1346 800250.281991 +1402 1346 547025.666412 +1403 1346 -2077312.65726 +1404 1346 -1360828.33484 +1347 1347 39001825.1235 +1351 1347 -1.30385160446e-8 +1352 1347 4.47034835815e-8 +1353 1347 1324183.34296 +1393 1347 -416676.259817 +1394 1347 1344116.96715 +1395 1347 -2296979.40748 +1396 1347 -23329.9193232 +1397 1347 -6684.54707698 +1398 1347 -14301137.6219 +1402 1347 358351.461509 +1403 1347 -1360828.33484 +1404 1347 -2284273.33935 +1348 1348 329626980.943 +1349 1348 -65098171.9808 +1396 1348 800250.281991 +1397 1348 6040681.08601 +1399 1348 64525513.4986 +1400 1348 4237777.92342 +1349 1349 569419560.785 +1396 1349 -8988445.57462 +1397 1349 -800250.281991 +1399 1349 4237777.92342 +1400 1349 48915433.0595 +1350 1350 790573.476703 +1401 1350 -357777.777778 +1351 1351 983097.51761 +1352 1351 -3993191.05725 +1353 1351 7.07805156708e-8 +1396 1351 -144050.092155 +1397 1351 547025.666412 +1398 1351 -358351.461509 +1402 1351 22134.9913963 +1403 1351 -90320.994053 +1404 1351 18909.0650259 +1352 1352 16310675.5472 +1353 1352 -2.98023223877e-7 +1396 1352 547025.666412 +1397 1352 -2077312.65726 +1398 1352 1360828.33484 +1402 1352 -90320.994053 +1403 1352 370626.696215 +1404 1352 4631.25243446 +1353 1353 19175351.0344 +1396 1353 -358351.461509 +1397 1353 1360828.33484 +1398 1353 -2284273.33935 +1402 1353 -18909.0650259 +1403 1353 -4631.25243458 +1404 1353 -5293544.01962 +1354 1354 1011851.60912 +1355 1354 4110093.43272 +1356 1354 -4.842877388e-8 +1357 1354 -311975.890718 +1358 1354 -1184718.57235 +1359 1354 1.02445483208e-8 +1355 1355 16788621.6609 +1356 1355 -2.08616256714e-7 +1357 1355 -1184718.57235 +1358 1355 -4498931.28739 +1359 1355 3.35276126862e-8 +1356 1356 18240145.4814 +1357 1356 1.11758708954e-8 +1358 1356 3.72529029846e-8 +1359 1356 758227.34113 +1357 1357 2350262.38096 +1358 1357 4600302.12491 +1359 1357 -2.60770320892e-8 +1360 1357 2.98023223877e-8 +1361 1357 -1.19209289551e-7 +1363 1357 -413671.243673 +1364 1357 -1334423.36669 +1365 1357 1.58324837685e-8 +1358 1358 17048556.8253 +1359 1358 -8.94069671631e-8 +1360 1358 5.96046447754e-8 +1363 1358 -1334423.36669 +1364 1358 -4304591.50545 +1365 1358 4.842877388e-8 +1359 1359 36176122.0894 +1363 1359 1.39698386192e-8 +1364 1359 4.09781932831e-8 +1365 1359 830431.306934 +1360 1360 305878308.174 +1361 1360 66094123.115 +1361 1361 549336918.275 +1362 1362 715555.555556 +1363 1363 1816739.3315 +1364 1363 5341279.87864 +1365 1363 -3.53902578354e-8 +1366 1363 -544032.43723 +1367 1363 -1483724.82881 +1368 1363 2.04890966415e-8 +1364 1364 15814018.062 +1365 1364 -1.11758708954e-7 +1366 1364 -1483724.82881 +1367 1364 -4046522.26039 +1368 1364 5.58793544769e-8 +1365 1365 18448886.5844 +1366 1365 2.04890966415e-8 +1367 1365 5.58793544769e-8 +1368 1365 931107.693646 +1366 1366 2558539.62181 +1367 1366 6138307.60949 +1368 1366 -5.21540641785e-8 +1369 1366 -759692.612592 +1370 1366 -1651505.67955 +1371 1366 2.98023223877e-8 +1367 1367 14915778.6682 +1368 1367 -1.34110450745e-7 +1369 1367 -1651505.67955 +1370 1367 -3590229.73815 +1371 1367 6.33299350738e-8 +1368 1368 18669379.7869 +1369 1368 2.88709998131e-8 +1370 1368 6.14672899246e-8 +1371 1368 1124555.53479 +1369 1369 3880798.16119 +1370 1369 7110485.96967 +1371 1369 -4.842877388e-8 +1372 1369 -1076372.38828 +1373 1369 -1736084.49722 +1374 1369 2.421438694e-8 +1370 1370 13318817.2723 +1371 1370 -9.685754776e-8 +1372 1370 -1736084.49722 +1373 1370 -2800136.28584 +1374 1370 3.91155481339e-8 +1371 1371 19146980.3477 +1372 1371 2.23517417908e-8 +1373 1371 3.53902578354e-8 +1374 1371 1520483.39551 +1372 1372 6516876.42304 +1373 1372 8531914.96891 +1374 1372 -2.23517417908e-8 +1375 1372 -2626121.17726 +1376 1372 -2984228.61052 +1377 1372 1.30385160446e-8 +1373 1373 11512089.4984 +1374 1373 -2.98023223877e-8 +1375 1373 -2984228.61052 +1376 1373 -3391168.87559 +1377 1373 1.30385160446e-8 +1374 1374 18361731.7736 +1375 1374 3.72529029846e-9 +1376 1374 5.58793544769e-9 +1377 1374 -125380.553907 +1375 1375 11197028.9194 +1376 1375 7624235.1212 +1377 1375 1.49011611938e-8 +1378 1375 -1154846.00933 +1379 1375 -476225.158488 +1376 1376 6560950.4012 +1377 1376 3.72529029846e-9 +1378 1376 -476225.158488 +1379 1376 -196381.508655 +1377 1377 21281133.3311 +1380 1377 4075797.18655 +1378 1378 14087608.2601 +1379 1378 -7.45058059692e-9 +1380 1378 -7.45058059692e-9 +1381 1378 -1154846.00933 +1382 1378 476225.158488 +1379 1379 2395597.11087 +1380 1379 -1.86264514923e-8 +1381 1379 476225.158488 +1382 1379 -196381.508655 +1380 1380 25315942.1547 +1383 1380 4075797.18655 +1381 1381 11197028.9194 +1382 1381 -7624235.1212 +1383 1381 -1.49011611938e-8 +1384 1381 -2626121.17726 +1385 1381 2984228.61052 +1386 1381 -5.58793544769e-9 +1382 1382 6560950.4012 +1383 1382 -7.45058059692e-9 +1384 1382 2984228.61052 +1385 1382 -3391168.87559 +1386 1382 5.58793544769e-9 +1383 1383 21281133.3311 +1384 1383 -1.11758708954e-8 +1385 1383 1.11758708954e-8 +1386 1383 -125380.553907 +1384 1384 6516876.42304 +1385 1384 -8531914.96891 +1386 1384 2.98023223877e-8 +1387 1384 -1076372.38828 +1388 1384 1736084.49722 +1389 1384 -1.67638063431e-8 +1385 1385 11512089.4984 +1386 1385 -3.72529029846e-8 +1387 1385 1736084.49722 +1388 1385 -2800136.28584 +1389 1385 2.60770320892e-8 +1386 1386 18361731.7736 +1387 1386 -2.04890966415e-8 +1388 1386 3.35276126862e-8 +1389 1386 1520483.39551 +1387 1387 3880798.16119 +1388 1387 -7110485.96967 +1389 1387 3.72529029846e-8 +1390 1387 -759692.612592 +1391 1387 1651505.67955 +1392 1387 -1.86264514923e-8 +1388 1388 13318817.2723 +1389 1388 -6.70552253723e-8 +1390 1388 1651505.67955 +1391 1388 -3590229.73815 +1392 1388 4.09781932831e-8 +1389 1389 19146980.3477 +1390 1389 -2.51457095146e-8 +1391 1389 5.40167093277e-8 +1392 1389 1124555.53479 +1390 1390 2558539.62181 +1391 1390 -6138307.60949 +1392 1390 4.09781932831e-8 +1393 1390 -544032.43723 +1394 1390 1483724.82881 +1395 1390 -2.51457095146e-8 +1391 1391 14915778.6682 +1392 1391 -1.19209289551e-7 +1393 1391 1483724.82881 +1394 1391 -4046522.26039 +1395 1391 7.07805156708e-8 +1392 1392 18669379.7869 +1393 1392 -2.79396772385e-8 +1394 1392 7.82310962677e-8 +1395 1392 931107.693646 +1393 1393 1816739.3315 +1394 1393 -5341279.87864 +1395 1393 3.72529029846e-8 +1396 1393 -413671.243673 +1397 1393 1334423.36669 +1398 1393 -8.38190317154e-9 +1394 1394 15814018.062 +1395 1394 -1.11758708954e-7 +1396 1394 1334423.36669 +1397 1394 -4304591.50545 +1398 1394 2.98023223877e-8 +1395 1395 18448886.5844 +1396 1395 -1.11758708954e-8 +1397 1395 3.72529029846e-8 +1398 1395 830431.306934 +1396 1396 2350262.78049 +1397 1396 -4600302.86075 +1398 1396 2.04890966415e-8 +1399 1396 -7.45058059692e-9 +1402 1396 -311975.890718 +1403 1396 1184718.57235 +1404 1396 -1.02445483208e-8 +1397 1397 17048556.4258 +1398 1397 -7.45058059692e-8 +1399 1397 5.96046447754e-8 +1400 1397 7.45058059692e-9 +1402 1397 1184718.57235 +1403 1397 -4498931.28739 +1404 1397 3.35276126862e-8 +1398 1398 36176122.0894 +1402 1398 -1.30385160446e-8 +1403 1398 4.47034835815e-8 +1404 1398 758227.341129 +1399 1399 305878011.196 +1400 1399 -66093576.1502 +1400 1400 549337215.254 +1401 1401 715555.555556 +1402 1402 1011851.60912 +1403 1402 -4110093.43272 +1404 1402 6.14672899246e-8 +1403 1403 16788621.6609 +1404 1403 -2.53319740295e-7 +1404 1404 18240145.4814 +1405 1405 3880798.16119 +1406 1405 -7110485.96967 +1407 1405 3.72529029846e-8 +1408 1405 -759692.612592 +1409 1405 1651505.67955 +1410 1405 -1.86264514923e-8 +1453 1405 -590912.90871 +1454 1405 953085.336629 +1455 1405 -741520.302649 +1456 1405 -52167.0393845 +1457 1405 83826.4031561 +1458 1405 61373.985991 +1459 1405 -379286.632065 +1460 1405 824536.156663 +1461 1405 588085.337671 +1406 1406 13318817.2723 +1407 1406 -6.70552253723e-8 +1408 1406 1651505.67955 +1409 1406 -3590229.73815 +1410 1406 4.09781932831e-8 +1453 1406 953085.336629 +1454 1406 -1537234.41392 +1455 1406 1196000.48814 +1456 1406 83826.4031561 +1457 1406 -134521.304432 +1458 1406 32978.3592395 +1459 1406 824536.156663 +1460 1406 -1792469.90579 +1461 1406 -1278446.38624 +1407 1407 19146980.3477 +1408 1407 -2.51457095146e-8 +1409 1407 5.40167093277e-8 +1410 1407 1124555.53479 +1453 1407 -741520.302649 +1454 1407 1196000.48814 +1455 1407 -2434642.38116 +1456 1407 -61373.985991 +1457 1407 -32978.3592395 +1458 1407 -6109264.54972 +1459 1407 588085.337671 +1460 1407 -1278446.38624 +1461 1407 -2352102.70813 +1408 1408 2558539.62181 +1409 1408 -6138307.60949 +1410 1408 4.09781932831e-8 +1411 1408 -544032.43723 +1412 1408 1483724.82881 +1413 1408 -2.51457095146e-8 +1456 1408 -379286.632065 +1457 1408 824536.156663 +1458 1408 -588085.337671 +1459 1408 12227.6197724 +1460 1408 -33038.3525624 +1461 1408 41457.2556898 +1462 1408 -260348.273704 +1463 1408 710040.746465 +1464 1408 484442.198447 +1409 1409 14915778.6682 +1410 1409 -1.11758708954e-7 +1411 1409 1483724.82881 +1412 1409 -4046522.26039 +1413 1409 7.07805156708e-8 +1456 1409 824536.156663 +1457 1409 -1792469.90579 +1458 1409 1278446.38624 +1459 1409 -33038.3525624 +1460 1409 89431.3340665 +1461 1409 17103.8438088 +1462 1409 710040.746465 +1463 1409 -1936474.76309 +1464 1409 -1321205.99576 +1410 1410 18669379.7869 +1411 1410 -2.88709998131e-8 +1412 1410 8.19563865662e-8 +1413 1410 931107.693646 +1456 1410 -588085.337671 +1457 1410 1278446.38624 +1458 1410 -2352102.70813 +1459 1410 -41457.2556898 +1460 1410 -17103.8438089 +1461 1410 -5695176.55864 +1462 1410 484442.198447 +1463 1410 -1321205.99576 +1464 1410 -2315242.24089 +1411 1411 1816739.3315 +1412 1411 -5341279.87864 +1413 1411 4.09781932831e-8 +1414 1411 -413671.243673 +1415 1411 1334423.36669 +1416 1411 -8.38190317154e-9 +1459 1411 -260348.273704 +1460 1411 710040.746465 +1461 1411 -484442.198447 +1462 1411 24667.0078013 +1463 1411 -73754.1287476 +1464 1411 27106.375452 +1465 1411 -193836.559394 +1466 1411 625279.223852 +1467 1411 416676.259817 +1412 1412 15814018.062 +1413 1412 -1.26659870148e-7 +1414 1412 1334423.36669 +1415 1412 -4304591.50545 +1416 1412 2.98023223877e-8 +1459 1412 710040.746465 +1460 1412 -1936474.76309 +1461 1412 1321205.99576 +1462 1412 -73754.1287476 +1463 1412 222052.369366 +1464 1412 9164.38855485 +1465 1412 625279.223852 +1466 1412 -2017029.75436 +1467 1412 -1344116.96715 +1413 1413 18448886.5844 +1414 1413 -1.02445483208e-8 +1415 1413 3.35276126862e-8 +1416 1413 830431.306934 +1459 1413 -484442.198447 +1460 1413 1321205.99576 +1461 1413 -2315242.24089 +1462 1413 -27106.375452 +1463 1413 -9164.3885549 +1464 1413 -5492991.14412 +1465 1413 416676.259817 +1466 1413 -1344116.96715 +1467 1413 -2296979.40748 +1414 1414 2350262.78049 +1415 1414 -4600302.86075 +1416 1414 2.04890966415e-8 +1417 1414 -7.45058059692e-9 +1420 1414 -311975.890718 +1421 1414 1184718.57235 +1422 1414 -1.02445483208e-8 +1462 1414 -193836.559394 +1463 1414 625279.223852 +1464 1414 -416676.259817 +1465 1414 -474421.1715 +1466 1414 -131724.429408 +1467 1414 23329.9193232 +1468 1414 -800250.281991 +1469 1414 8988445.57462 +1471 1414 -144050.092155 +1472 1414 547025.666412 +1473 1414 358351.461509 +1415 1415 17048556.4258 +1416 1415 -7.45058059692e-8 +1417 1415 5.96046447754e-8 +1418 1415 7.45058059692e-9 +1420 1415 1184718.57235 +1421 1415 -4498931.28739 +1422 1415 3.35276126862e-8 +1462 1415 625279.223852 +1463 1415 -2017029.75436 +1464 1415 1344116.96715 +1465 1415 -131724.429408 +1466 1415 -28174.4048381 +1467 1415 6684.54707695 +1468 1415 -6040681.08601 +1469 1415 800250.281991 +1471 1415 547025.666412 +1472 1415 -2077312.65726 +1473 1415 -1360828.33484 +1416 1416 36176122.0894 +1420 1416 -1.30385160446e-8 +1421 1416 4.47034835815e-8 +1422 1416 758227.341129 +1462 1416 -416676.259817 +1463 1416 1344116.96715 +1464 1416 -2296979.40748 +1465 1416 -23329.9193232 +1466 1416 -6684.54707698 +1467 1416 -14301137.6219 +1471 1416 358351.461509 +1472 1416 -1360828.33484 +1473 1416 -2284273.33935 +1417 1417 305878011.196 +1418 1417 -66093576.1502 +1465 1417 800250.281991 +1466 1417 6040681.08601 +1468 1417 64525513.4986 +1469 1417 4237777.92342 +1418 1418 549337215.254 +1465 1418 -8988445.57462 +1466 1418 -800250.281991 +1468 1418 4237777.92342 +1469 1418 48915433.0595 +1419 1419 715555.555556 +1470 1419 -357777.777778 +1420 1420 1011851.60912 +1421 1420 -4110093.43272 +1422 1420 6.33299350738e-8 +1465 1420 -144050.092155 +1466 1420 547025.666412 +1467 1420 -358351.461509 +1471 1420 22134.9913963 +1472 1420 -90320.9940531 +1473 1420 18909.0650259 +1421 1421 16788621.6609 +1422 1421 -2.60770320892e-7 +1465 1421 547025.666412 +1466 1421 -2077312.65726 +1467 1421 1360828.33484 +1471 1421 -90320.9940531 +1472 1421 370626.696215 +1473 1421 4631.25243447 +1422 1422 18240145.4814 +1465 1422 -358351.461509 +1466 1422 1360828.33484 +1467 1422 -2284273.33935 +1471 1422 -18909.0650259 +1472 1422 -4631.25243459 +1473 1422 -5293544.01962 +1423 1423 1011851.60912 +1424 1423 4110093.43272 +1425 1423 -4.65661287308e-8 +1426 1423 -311975.890718 +1427 1423 -1184718.57235 +1428 1423 1.30385160446e-8 +1424 1424 16788621.6609 +1425 1424 -2.01165676117e-7 +1426 1424 -1184718.57235 +1427 1424 -4498931.28739 +1428 1424 4.47034835815e-8 +1425 1425 18240145.4814 +1426 1425 1.210719347e-8 +1427 1425 4.09781932831e-8 +1428 1425 758227.34113 +1426 1426 2350262.38096 +1427 1426 4600302.12491 +1428 1426 -2.421438694e-8 +1429 1426 2.98023223877e-8 +1430 1426 -1.19209289551e-7 +1432 1426 -413671.243673 +1433 1426 -1334423.36669 +1434 1426 1.49011611938e-8 +1427 1427 17048556.8253 +1428 1427 -8.19563865662e-8 +1429 1427 5.96046447754e-8 +1432 1427 -1334423.36669 +1433 1427 -4304591.50545 +1434 1427 4.47034835815e-8 +1428 1428 36176122.0894 +1432 1428 1.49011611938e-8 +1433 1428 4.47034835815e-8 +1434 1428 830431.306934 +1429 1429 305878308.174 +1430 1429 66094123.115 +1430 1430 549336918.275 +1431 1431 715555.555556 +1432 1432 1816739.3315 +1433 1432 5341279.87864 +1434 1432 -3.53902578354e-8 +1435 1432 -544032.43723 +1436 1432 -1483724.82881 +1437 1432 2.04890966415e-8 +1433 1433 15814018.062 +1434 1433 -1.11758708954e-7 +1435 1433 -1483724.82881 +1436 1433 -4046522.26039 +1437 1433 5.58793544769e-8 +1434 1434 18448886.5844 +1435 1434 2.23517417908e-8 +1436 1434 5.96046447754e-8 +1437 1434 931107.693647 +1435 1435 2558539.62181 +1436 1435 6138307.60949 +1437 1435 -5.58793544769e-8 +1438 1435 -759692.612592 +1439 1435 -1651505.67955 +1440 1435 2.98023223877e-8 +1436 1436 14915778.6682 +1437 1436 -1.34110450745e-7 +1438 1436 -1651505.67955 +1439 1436 -3590229.73815 +1440 1436 6.33299350738e-8 +1437 1437 18669379.7869 +1438 1437 2.70083546639e-8 +1439 1437 5.77419996262e-8 +1440 1437 1124555.53479 +1438 1438 3880798.16119 +1439 1438 7110485.96967 +1440 1438 -4.47034835815e-8 +1441 1438 -1076372.38828 +1442 1438 -1736084.49722 +1443 1438 2.79396772385e-8 +1439 1439 13318817.2723 +1440 1439 -8.94069671631e-8 +1441 1439 -1736084.49722 +1442 1439 -2800136.28584 +1443 1439 4.47034835815e-8 +1440 1440 19146980.3477 +1441 1440 2.23517417908e-8 +1442 1440 3.53902578354e-8 +1443 1440 1520483.39551 +1441 1441 6516876.42304 +1442 1441 8531914.96891 +1443 1441 -2.23517417908e-8 +1444 1441 -2626121.17726 +1445 1441 -2984228.61052 +1446 1441 9.31322574615e-9 +1442 1442 11512089.4984 +1443 1442 -3.72529029846e-8 +1444 1442 -2984228.61052 +1445 1442 -3391168.87559 +1446 1442 1.11758708954e-8 +1443 1443 18361731.7736 +1444 1443 3.72529029846e-9 +1445 1443 5.58793544769e-9 +1446 1443 -125380.553907 +1444 1444 11197028.9194 +1445 1444 7624235.1212 +1446 1444 -1.49011611938e-8 +1447 1444 -1154846.00933 +1448 1444 -476225.158488 +1449 1444 1.86264514923e-9 +1445 1445 6560950.4012 +1446 1445 -7.45058059692e-9 +1447 1445 -476225.158488 +1448 1445 -196381.508655 +1449 1445 9.31322574615e-10 +1446 1446 21281133.3311 +1449 1446 4075797.18655 +1447 1447 14087608.2601 +1448 1447 -7.45058059692e-9 +1449 1447 -7.45058059692e-9 +1450 1447 -1154846.00933 +1451 1447 476225.158488 +1452 1447 1.86264514923e-9 +1448 1448 2395597.11087 +1449 1448 7.45058059692e-9 +1450 1448 476225.158488 +1451 1448 -196381.508655 +1452 1448 -9.31322574615e-10 +1449 1449 25315942.1547 +1452 1449 4075797.18655 +1450 1450 11197028.9194 +1451 1450 -7624235.1212 +1452 1450 1.11758708954e-8 +1453 1450 -2626121.17726 +1454 1450 2984228.61052 +1455 1450 -7.45058059692e-9 +1451 1451 6560950.4012 +1452 1451 -7.45058059692e-9 +1453 1451 2984228.61052 +1454 1451 -3391168.87559 +1455 1451 7.45058059692e-9 +1452 1452 21281133.3311 +1453 1452 -2.04890966415e-8 +1454 1452 2.23517417908e-8 +1455 1452 -125380.553907 +1453 1453 6516876.42304 +1454 1453 -8531914.96891 +1455 1453 3.72529029846e-8 +1456 1453 -1076372.38828 +1457 1453 1736084.49722 +1458 1453 -1.30385160446e-8 +1454 1454 11512089.4984 +1455 1454 -4.47034835815e-8 +1456 1454 1736084.49722 +1457 1454 -2800136.28584 +1458 1454 2.04890966415e-8 +1455 1455 18361731.7736 +1456 1455 -2.04890966415e-8 +1457 1455 3.35276126862e-8 +1458 1455 1520483.39551 +1456 1456 3880798.16119 +1457 1456 -7110485.96967 +1458 1456 4.09781932831e-8 +1459 1456 -759692.612592 +1460 1456 1651505.67955 +1461 1456 -1.86264514923e-8 +1457 1457 13318817.2723 +1458 1457 -7.45058059692e-8 +1459 1457 1651505.67955 +1460 1457 -3590229.73815 +1461 1457 4.09781932831e-8 +1458 1458 19146980.3477 +1459 1458 -2.51457095146e-8 +1460 1458 5.40167093277e-8 +1461 1458 1124555.53479 +1459 1459 2558539.62181 +1460 1459 -6138307.60949 +1461 1459 4.28408384323e-8 +1462 1459 -544032.43723 +1463 1459 1483724.82881 +1464 1459 -2.79396772385e-8 +1460 1460 14915778.6682 +1461 1460 -1.19209289551e-7 +1462 1460 1483724.82881 +1463 1460 -4046522.26039 +1464 1460 7.82310962677e-8 +1461 1461 18669379.7869 +1462 1461 -2.79396772385e-8 +1463 1461 8.19563865662e-8 +1464 1461 931107.693647 +1462 1462 1816739.3315 +1463 1462 -5341279.87864 +1464 1462 3.72529029846e-8 +1465 1462 -413671.243673 +1466 1462 1334423.36669 +1467 1462 -7.45058059692e-9 +1463 1463 15814018.062 +1464 1463 -1.11758708954e-7 +1465 1463 1334423.36669 +1466 1463 -4304591.50545 +1467 1463 2.60770320892e-8 +1464 1464 18448886.5844 +1465 1464 -1.02445483208e-8 +1466 1464 3.35276126862e-8 +1467 1464 830431.306934 +1465 1465 2350262.78049 +1466 1465 -4600302.86075 +1467 1465 1.86264514923e-8 +1468 1465 -7.45058059692e-9 +1471 1465 -311975.890718 +1472 1465 1184718.57235 +1473 1465 -1.02445483208e-8 +1466 1466 17048556.4258 +1467 1466 -6.70552253723e-8 +1468 1466 5.96046447754e-8 +1469 1466 7.45058059692e-9 +1471 1466 1184718.57235 +1472 1466 -4498931.28739 +1473 1466 3.35276126862e-8 +1467 1467 36176122.0894 +1471 1467 -1.39698386192e-8 +1472 1467 4.842877388e-8 +1473 1467 758227.34113 +1468 1468 305878011.196 +1469 1468 -66093576.1502 +1469 1469 549337215.254 +1470 1470 715555.555556 +1471 1471 1011851.60912 +1472 1471 -4110093.43272 +1473 1471 6.33299350738e-8 +1472 1472 16788621.6609 +1473 1472 -2.60770320892e-7 +1473 1473 18240145.4814 diff --git a/packages/belos/kokkos/src/BelosKokkosAdapter.hpp b/packages/belos/kokkos/src/BelosKokkosAdapter.hpp new file mode 100644 index 000000000000..745258f9f433 --- /dev/null +++ b/packages/belos/kokkos/src/BelosKokkosAdapter.hpp @@ -0,0 +1,686 @@ +//@HEADER +// ************************************************************************ +// +// Belos: Block Linear Solvers Package +// Copyright 2004 Sandia Corporation +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Jennifer A. Loe (jloe@sandia.gov) +// +// ************************************************************************ +//@HEADER + +/*! \file BelosKokkosAdapter.hpp + \brief Implementation of the interface between Belos virtual classes and Kokkos concrete classes. + Allows the user to use a Kokkos::view as a Belos::MultiVector in the Belos solvers. + \warning Note: This class does not currently allow the user to run Belos solvers which + require accessing non-contiguous columns of data in memory. +*/ + +#include +#include +#include +#include + +#include +#include +#include + +#include + + +#ifndef BELOS_KOKKOS_ADAPTER_HPP +#define BELOS_KOKKOS_ADAPTER_HPP +namespace Belos { + +//Forward class declaration of KokkosCrsOperator: +template +class KokkosCrsOperator; + +/// \class KokkosMultiVec +/// \brief Implementation of Belos::MultiVec using Kokkos::View. +/// \author Jennifer Loe +/// +/// \tparam ScalarType The type of entries of the multivector. +/// \tparam Device The Kokkos::ExecutionSpace where the multivector +/// should reside. +/// +/// Belos::MultiVec offers a simple abstract interface for +/// multivector operations in Belos solver algorithms. This class +/// implements Belos::MultiVec using Kokkos::View. +template +class KokkosMultiVec : public MultiVec { + +public: + + using ViewVectorType = Kokkos::View; + using ConstViewVectorType = Kokkos::View; + using ViewMatrixType = Kokkos::View; + using ConstViewMatrixType = Kokkos::View; + + //Unmanaged view types: + using UMHostViewVectorType = + Kokkos::View>; + using UMHostConstViewVectorType = + Kokkos::View>; + using UMHostViewMatrixType = + Kokkos::View>; + using UMHostConstViewMatrixType = + Kokkos::View>; + +protected: + ViewMatrixType myView; + +public: + //! @name Constructors/Destructor + //@{ + + /// \brief Returns a multivector with `numrows` rows and `numvecs` columns. + /// + /// The `label` string indicates the label for the internal `Kokkos::view`. + /// If `zeroOut` is set to `true`, the multivector will be initialized to zeros. + KokkosMultiVec (const std::string label, const int numrows, const int numvecs, const bool zeroOut = true) : + myView (Kokkos::view_alloc(Kokkos::WithoutInitializing,label),numrows,numvecs) + { if (zeroOut) { Kokkos::deep_copy(myView,0); } } + + /// \brief Returns a multivector with `numrows` rows and `numvecs` columns. + /// + /// If `zeroOut` is set to `true`, the multivector will be initialized to zeros. + KokkosMultiVec (const int numrows, const int numvecs, const bool zeroOut = true) : + myView (Kokkos::view_alloc(Kokkos::WithoutInitializing,"MV"),numrows,numvecs) + { if (zeroOut) { Kokkos::deep_copy(myView,0); } } + + /// \brief Returns a single column multivector with `numrows` rows. + /// + /// If `zeroOut` is set to `true`, the multivector will be initialized to zeros. + KokkosMultiVec (const int numrows, const bool zeroOut = true) : + myView(Kokkos::view_alloc(Kokkos::WithoutInitializing,"MV"),numrows,1) + { if (zeroOut) { Kokkos::deep_copy(myView,0); } } + + //! Copy constructor (performs deep copy). + + /// This copy constructor returns a new KokksMultiVec containing a + /// deep copy of the multivector given by the user. + KokkosMultiVec (const KokkosMultiVec &sourceVec) : + myView(Kokkos::view_alloc(Kokkos::WithoutInitializing,"MV"),(int)sourceVec.GetGlobalLength(),sourceVec.GetNumberVecs()) + { Kokkos::deep_copy(myView,sourceVec.GetInternalViewConst()); } + + //! Copy constructor for type conversion. (Performs deep copy.) + + /// This copy constructor returns a new KokksMultiVec containing a + /// deep copy of the multivector given by the user. + /// The internal data of the multivector is converted from + /// `ScalarType` to `ScalarType2`. + template < class ScalarType2 > + KokkosMultiVec (const KokkosMultiVec &sourceVec) : + myView(Kokkos::view_alloc(Kokkos::WithoutInitializing, "MV"),(int)sourceVec.GetGlobalLength(),sourceVec.GetNumberVecs()) + { Kokkos::deep_copy(myView,sourceVec.GetInternalViewConst()); } + + //! Assignment operator (performs deep copy). + + /// This `=` operator performs a deep copy of + /// the right-hand side KokkosMultiVec to the + /// left-hand side KokkosMultiVec. The left-hand + /// side MultiVec will be resized if necessary. + KokkosMultiVec & operator=(const KokkosMultiVec & sourceVec) { + int len = sourceVec.GetGlobalLength(); + int cols = sourceVec.GetNumberVecs(); + if( len != (int)myView.extent(0) || cols != (int)myView.extent(1) ){ + Kokkos::resize(myView, len, cols); + } + Kokkos::deep_copy(myView,sourceVec.GetInternalViewConst()); + return *this; + } + + //! Assignment operator for type conversion. (Performs deep copy.) + + /// This `=` operator performs a deep copy of + /// the right-hand side KokkosMultiVec to the + /// left-hand side KokkosMultiVec. The left-hand + /// side MultiVec will be resized if necessary. + /// The internal data of the right-hand side multivec + /// is converted from `ScalarType` to `ScalarType2`. + template < class ScalarType2 > + KokkosMultiVec & operator=(const KokkosMultiVec & sourceVec) { + int len = sourceVec.GetGlobalLength(); + int cols = sourceVec.GetNumberVecs(); + if( len != (int)myView.extent(0) || cols != (int)myView.extent(1) ){ + Kokkos::resize(myView, len, cols); + } + Kokkos::deep_copy(myView,sourceVec.GetInternalViewConst()); + return *this; + } + + //! Create a KokkosMultiVec from a given `Kokkos::view`. + + /// Returns a KokkosMultiVec that internally stores the + /// data given in `sourceView`. If `makeCopy` has value + /// `true`, then this function makes a deep copy of the + /// `Kokkos::view`. If `false`, then the KokkosMultiVec stores a + /// shallow copy of the given `Kokkos::view`. (This option assumes that + /// the user will make no changes to that view outside of + /// the KokkosMultiVec interface.) + KokkosMultiVec (const ViewMatrixType & sourceView, bool makeCopy = true) { + if( makeCopy ){ + if( sourceView.extent(0) != myView.extent(0) || sourceView.extent(1) != myView.extent(1) ){ + Kokkos::resize(myView, sourceView.extent(0), sourceView.extent(1)); + } + Kokkos::deep_copy(myView, sourceView); + } + else{ + myView = sourceView; + } + } + + //! Create a KokkosMultiVec with ScalarType2 from a given `Kokkos::view` with ScalarType1. + + /// Returns a KokkosMultiVec that internally stores the + /// data given in `sourceView`. This function always makes + /// a deep copy of the sourceView in order to change scalar + /// types. + template < class ScalarType2 > //TODO: Fix this so that passing in a view without device specified actually compiles... + KokkosMultiVec (const Kokkos::View & sourceView) : + myView(Kokkos::view_alloc(Kokkos::WithoutInitializing, "MV"),sourceView.extent(0),sourceView.extent(1)) + { Kokkos::deep_copy(myView,sourceView); } + + //! Destructor (default) + ~KokkosMultiVec(){} + + //@} + + //! @name Data Access Methods + //@{ + + //! Reader (const) method for internal view. + + /// Returns the (const) view that stores internal data + /// for the KokkosMultiVec. + ConstViewMatrixType GetInternalViewConst() const { + return myView; + } + + //! Reader/Writer method for internal view. + + /// Returns the view that stores internal data + /// for the KokkosMultiVec. + /// \warning ** Be careful!! ** The KokkosMultiVec does + /// NOT expect users to use this function to + /// make changes to its internal data. Most + /// necessary changes can be made via other functions. + /// Make sure you know what you are doing! + ViewMatrixType GetInternalViewNonConst(){ + return myView; + } + //@} + + //! @name Copy/View functions inherited from Belos::MultiVec + //@{ + + //! Creates a MultiVec with same data distribution as `this`. + + /// A virtual "copy constructor" that returns a pointer to a new + /// Belos::MultiVec. (Underneath, the vector is a KokkosMultiVec, + /// and its properties can be accessed via `dynamic_cast`.) + /// This vector's entries are + /// not copied; instead, a new MultiVec is created with the same + /// data distribution, but with numvecs columns (numvecs > 0). + /// Multivector entries are not initialized. + /// + /// \param numvecs [in] The number of columns in the output + /// multivector. Must be positive. + MultiVec * Clone ( const int numvecs ) const{ + KokkosMultiVec * ptr = new KokkosMultiVec(myView.extent(0),numvecs, false); + return ptr; + } + + //! \brief Creates a MultiVec which is a (deep) copy of `this`. + /// + /// A virtual "copy constructor" returning a pointer to a new + /// object of Belos::MultiVec. (KokkosMultiVec underneath.) + /// All of this vector's entries are + /// copied and a new stand-alone multivector is created. (deep + /// copy). + MultiVec * CloneCopy () const{ + KokkosMultiVec * ptr = new KokkosMultiVec(myView.extent(0),myView.extent(1), false); + Kokkos::deep_copy(ptr->GetInternalViewNonConst(),myView); + return ptr; + } + + //! \brief Creates a MultiVec which is a (deep) copy of selected columns of `this`. + /// + /// A virtual "copy constructor" returning a pointer to a new Belos::MultiVec. + /// This vector's entries are copied and a new + /// stand-alone MultiVector is created where only selected columns + /// are chosen. (deep copy). + /// Indexing is zero-based, so an `std::vector index` with values 0, 3, 5 would + /// indicate copying the first, 4th, and 6th columns of the original multivector. + /// Indices need not be contiguous or ordered. + /// Result is `output[:,i] = (*this)[:,index[i]]`. + MultiVec * CloneCopy ( const std::vector& index ) const{ + // JAL- If debug options needed, could add validity checks of index. + // See debug code in belos/src/tpetra/BelosMultiVecTraits_Tpetra.hpp. + int numvecs = index.size(); + KokkosMultiVec * B = new KokkosMultiVec("B",myView.extent(0),numvecs, false); + bool isContigAscending = true; + + //Check whether the given indices are contiguous and ascending. + for(unsigned int i=0; i< (index.size()-1); i++){ + if( index[i+1] != index[i]+1 ){ + isContigAscending = false; + } + } + + //Copy the vectors: (Case depends on indices.) + if(isContigAscending && index.size()==(unsigned)this->GetNumberVecs()){ //Copy entire multivec. + Kokkos::deep_copy(B->GetInternalViewNonConst(),myView); + } + else if (isContigAscending){ //Copy contiguous subset + ViewMatrixType ThisSub = Kokkos::subview(myView, Kokkos::ALL, std::make_pair(index.front(), index.back()+1)); + Kokkos::deep_copy(B->GetInternalViewNonConst(),ThisSub); + } + else{ //Copy columns one by one + for(unsigned int i=0; iGetInternalViewNonConst(), Kokkos::ALL, i); + auto ThisSub = Kokkos::subview(myView, Kokkos::ALL, index[i]); + Kokkos::deep_copy(Bsub, ThisSub); + } + } + return B; + } + + //! \brief Creates a (read-only) MultiVec which is a shallow copy of selected columns of `this`. + /// + /// A virtual view constructor returning a pointer to a new Belos::MultiVec with + /// selected columns. (Column indexing is zero-based.) The view is read-only. + /// This vector's entries are shared and hence no + /// memory is allocated for the columns. (Internally, we create + /// a `Kokkos::subview`.) + /// \warning At this time, the Kokkos-Belos adapter only supports + /// viewing column indices that form a contiguous subset in memory. + /// Thus, the values in `index` must be contiguous and ascending (e.g. 0,1,2,3). + const MultiVec * CloneView ( const std::vector& index ) const { //TODO This isn't const!! + bool isContigAscending = true; + //Check whether the given indices are contiguous and ascending. + for(unsigned int i=0; i< (index.size()-1); i++){ + if( index[i+1] != index[i]+1 ){ + isContigAscending = false; + } + } + if(isContigAscending ){ + const KokkosMultiVec * B = + new KokkosMultiVec(Kokkos::subview(myView, Kokkos::ALL, std::make_pair(index.front(), index.back()+1)),false); + return B; + } + else{ + throw std::runtime_error("CloneView asked for non-contiguous subset. \n This feature is not yet supported in Belos for Kokkos."); + } + } + + + //! \brief Creates a nonconst MultiVec which is a shallow copy of selected columns of `this`. + /// + /// A virtual view constructor returning a pointer to a new Belos::MultiVec with + /// selected columns. (Column indexing is zero-based.) + /// This vector's entries are shared and hence no + /// memory is allocated for the columns. (Internally, we create + /// a `Kokkos::subview`.) Writing to this view will change the + /// entries of the original multivector. + /// \warning At this time, the Kokkos-Belos adapter only supports + /// viewing column indices that form a contiguous subset in memory. + /// Thus, the values in `index` must be contiguous and ascending (e.g. 0,1,2,3). + MultiVec * CloneViewNonConst ( const std::vector& index ){ + bool isContigAscending = true; + //Check whether the given indices are contiguous and ascending. + for(unsigned int i=0; i< (index.size()-1); i++){ + if( index[i+1] != index[i]+1 ){ + isContigAscending = false; + } + } + if(isContigAscending ){ + KokkosMultiVec * B = + new KokkosMultiVec(Kokkos::subview(myView, Kokkos::ALL, std::make_pair(index.front(), index.back()+1)),false); + return B; + } + else{ + throw std::runtime_error("CloneViewNonConst asked for non-contiguous subset. \n This feature is not yet supported in Belos for Kokkos."); + } + } + + //! Copy the vectors in A to the vectors in (*this) specified by index. (Deep copy.) + + /// Copies vectors of A to a sub-block of vectors of this multivector. (Deep copy.) + /// The sub-block to be overwritten is given by the indices and need not be contiguous. + /// Result is (*this)[:,index[i]] = A[:,i]. + /// Column indexing is zero-based. + /// + void SetBlock ( const MultiVec& A, const std::vector& index ){ + KokkosMultiVec *A_vec = dynamic_cast(&const_cast &>(A)); + + if( index.size() > myView.extent(1) ){ + throw std::runtime_error("Error in KokkosMultiVec::SetBlock. A cannot have more vectors than (*this)."); + } + bool isContigAscending = true; + //Check whether the given indices are contiguous and ascending. + for(unsigned int i=0; i< (index.size()-1); i++){ + if( index[i+1] != index[i]+1 ){ + isContigAscending = false; + } + } + + //Perform deep copy of sub block: + if(isContigAscending && index.size()==(unsigned)this->GetNumberVecs()){ //Copy entire multivec. + Kokkos::deep_copy(myView,A_vec->GetInternalViewConst()); + } + else if (isContigAscending){ //Copy contiguous subset + ConstViewMatrixType Asub = Kokkos::subview(A_vec->GetInternalViewConst(), Kokkos::ALL, std::make_pair(0,(int)index.size())); + ViewMatrixType ThisSub = Kokkos::subview(myView, Kokkos::ALL, std::make_pair(index.front(), index.back()+1)); + Kokkos::deep_copy(ThisSub, Asub); + } + else{ //Copy columns one by one + for(unsigned int i=0; iGetInternalViewConst(), Kokkos::ALL, i); + ViewVectorType ThisSub = Kokkos::subview(myView, Kokkos::ALL, index[i]); + Kokkos::deep_copy(ThisSub, Asub); + } + } + } + //@} + + //! @name Attribue functions inherited from Belos::MultiVec + //@{ + //! Returns the number of rows in the multivector. + ptrdiff_t GetGlobalLength () const { + return static_cast(myView.extent(0)); + } + + //! Returns the number of columns in the multivector. + int GetNumberVecs () const { return myView.extent(1); } + //@} + + //! @name Mathematical functions inherited from Belos::MultiVec + //@{ + //! \brief `*this <- alpha * A * B + beta * (*this)` + /// + /// where alpha and beta are scalars and the dimensions of `A*B` match + /// the dimensions of `(*this)`. + void MvTimesMatAddMv ( const ScalarType alpha, const MultiVec& A, + const Teuchos::SerialDenseMatrix& B, const ScalarType beta ){ + KokkosMultiVec *A_vec = dynamic_cast(&const_cast &>(A)); + if( myView.extent(1) == 1 && A_vec->GetInternalViewConst().extent(1) == 1){ //B is a scalar. + ScalarType scal1 = alpha*B(0,0); + ViewVectorType mysub = Kokkos::subview(myView, Kokkos::ALL, 0); + ConstViewVectorType Asub = Kokkos::subview(A_vec->GetInternalViewConst(), Kokkos::ALL, 0); + KokkosBlas::axpby(scal1, Asub, beta, mysub); + } + else{ + UMHostConstViewMatrixType mat_h(B.values(), A_vec->GetInternalViewConst().extent(1), myView.extent(1)); + ViewMatrixType mat_d(Kokkos::view_alloc(Kokkos::WithoutInitializing,"mat"), A_vec->GetInternalViewConst().extent(1), myView.extent(1)); + Kokkos::deep_copy(mat_d, mat_h); + if( myView.extent(1) == 1 ){ // B has only 1 col + ConstViewVectorType Bsub = Kokkos::subview(mat_d, Kokkos::ALL, 0); + ViewVectorType mysub = Kokkos::subview(myView, Kokkos::ALL, 0); + KokkosBlas::gemv("N", alpha, A_vec->GetInternalViewConst(), Bsub, beta, mysub); + } + else{ + KokkosBlas::gemm("N", "N", alpha, A_vec->GetInternalViewConst(), mat_d, beta, myView); + } + } + } + + //! `*this <- alpha * A + beta * B` + /// + /// Scale and add two vectors. Store the result in `*this`. + void MvAddMv ( const ScalarType alpha, const MultiVec& A, const ScalarType beta, + const MultiVec& B){ + KokkosMultiVec *A_vec = dynamic_cast(&const_cast &>(A)); + KokkosMultiVec *B_vec = dynamic_cast(&const_cast &>(B)); + + KokkosBlas::update(alpha, A_vec->GetInternalViewConst(), beta, B_vec->GetInternalViewConst(), (ScalarType) 0.0, myView); + } + + /// `*this <- alpha * this` + /// + //! Scale (multiply) each element of the vectors in \c *this with \c alpha. + void MvScale ( const ScalarType alpha ) { + KokkosBlas::scal(myView, alpha, myView); + } + + /// `*this[:,i] <- alpha[i] * this[:,i]` + /// + //! Scale (multiply) each element of the \c i-th vector in \c *this with \c alpha[i]. + void MvScale ( const std::vector& alpha ){ + + //Move scalar values to a Kokkos View: + UMHostConstViewVectorType scalars_h(alpha.data(), alpha.size()); + ViewVectorType scalars_d(Kokkos::view_alloc(Kokkos::WithoutInitializing,"scalars_d"), alpha.size()); + Kokkos::deep_copy(scalars_d, scalars_h); + + KokkosBlas::scal(myView, scalars_d, myView); + } + + //! B <- alpha * A^T * (*this) + /// + /// Computes matrix product with transpose. Result is a dense matrix. + /// Conjugate transpose is used as appropriate. + void MvTransMv ( const ScalarType alpha, const MultiVec& A, Teuchos::SerialDenseMatrix& B ) const{ + KokkosMultiVec *A_vec = dynamic_cast(&const_cast &>(A)); + if(A_vec->myView.extent(1) == 1 && myView.extent(1) == 1){ + ConstViewVectorType Asub = Kokkos::subview(A_vec->GetInternalViewConst(), Kokkos::ALL, 0); + ViewVectorType mysub = Kokkos::subview(myView, Kokkos::ALL, 0); + ScalarType soln = KokkosBlas::dot(Asub, mysub); + soln = alpha*soln; + B(0,0) = soln; + } + // *** + // For MvTransMv, this option runs slower than GEMM on NVIDIA V100. + // Do not enable for now. + // **** + // else if( myView.extent(1) == 1 ){ // Only 1 col in soln vec + // ViewVectorType soln(Kokkos::view_alloc(Kokkos::WithoutInitializing,"soln"), A_vec->GetInternalViewConst().extent(1)); + // ViewVectorType mysub = Kokkos::subview(myView, Kokkos::ALL, 0); + // KokkosBlas::gemv("C", alpha, A_vec->GetInternalViewConst(), mysub, ScalarType(0.0), soln); + // for( unsigned int i = 0; i < soln.extent(0); i++){ + // B(i,0) = soln(i); + // } + // } + else{ + UMHostViewMatrixType soln_h(B.values(), A_vec->GetInternalViewConst().extent(1), myView.extent(1)); + ViewMatrixType soln_d(Kokkos::view_alloc(Kokkos::WithoutInitializing,"mat"), A_vec->GetInternalViewConst().extent(1), myView.extent(1)); + KokkosBlas::gemm("C", "N", alpha, A_vec->GetInternalViewConst(), myView, ScalarType(0.0), soln_d); + Kokkos::deep_copy(soln_h, soln_d); + } + } + + + //! b[i] = A[i]^T * this[i] + + /// Performs a dot product between A and (*this). + /// Uses conjugate transpose when appropriate. + /// Output is a vector. + void MvDot ( const MultiVec& A, std::vector& b ) const{ + //Put output vector in unmanaged Kokkos view: + UMHostViewVectorType dotView_h(b.data(),myView.extent(1)); + ViewVectorType dotView_d(Kokkos::view_alloc(Kokkos::WithoutInitializing,"Dot"),myView.extent(1)); + + KokkosMultiVec *A_vec = dynamic_cast(&const_cast &>(A)); + + KokkosBlas::dot(dotView_d, A_vec->GetInternalViewConst(), myView); + Kokkos::deep_copy(dotView_h, dotView_d); + } + + //! alpha[i] = norm of i-th column of (*this) + + /// Valid norm types are Belos::TwoNorm, Belos::OneNorm, + /// and Belos::InfNorm. + void MvNorm ( std::vector& normvec, NormType norm_type = TwoNorm ) const{ + + //Put output vector in unmanaged Kokkos view: + UMHostViewVectorType normView_h(normvec.data(),myView.extent(1)); + ViewVectorType normView_d(Kokkos::view_alloc(Kokkos::WithoutInitializing,"Norm"),myView.extent(1)); + + switch( norm_type ) { + case ( OneNorm ) : + KokkosBlas::nrm1(normView_d, myView); + break; + case ( TwoNorm ) : + KokkosBlas::nrm2(normView_d, myView); + break; + case ( InfNorm ) : + KokkosBlas::nrminf(normView_d, myView); + break; + default: + TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, + "Belos::KokkosMultiVec::MvNorm: Invalid norm_type " + << norm_type << ". The current list of valid norm " + "types is {OneNorm, TwoNorm, InfNorm}."); + } + Kokkos::deep_copy(normView_h, normView_d); + } + //@} + + //! @name Initialization functions inherited from Belos::MultiVec + //@{ + //! Fill all columns of *this with random values. + void MvRandom() { + int rand_seed = std::rand(); + Kokkos::Random_XorShift64_Pool<> pool(rand_seed); + Kokkos::fill_random(myView, pool, -1,1); + } + + //! Initialize each element of (*this) to the scalar value alpha. + void MvInit ( const ScalarType alpha ) { + Kokkos::deep_copy(myView,alpha); + } + //@} + + //! @name Print function inherited from Belos::MultiVec + //@{ + //! Print (*this) to the given output stream. + + /// (This function will first copy the multivector to host space + /// if needed.) + void MvPrint( std::ostream& os ) const { + typename ViewMatrixType::HostMirror hostView("myViewMirror", myView.extent(0), myView.extent(1)); + Kokkos::deep_copy(hostView, myView); + for(unsigned int i = 0; i < (hostView.extent(0)); i++){ + for (unsigned int j = 0; j < (hostView.extent(1)); j++){ + os << hostView(i , j) << " "; + } + os << std::endl; + } + os << std::endl; + } + //@} + +}; + +/// \class KokkosCrsOperator +/// \brief Implementation of Belos::Operator using KokkosSparse::CrsMatrix. +template +class KokkosCrsOperator : public Operator { + +private: + // Shallow copy of the CrsMatrix used for SpMV. + KokkosSparse::CrsMatrix myMatrix; + +public: + //! @name Constructor/Destructor + //@{ + //! Constructor obtains a shallow copy of the given CrsMatrix. + KokkosCrsOperator (const KokkosSparse::CrsMatrix mat) + : myMatrix(mat) {} + + //! Destructor. + ~KokkosCrsOperator(){} + //@} + + //! @name Methods relating to applying the operator + //@{ + + /// \brief Apply the operator to x, putting the result in y. + /// + /// Take the KokkosMultiVec \c x and apply the operator (or its + /// transpose or Hermitian transpose) to it, writing the result + /// into the KokkosMultiVec \c y. (This performs a sparse matrix- + /// vector product.) + /// + /// \param x [in] The input multivector. + /// + /// \param y [out] The output multivector. x and y may not alias + /// (i.e., be views of) one another. + /// + /// \param trans [in] Whether to apply the operator (Belos::NOTRANS), its + /// transpose (Belos::TRANS), or its Hermitian transpose (Belos::CONJTRANS). + /// The default is Belos::NOTRANS. (Defined in BelosTypes.hpp.) + /// + void Apply (const MultiVec& x, MultiVec& y, ETrans trans=NOTRANS) const{ + + // Determine transpose mode: + char mode[] = "X"; + switch(trans){ + case NOTRANS: + mode[0]='N'; + break; + case TRANS: + mode[0]='T'; + break; + case CONJTRANS: + mode[0]='C'; + break; + default: + TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, + "Belos::KokkosCrsOperator::Apply: Invalid ETrans type "); + } + + //Use dynamic_cast to tell the compiler these are Kokkos Multivecs. + KokkosMultiVec *x_vec = + dynamic_cast *>(&const_cast &>(x)); + KokkosMultiVec *y_vec = dynamic_cast *>(&y); + + //KokkosSparse::spmv computes y = beta*y + alpha*Op(A)*x + ScalarType alpha = 1.0; + ScalarType beta = 0; + KokkosSparse::spmv(mode, alpha, myMatrix, x_vec->GetInternalViewConst(), beta, y_vec->GetInternalViewNonConst()); + } + + /// \brief Whether this operator implements applying the transpose. + /// + /// This function returns true since we can always apply the transpose + /// of a Kokkos::CrsMatrix. + /// + bool HasApplyTranspose () const { + return true; + } + //@} +}; +}// end namespace Belos +#endif diff --git a/packages/belos/kokkos/src/CMakeLists.txt b/packages/belos/kokkos/src/CMakeLists.txt new file mode 100644 index 000000000000..4d8c6851c1f1 --- /dev/null +++ b/packages/belos/kokkos/src/CMakeLists.txt @@ -0,0 +1,39 @@ + + +# +# A) Package-specific configuration options +# + +# +# B) Define the header and source files (and directories) +# + +# +# src +# + +SET(HEADERS "") +SET(SOURCES "") + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + +# +# Core Files +# + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +APPEND_SET(HEADERS + BelosKokkosAdapter.hpp + ) + +# +# C) Define the targets for package's library(s) +# +#TRIBITS_ADD_LIBRARY( +# beloskokkos +# HEADERS ${HEADERS} +# SOURCES ${SOURCES} +# DEPLIBS belos +# ) + diff --git a/packages/belos/kokkos/test/BelosKokkosMVOPTester.hpp b/packages/belos/kokkos/test/BelosKokkosMVOPTester.hpp new file mode 100644 index 000000000000..51a4be03b13e --- /dev/null +++ b/packages/belos/kokkos/test/BelosKokkosMVOPTester.hpp @@ -0,0 +1,1610 @@ +//@HEADER +// ************************************************************************ +// +// Belos: Block Linear Solvers Package +// Copyright 2004 Sandia Corporation +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Michael A. Heroux (maherou@sandia.gov) +// +// ************************************************************************ +//@HEADER +// +#ifndef BELOS_KOKKOS_MVOPTESTER_HPP +#define BELOS_KOKKOS_MVOPTESTER_HPP + +// Assumptions that I have made: +// * I assume/verify that a multivector must have at least one std::vector. This seems +// to be consistent with Epetra_MultiVec. +// * I do not assume that an operator is deterministic; I do assume that the +// operator, applied to 0, will return 0. + +/*! \file BelosKokkosMVOPTester.hpp + \brief Test routines for Kokkos MultiVecTraits and OperatorTraits conformity. + Note: This tester file is a direct copy of the BelosMVOPTester.hpp, + except that the tests for accessing non-contiguous columns of data have + been removed for the testing of the Kokkos adapter. +*/ + +#include "BelosConfigDefs.hpp" +#include "BelosTypes.hpp" + +#include "BelosMultiVecTraits.hpp" +#include "BelosOperatorTraits.hpp" +#include "BelosOutputManager.hpp" + +#include "Teuchos_RCP.hpp" +#include "Teuchos_SetScientific.hpp" +#include "Teuchos_SerialDenseHelpers.hpp" + +namespace Belos { + + /// \brief Test correctness of a MultiVecTraits specialization and + /// multivector implementation. + /// + /// \tparam ScalarType The type of the entries in the multivectors; + /// the first template parameter of MultiVecTraits. + /// \tparam MV The multivector type; the second template parameter + /// of MultiVecTraits. + /// + /// \param om [in/out] A valid OutputManager, for displaying test results. + /// + /// \param A [in] An initial multivector, to use for making new + /// multivectors. (Belos doesn't currently have a "vector space" + /// abstraction; making a new multivector requires a valid input + /// multivector to clone.) + /// + /// \return Test result: true if all tests passed, else false. + template< class ScalarType, class MV > + bool + TestKokkosMultiVecTraits (const Teuchos::RCP > &om, + const Teuchos::RCP &A) + { + using Teuchos::SetScientific; + using std::endl; + typedef MultiVecTraits MVT; + typedef Teuchos::ScalarTraits STS; + typedef typename STS::magnitudeType MagType; + + // Make sure that all floating-point numbers are printed with the + // right precision. + SetScientific sci (om->stream (Warnings)); + + // FIXME (mfh 09 Jan 2013) Added an arbitrary tolerance in case + // norms are not computed deterministically (which is possible + // even with MPI only, and more likely with threads). + const MagType tol = Teuchos::as (100) * STS::eps (); + + /* MVT Contract: + + Clone(MV,int) + CloneCopy(MV) + CloneCopy(MV,vector) + USER: will request positive number of vectors + MV: will return a multivector with exactly the number of + requested vectors. + vectors are the same dimension as the cloned MV + + + CloneView(MV,vector) [const and non-const] + USER: There is no assumed communication between creation and + destruction of a view. I.e., after a view is created, changes to the + source multivector are not reflected in the view. Likewise, until + destruction of the view, changes in the view are not reflected in the + source multivector. + + GetGlobalLength + MV: will always be positive (MV cannot have zero vectors) + + GetNumberVecs + MV: will always be positive (MV cannot have zero vectors) + + MvAddMv + USER: multivecs will be of the same dimension and same number of vecs + MV: input vectors will not be modified + performing C=0*A+1*B will assign B to C exactly + + MvTimesMatAddMv + USER: multivecs and serialdensematrix will be of the proper shape + MV: input arguments will not be modified + following arithmetic relations hold exactly: + A*I = A + 0*B = B + 1*B = B + + MvTransMv + USER: SerialDenseMatrix will be large enough to hold results. + MV: SerialDenseMatrix will not be resized. + Inner products will satisfy |a'*b| <= |a|*|b| + alpha == 0 => SerialDenseMatrix == 0 + + MvDot + USER: Results vector will be large enough for results. + Both multivectors will have the same number of vectors. + (Epetra crashes, otherwise.) + MV: Inner products will satisfy |a'*b| <= |a|*|b| + Results vector will not be resized. + + MvNorm + MV: vector norm is always non-negative, and zero + only for zero vectors. + results vector should not be resized + + SetBlock + USER: indices will be distinct + MV: assigns copies of the vectors to the specified + locations, leaving the other vectors untouched. + + MvRandom + MV: Generate zero vector with "zero" probability + Don't gen the same vectors twice. + + MvInit + MV: Init(alpha) sets all elements to alpha + + MvScale (two versions) + MV: scales multivector values + + MvPrint + MV: routine does not modify vectors (not tested here) + *********************************************************************/ + + const ScalarType one = STS::one(); + const ScalarType zero = STS::zero(); + const MagType zero_mag = Teuchos::ScalarTraits::zero(); + + // Don't change these two without checking the initialization of ind below + const int numvecs = 10; + const int numvecs_2 = 5; + + std::vector ind(numvecs_2); + std::vector indContig(numvecs_2); + + /* Initialize indices for selected copies/views + The MVT specialization should not assume that + these are ordered or even distinct. + Also retrieve the edges. + + However, to spice things up, grab the first std::vector, + last std::vector, and choose the others randomly. + */ + TEUCHOS_TEST_FOR_EXCEPT(numvecs_2 != 5); + ind[0] = 0; + ind[1] = 5; + ind[2] = 2; + ind[3] = 2; + ind[4] = 9; + + indContig[0] = 2; + indContig[1] = 3; + indContig[2] = 4; + indContig[3] = 5; + indContig[4] = 6; + + /*********** GetNumberVecs() ***************************************** + Verify: + 1) This number should be strictly positive + *********************************************************************/ + if ( MVT::GetNumberVecs(*A) <= 0 ) { + om->stream(Warnings) + << "*** ERROR *** MultiVectorTraits::GetNumberVecs()." << endl + << "Returned <= 0." << endl; + return false; + } + + + /*********** GetGlobalLength() *************************************** + Verify: + 1) This number should be strictly positive + *********************************************************************/ + if ( MVT::GetGlobalLength(*A) <= 0 ) { + om->stream(Warnings) + << "*** ERROR *** MultiVectorTraitsExt::GetGlobalLength()" << endl + << "Returned <= 0." << endl; + return false; + } + + + /*********** Clone() and MvNorm() ************************************ + Verify: + 1) Clone() allows us to specify the number of vectors + 2) Clone() returns a multivector of the same dimension + 3) Vector norms shouldn't be negative + 4) MvNorm result std::vector should not be resized + *********************************************************************/ + { + Teuchos::RCP B = MVT::Clone(*A,numvecs); + std::vector norms(2*numvecs); + bool ResizeWarning = false; + if ( MVT::GetNumberVecs(*B) != numvecs ) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::Clone()." << endl + << "Did not allocate requested number of vectors." << endl; + return false; + } + if ( MVT::GetGlobalLength(*B) != MVT::GetGlobalLength(*A) ) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::Clone()." << endl + << "Did not allocate requested number of vectors." << endl; + return false; + } + MVT::MvNorm(*B, norms); + if ( norms.size() != 2*numvecs && ResizeWarning==false ) { + om->stream(Warnings) + << "*** WARNING *** MultiVecTraits::MvNorm()." << endl + << "Method resized the output vector." << endl; + ResizeWarning = true; + } + for (int i=0; istream(Warnings) + << "*** ERROR *** MultiVecTraits::Clone()." << endl + << "Vector had negative norm." << endl; + return false; + } + } + } + + + /*********** MvRandom() and MvNorm() and MvInit() ******************** + Verify: + 1) Set vectors to zero + 2) Check that norm is zero + 3) Perform MvRandom. + 4) Verify that vectors aren't zero anymore + 5) Perform MvRandom again. + 6) Verify that std::vector norms are different than before + + Without knowing something about the random distribution, + this is about the best that we can do, to make sure that MvRandom + did at least *something*. + + Also, make sure std::vector norms aren't negative. + *********************************************************************/ + { + Teuchos::RCP B = MVT::Clone(*A,numvecs); + std::vector norms(numvecs), norms2(numvecs); + + MVT::MvInit(*B); + MVT::MvNorm(*B, norms); + for (int i=0; istream(Warnings) + << "*** ERROR *** MultiVecTraits::MvInit() " + << "and MultiVecTraits::MvNorm()" << endl + << "Supposedly zero vector has non-zero norm." << endl; + return false; + } + } + MVT::MvRandom(*B); + MVT::MvNorm(*B, norms); + MVT::MvRandom(*B); + MVT::MvNorm(*B, norms2); + for (int i=0; istream(Warnings) + << "*** ERROR *** MultiVecTraits::MvRandom()." << endl + << "Random vector was empty (very unlikely)." << endl; + return false; + } + else if ( norms[i] < zero_mag || norms2[i] < zero_mag ) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvRandom()." << endl + << "Vector had negative norm." << endl; + return false; + } + else if ( norms[i] == norms2[i] ) { + om->stream(Warnings) + << "*** ERROR *** MutliVecTraits::MvRandom()." << endl + << "Vectors not random enough." << endl; + return false; + } + } + } + + + /*********** MvRandom() and MvNorm() and MvScale() ******************* + Verify: + 1) Perform MvRandom. + 2) Verify that vectors aren't zero + 3) Set vectors to zero via MvScale + 4) Check that norm is zero + *********************************************************************/ + { + Teuchos::RCP B = MVT::Clone(*A,numvecs); + std::vector norms(numvecs); + + MVT::MvRandom(*B); + MVT::MvScale(*B,STS::zero()); + MVT::MvNorm(*B, norms); + for (int i=0; istream(Warnings) + << "*** ERROR *** MultiVecTraits::MvScale(alpha) " + << "Supposedly zero vector has non-zero norm." << endl; + return false; + } + } + + MVT::MvRandom(*B); + std::vector zeros(numvecs,STS::zero()); + MVT::MvScale(*B,zeros); + MVT::MvNorm(*B, norms); + for (int i=0; istream(Warnings) + << "*** ERROR *** MultiVecTraits::MvScale(alphas) " + << "Supposedly zero vector has non-zero norm." << endl; + return false; + } + } + } + + + /*********** MvInit() and MvNorm() *********************************** + A std::vector of ones of dimension n should have norm std::sqrt(n) + 1) Init vectors to all ones + 2) Verify that norm is std::sqrt(n) + 3) Verify that norms aren't negative + + Note: I'm not sure that we can expect this to hold in practice. + Maybe something like std::abs(norm-std::sqrt(n)) < STS::eps() ??? + The sum of 1^2==1 should be n, but what about std::sqrt(n)? + They may be using a different square root than ScalartTraits + On my iBook G4 and on jeter, this test works. + Right now, this has been demoted to a warning. + *********************************************************************/ + { + Teuchos::RCP B = MVT::Clone(*A,numvecs); + std::vector norms(numvecs); + + MVT::MvInit(*B,one); + MVT::MvNorm(*B, norms); + bool BadNormWarning = false; + for (int i=0; istream(Warnings) + << "*** ERROR *** MultiVecTraits::MvRandom()." << endl + << "Vector had negative norm." << endl; + return false; + } + else if ( norms[i] != STS::squareroot(MVT::GetGlobalLength(*B)) && !BadNormWarning ) { + om->stream(Warnings) + << endl + << "Warning testing MultiVecTraits::MvInit()." << endl + << "Ones std::vector should have norm std::sqrt(dim)." << endl + << "norms[i]: " << norms[i] << "\tdim: " << MVT::GetGlobalLength(*B) << endl << endl; + BadNormWarning = true; + } + } + } + + + /*********** MvInit() and MvNorm() *********************************** + A std::vector of zeros of dimension n should have norm 0 + 1) Verify that norms aren't negative + 2) Verify that norms are zero + + We must know this works before the next tests. + *********************************************************************/ + { + Teuchos::RCP B = MVT::Clone(*A,numvecs); + std::vector norms(numvecs); + MVT::MvInit(*B, zero_mag); + MVT::MvNorm(*B, norms); + for (int i=0; istream(Warnings) + << "*** ERROR *** MultiVecTraits::MvInit()." << endl + << "Vector had negative norm." << endl; + return false; + } + else if ( norms[i] != zero_mag ) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvInit()." << endl + << "Zero std::vector should have norm zero." << endl; + return false; + } + } + } + + + /*********** CloneCopy(MV,std::vector) and MvNorm ******************** + 1) Check quantity/length of vectors + 2) Check std::vector norms for agreement + 3) Zero out B and make sure that C norms are not affected + *********************************************************************/ + { + Teuchos::RCP B, C; + std::vector norms(numvecs), norms2(numvecs); + + B = MVT::Clone(*A,numvecs); + MVT::MvRandom(*B); + MVT::MvNorm(*B, norms); + C = MVT::CloneCopy(*B,ind); + MVT::MvNorm(*C, norms2); + if ( MVT::GetNumberVecs(*C) != numvecs_2 ) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::CloneCopy(ind)." << endl + << "Wrong number of vectors." << endl; + return false; + } + if ( MVT::GetGlobalLength(*C) != MVT::GetGlobalLength(*B) ) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::CloneCopy(ind)." << endl + << "Vector lengths don't match." << endl; + return false; + } + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::CloneCopy(ind)." << endl + << "Copied vectors do not agree: " + << norms2[i] << " != " << norms[ind[i]] << endl + << "Difference " << STS::magnitude (norms2[i] - norms[ind[i]]) + << " exceeds the tolerance 100*eps = " << tol << endl; + return false; + } + } + MVT::MvInit(*B,zero); + MVT::MvNorm(*C, norms); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::CloneCopy(ind)." << endl + << "Copied vectors were not independent." << endl + << norms2[i] << " != " << norms[i] << endl + << "Difference " << STS::magnitude (norms2[i] - norms[i]) + << " exceeds the tolerance 100*eps = " << tol << endl; + return false; + } + } + } + + /*********** CloneCopy(MV) and MvNorm ******************************** + 1) Check quantity + 2) Check value of norms + 3) Zero out B and make sure that C is still okay + *********************************************************************/ + { + Teuchos::RCP B, C; + std::vector norms(numvecs), norms2(numvecs); + + B = MVT::Clone(*A,numvecs); + MVT::MvRandom(*B); + MVT::MvNorm(*B, norms); + C = MVT::CloneCopy(*B); + MVT::MvNorm(*C, norms2); + if ( MVT::GetNumberVecs(*C) != numvecs ) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::CloneCopy()." << endl + << "Wrong number of vectors." << endl; + return false; + } + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::CloneCopy()." << endl + << "Copied vectors do not agree: " + << norms2[i] << " != " << norms[i] << endl + << "Difference " << STS::magnitude (norms2[i] - norms[i]) + << " exceeds the tolerance 100*eps = " << tol << endl; + return false; + } + } + MVT::MvInit(*B,zero); + MVT::MvNorm(*C, norms); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::CloneCopy()." << endl + << "Copied vectors were not independent." << endl + << norms2[i] << " != " << norms[i] << endl + << "Difference " << STS::magnitude (norms2[i] - norms[i]) + << " exceeds the tolerance 100*eps = " << tol << endl; + return false; + } + } + } + + + /*********** CloneView(MV,std::vector) and MvNorm ******************** + Check that we have a view of the selected vectors + 1) Check quantity + 2) Check value of norms + 3) Zero out B and make sure that C is zero as well + *********************************************************************/ + { + Teuchos::RCP B, C; + std::vector norms(numvecs), norms2(numvecs); + + B = MVT::Clone(*A,numvecs); + MVT::MvRandom(*B); + MVT::MvNorm(*B, norms); + C = MVT::CloneViewNonConst(*B,indContig); + MVT::MvNorm(*C, norms2); + if ( MVT::GetNumberVecs(*C) != numvecs_2 ) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::CloneView(indContig)." << endl + << "Wrong number of vectors." << endl; + return false; + } + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::CloneView(indContig)." << endl + << "Viewed vectors do not agree." << endl; + return false; + } + } + } + + + /*********** CloneView(const MV,std::vector) and MvNorm() ************ + Check that we have a view of the selected vectors. + 1) Check quantity + 2) Check value of norms for agreement + 3) Zero out B and make sure that C is zerod as well + *********************************************************************/ + { + Teuchos::RCP B; + Teuchos::RCP C; + std::vector normsB(numvecs), normsC(numvecs_2); + std::vector allind(numvecs); + for (int i=0; istream(Warnings) + << "*** ERROR *** const MultiVecTraits::CloneView(indContig)." << endl + << "Wrong number of vectors." << endl; + return false; + } + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** const MultiVecTraits::CloneView(indContig)." << endl + << "Viewed vectors do not agree." << endl; + return false; + } + } + } + + /*********** SetBlock() and MvNorm() ********************************* + SetBlock() will copy the vectors from C into B + 1) Verify that the specified vectors were copied + 2) Verify that the other vectors were not modified + 3) Verify that C was not modified + 4) Change C and then check B to make sure it was not modified + + Use a different index set than has been used so far (distinct entries). + This is because duplicate entries will cause the std::vector to be + overwritten, making it more difficult to test. + *********************************************************************/ + { + Teuchos::RCP B, C; //numvecs = 10, numvecs_2 = 5 + std::vector normsB1(numvecs), normsB2(numvecs), + normsC1(numvecs_2), normsC2(numvecs_2); + + B = MVT::Clone(*A,numvecs); + C = MVT::Clone(*A,numvecs_2); + // Just do every other one, interleaving the vectors of C into B + ind.resize(numvecs_2); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::SetBlock()." << endl + << "Operation modified source vectors." << endl; + return false; + } + } + // check that the correct vectors of B were modified + // and the others were not + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::SetBlock()." << endl + << "Copied vectors do not agree: " << endl + << normsB2[i] << " != " << normsC1[i/2] << endl + << "Difference " << STS::magnitude (normsB2[i] - normsC1[i/2]) + << " exceeds the tolerance 100*eps = " << tol << endl; + return false; + } + } + else { + // should be an original vector + if (STS::magnitude (normsB1[i] - normsB2[i]) > tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::SetBlock()." << endl + << "Incorrect vectors were modified." << endl + << normsB1[i] << " != " << normsB2[i] << endl + << "Difference " << STS::magnitude (normsB2[i] - normsB2[i]) + << " exceeds the tolerance 100*eps = " << tol << endl; + return false; + } + } + } + MVT::MvInit(*C,zero); + MVT::MvNorm(*B,normsB1); + // verify that we copied and didn't reference + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::SetBlock()." << endl + << "Copied vectors were not independent." << endl + << normsB1[i] << " != " << normsB2[i] << endl + << "Difference " << STS::magnitude (normsB1[i] - normsB2[i]) + << " exceeds the tolerance 100*eps = " << tol << endl; + return false; + } + } + } + + + /*********** SetBlock() and MvNorm() ********************************* + SetBlock() will copy the vectors from C into B + 1) Verify that the specified vectors were copied + 2) Verify that the other vectors were not modified + 3) Verify that C was not modified + 4) Change C and then check B to make sure it was not modified + + Use a different index set than has been used so far (distinct entries). + This is because duplicate entries will cause the std::vector to be + overwritten, making it more difficult to test. + + These tests are the same as the ones above, except that the + number of indices (to be copied into B) is less than the number + of vectors in C, so that not all of C is put into B. + *********************************************************************/ + { + Teuchos::RCP B, C; + // set these: we assume below that setSize*2=BSize + const int CSize = 6, + setSize = 5, + BSize = 2*setSize; + std::vector normsB1(BSize), normsB2(BSize), + normsC1(CSize), normsC2(CSize); + + B = MVT::Clone(*A,BSize); + C = MVT::Clone(*A,CSize); + // Just do every other one, interleaving the vectors of C into B + ind.resize(setSize); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::SetBlock()." << endl + << "Operation modified source vectors." << endl; + return false; + } + } + // check that the correct vectors of B were modified + // and the others were not + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::SetBlock()." << endl + << "Copied vectors do not agree: " << endl + << normsB2[i] << " != " << normsC1[i/2] << endl + << "Difference " << diff << " exceeds the tolerance 100*eps = " + << tol << endl; + return false; + } + } + else { + // should be an original vector + const MagType diff = STS::magnitude (normsB1[i] - normsB2[i]); + //if ( normsB1[i] != normsB2[i] ) { + if (diff > tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::SetBlock()." << endl + << "Incorrect vectors were modified." << endl + << normsB1[i] << " != " << normsB2[i] << endl + << "Difference " << diff << " exceeds the tolerance 100*eps = " + << tol << endl; + return false; + } + } + } + MVT::MvInit(*C,zero); + MVT::MvNorm(*B,normsB1); + // verify that we copied and didn't reference + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::SetBlock()." << endl + << "Copied vectors were not independent." << endl + << normsB1[i] << " != " << normsB2[i] << endl + << "Difference " << STS::magnitude (normsB1[i] - normsB2[i]) + << " exceeds the tolerance 100*eps = " << tol << endl; + return false; + } + } + } + + + /*********** MvTransMv() ********************************************* + Performs C = alpha * A^H * B, where + alpha is type ScalarType + A,B are type MV with p and q vectors, respectively + C is a SerialDenseMatrix ALREADY sized to p by q + + Verify: + 1) C is not resized by the routine + 3) Check that zero*(A^H B) == zero + 3) Check inner product inequality: + [ |a1|*|b1| ... |ap|*|b1| ] + [a1 ... ap]^H [b1 ... bq] <= [ ... |ai|*|bj| ... ] + [ |ap|*|b1| ... |ap|*|bq| ] + 4) Zero B and check that C is zero + 5) Zero A and check that C is zero + + Note: Should we really require that C is correctly sized already? + Epetra does (and crashes if it isn't.) + *********************************************************************/ + { + const int p = 7; + const int q = 9; + Teuchos::RCP B, C; + std::vector normsB(p), normsC(q); + Teuchos::SerialDenseMatrix SDM(p,q); + + B = MVT::Clone(*A,p); + C = MVT::Clone(*A,q); + + // randomize the multivectors + MVT::MvRandom(*B); + MVT::MvNorm(*B,normsB); + MVT::MvRandom(*C); + MVT::MvNorm(*C,normsC); + + // perform SDM = zero() * B^H * C + MVT::MvTransMv( zero, *B, *C, SDM ); + + // check the sizes: not allowed to have shrunk + if ( SDM.numRows() != p || SDM.numCols() != q ) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTransMv()." << endl + << "Routine resized SerialDenseMatrix." << endl; + return false; + } + + // check that zero**A^H*B == zero + if ( SDM.normOne() != zero ) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTransMv()." << endl + << "Scalar argument processed incorrectly." << endl; + return false; + } + + // perform SDM = one * B^H * C + MVT::MvTransMv( one, *B, *C, SDM ); + + // check the norms: a^H b = |a| |b| cos(theta) <= |a| |b| + // with equality only when a and b are colinear + for (int i=0; i STS::magnitude(normsB[i]*normsC[j]) ) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTransMv()." << endl + << "Triangle inequality did not hold: " + << STS::magnitude(SDM(i,j)) + << " > " + << STS::magnitude(normsB[i]*normsC[j]) + << endl; + return false; + } + } + } + MVT::MvInit(*C); + MVT::MvRandom(*B); + MVT::MvTransMv( one, *B, *C, SDM ); + for (int i=0; istream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTransMv()." << endl + << "Inner products not zero for C==0." << endl; + return false; + } + } + } + MVT::MvInit(*B); + MVT::MvRandom(*C); + MVT::MvTransMv( one, *B, *C, SDM ); + for (int i=0; istream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTransMv()." << endl + << "Inner products not zero for B==0." << endl; + return false; + } + } + } + } + + + /*********** MvDot() ************************************************* + Verify: + 1) Results std::vector not resized + 2) Inner product inequalities are satisfied + 3) Zero vectors give zero inner products + *********************************************************************/ + { + const int p = 7; + const int q = 9; + Teuchos::RCP B, C; + std::vector iprods(p+q); + std::vector normsB(numvecs), normsC(numvecs); + + B = MVT::Clone(*A,p); + C = MVT::Clone(*A,p); + + MVT::MvRandom(*B); + MVT::MvRandom(*C); + MVT::MvNorm(*B,normsB); + MVT::MvNorm(*C,normsC); + MVT::MvDot( *B, *C, iprods ); + if ( iprods.size() != p+q ) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvDot." << endl + << "Routine resized results std::vector." << endl; + return false; + } + for (int i=0; i STS::magnitude(normsB[i]*normsC[i]) ) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvDot()." << endl + << "Inner products not valid." << endl; + return false; + } + } + MVT::MvInit(*B); + MVT::MvRandom(*C); + MVT::MvDot( *B, *C, iprods ); + for (int i=0; istream(Warnings) + << "*** ERROR *** MultiVecTraits::MvDot()." << endl + << "Inner products not zero for B==0." << endl; + return false; + } + } + MVT::MvInit(*C); + MVT::MvRandom(*B); + MVT::MvDot( *B, *C, iprods ); + for (int i=0; istream(Warnings) + << "*** ERROR *** MultiVecTraits::MvDot()." << endl + << "Inner products not zero for C==0." << endl; + return false; + } + } + } + + + /*********** MvAddMv() *********************************************** + D = alpha*B + beta*C + 1) Use alpha==0,beta==1 and check that D == C + 2) Use alpha==1,beta==0 and check that D == B + 3) Use D==0 and D!=0 and check that result is the same + 4) Check that input arguments are not modified + *********************************************************************/ + { + const int p = 7; + Teuchos::RCP B, C, D; + std::vector normsB1(p), normsB2(p), + normsC1(p), normsC2(p), + normsD1(p), normsD2(p); + + Teuchos::SerialDenseMatrix Alpha(1,1), Beta(1,1); + Teuchos::randomSyncedMatrix( Alpha ); + Teuchos::randomSyncedMatrix( Beta ); + ScalarType alpha = Alpha(0,0), + beta = Beta(0,0); + + B = MVT::Clone(*A,p); + C = MVT::Clone(*A,p); + D = MVT::Clone(*A,p); + + MVT::MvRandom(*B); + MVT::MvRandom(*C); + MVT::MvNorm(*B,normsB1); + MVT::MvNorm(*C,normsC1); + + // check that 0*B+1*C == C + MVT::MvAddMv(zero,*B,one,*C,*D); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC2); + MVT::MvNorm(*D,normsD1); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvAddMv()." << endl + << "Input arguments were modified." << endl; + return false; + } + else if (STS::magnitude (normsC1[i] - normsC2[i]) > tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvAddMv()." << endl + << "Input arguments were modified." << endl; + return false; + } + else if (STS::magnitude (normsC1[i] - normsD1[i]) > tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvAddMv()." << endl + << "Assignment did not work." << endl; + return false; + } + } + + // check that 1*B+0*C == B + MVT::MvAddMv(one,*B,zero,*C,*D); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC2); + MVT::MvNorm(*D,normsD1); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvAddMv()." << endl + << "Input arguments were modified." << endl; + return false; + } + else if (STS::magnitude (normsC1[i] - normsC2[i]) > tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvAddMv()." << endl + << "Input arguments were modified." << endl; + return false; + } + else if (STS::magnitude (normsB1[i] - normsD1[i]) > tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvAddMv()." << endl + << "Assignment did not work." << endl; + return false; + } + } + + // check that alpha*B+beta*C -> D is invariant under initial D + // first, try random D + MVT::MvRandom(*D); + MVT::MvAddMv(alpha,*B,beta,*C,*D); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC2); + MVT::MvNorm(*D,normsD1); + // check that input args are not modified + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvAddMv()." << endl + << "Input arguments were modified." << endl; + return false; + } + else if (STS::magnitude (normsC1[i] - normsC2[i]) > tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvAddMv()." << endl + << "Input arguments were modified." << endl; + return false; + } + } + // next, try zero D + MVT::MvInit(*D); + MVT::MvAddMv(alpha,*B,beta,*C,*D); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC2); + MVT::MvNorm(*D,normsD2); + // check that input args are not modified and that D is the same + // as the above test + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvAddMv()." << endl + << "Input arguments were modified." << endl; + return false; + } + else if (STS::magnitude (normsC1[i] - normsC2[i]) > tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvAddMv()." << endl + << "Input arguments were modified." << endl; + return false; + } + else if (STS::magnitude (normsD1[i] - normsD2[i]) > tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvAddMv()." << endl + << "Results varies depending on initial state of dest vectors." << endl; + return false; + } + } + } + + /*********** MvAddMv() *********************************************** + Similar to above, but where B or C are potentially the same + object as D. This case is commonly used, for example, to affect + A <- alpha*A + via + MvAddMv(alpha,A,zero,A,A) + ** OR ** + MvAddMv(zero,A,alpha,A,A) + + The result is that the operation has to be "atomic". That is, + B and C are no longer reliable after D is modified, so that + the assignment to D must be the last thing to occur. + + D = alpha*B + beta*C + + 1) Use alpha==0,beta==1 and check that D == C + 2) Use alpha==1,beta==0 and check that D == B + *********************************************************************/ + { + const int p = 7; + Teuchos::RCP B, D; + Teuchos::RCP C; + std::vector normsB(p), + normsD(p); + std::vector lclindex(p); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvAddMv() #2" << endl + << "Assignment did not work." << endl; + return false; + } + } + + // check that 1*B+0*C == B + MVT::MvAddMv(one,*B,zero,*C,*D); + MVT::MvNorm(*D,normsD); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvAddMv() #2" << endl + << "Assignment did not work." << endl; + return false; + } + } + + } + + /*********** MvTimesMatAddMv() 7 by 5 ******************************** + C = alpha*B*SDM + beta*C + 1) Use alpha==0, SDM!=0, beta==1 and check that C is unchanged + 2) Use alpha==0, SDM!=0, beta==0 and check that C is set to zero + 3) Use alpha==1, SDM==I, beta==0 and check that C is set to B + 4) Use alpha==1, SDM==0, beta==1 and check that C is unchanged + 5) Test with non-square matrices + 6) Always check that input arguments are not modified + *********************************************************************/ + { + const int p = 7, q = 5; + Teuchos::RCP B, C; + Teuchos::SerialDenseMatrix SDM(p,q); + std::vector normsC1(q), normsC2(q), + normsB1(p), normsB2(p); + + B = MVT::Clone(*A,p); + C = MVT::Clone(*A,q); + + // Test 1: alpha==0, SDM!=0, beta==1 and check that C is unchanged + MVT::MvRandom(*B); + MVT::MvRandom(*C); + MVT::MvNorm(*B,normsB1); + MVT::MvNorm(*C,normsC1); + Teuchos::randomSyncedMatrix(SDM); + MVT::MvTimesMatAddMv(zero,*B,SDM,one,*C); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC2); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Input vectors were modified." << endl; + return false; + } + } + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Arithmetic test 1 failed." << endl; + return false; + } + } + + // Test 2: alpha==0, SDM!=0, beta==0 and check that C is set to zero + MVT::MvRandom(*B); + MVT::MvRandom(*C); + MVT::MvNorm(*B,normsB1); + MVT::MvNorm(*C,normsC1); + Teuchos::randomSyncedMatrix(SDM); + MVT::MvTimesMatAddMv(zero,*B,SDM,zero,*C); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC2); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Input vectors were modified." << endl; + return false; + } + } + for (int i=0; istream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Arithmetic test 2 failed: " + << normsC2[i] + << " != " + << zero + << endl; + return false; + } + } + + // Test 3: alpha==1, SDM==|I|, beta==0 and check that C is set to B + // |0| + MVT::MvRandom(*B); + MVT::MvRandom(*C); + MVT::MvNorm(*B,normsB1); + MVT::MvNorm(*C,normsC1); + SDM.scale(zero); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Input vectors were modified." << endl; + return false; + } + } + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Arithmetic test 3 failed: " + << normsB1[i] + << " != " + << normsC2[i] + << endl; + return false; + } + } + + // Test 4: alpha==1, SDM==0, beta==1 and check that C is unchanged + MVT::MvRandom(*B); + MVT::MvRandom(*C); + MVT::MvNorm(*B,normsB1); + MVT::MvNorm(*C,normsC1); + SDM.scale(zero); + MVT::MvTimesMatAddMv(one,*B,SDM,one,*C); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC2); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Input vectors were modified." << endl; + return false; + } + } + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Arithmetic test 4 failed." << endl; + return false; + } + } + } + + /*********** MvTimesMatAddMv() 5 by 7 ******************************** + C = alpha*B*SDM + beta*C + 1) Use alpha==0, SDM!=0, beta==1 and check that C is unchanged + 2) Use alpha==0, SDM!=0, beta==0 and check that C is set to zero + 3) Use alpha==1, SDM==I, beta==0 and check that C is set to B + 4) Use alpha==1, SDM==0, beta==1 and check that C is unchanged + 5) Test with non-square matrices + 6) Always check that input arguments are not modified + *********************************************************************/ + { + const int p = 5, q = 7; + Teuchos::RCP B, C; + Teuchos::SerialDenseMatrix SDM(p,q); + std::vector normsC1(q), normsC2(q), + normsB1(p), normsB2(p); + + B = MVT::Clone(*A,p); + C = MVT::Clone(*A,q); + + // Test 5: alpha==0, SDM!=0, beta==1 and check that C is unchanged + MVT::MvRandom(*B); + MVT::MvRandom(*C); + MVT::MvNorm(*B,normsB1); + MVT::MvNorm(*C,normsC1); + Teuchos::randomSyncedMatrix(SDM); + MVT::MvTimesMatAddMv(zero,*B,SDM,one,*C); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC2); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Input vectors were modified." << endl; + return false; + } + } + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Arithmetic test 5 failed." << endl; + return false; + } + } + + // Test 6: alpha==0, SDM!=0, beta==0 and check that C is set to zero + MVT::MvRandom(*B); + MVT::MvRandom(*C); + MVT::MvNorm(*B,normsB1); + MVT::MvNorm(*C,normsC1); + Teuchos::randomSyncedMatrix(SDM); + MVT::MvTimesMatAddMv(zero,*B,SDM,zero,*C); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC2); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Input vectors were modified." << endl; + return false; + } + } + for (int i=0; istream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Arithmetic test 6 failed: " + << normsC2[i] + << " != " + << zero + << endl; + return false; + } + } + + // Test 7: alpha==1, SDM==[I 0], beta==0 and check that C is set to B + MVT::MvRandom(*B); + MVT::MvRandom(*C); + MVT::MvNorm(*B,normsB1); + MVT::MvNorm(*C,normsC1); + SDM.scale(zero); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Input vectors were modified." << endl; + return false; + } + } + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Arithmetic test 7 failed." << endl; + return false; + } + } + for (int i=p; istream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Arithmetic test 7 failed." << endl; + return false; + } + } + + // Test 8: alpha==1, SDM==0, beta==1 and check that C is unchanged + MVT::MvRandom(*B); + MVT::MvRandom(*C); + MVT::MvNorm(*B,normsB1); + MVT::MvNorm(*C,normsC1); + SDM.scale(zero); + MVT::MvTimesMatAddMv(one,*B,SDM,one,*C); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC2); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Input vectors were modified." << endl; + return false; + } + } + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** MultiVecTraits::MvTimesMatAddMv()." << endl + << "Arithmetic test 8 failed." << endl; + return false; + } + } + } + + return true; + + } + + + + /// \brief Test correctness of OperatorTraits specialization and its + /// operator implementation. + /// + /// \tparam ScalarType The type of the entries in the multivectors; + /// the first template parameter of MultiVecTraits and + /// OperatorTraits. + /// \tparam MV The multivector type; the second template parameter + /// of MultiVecTraits and OperatorTraits. + /// \tparam OP The operator type; the third template parameter + /// of OperatorTraits. + /// + /// \param om [in/out] A valid OutputManager, for displaying test results. + /// + /// \param A [in] An initial multivector, to use for making new + /// multivectors. (Belos doesn't currently have a "vector space" + /// abstraction; making a new multivector requires a valid input + /// multivector to clone.) + /// + /// \param M [in] The operator to test. + /// + /// \return Test result: true if all tests passed, else false. + template< class ScalarType, class MV, class OP> + bool + TestKokkosOperatorTraits (const Teuchos::RCP > &om, + const Teuchos::RCP &A, + const Teuchos::RCP &M) + { + using Teuchos::SetScientific; + using std::endl; + typedef MultiVecTraits MVT; + typedef Teuchos::ScalarTraits STS; + typedef typename STS::magnitudeType MagType; + + // Make sure that all floating-point numbers are printed with the + // right precision. + SetScientific sci (om->stream (Warnings)); + + // FIXME (mfh 09 Jan 2013) Added an arbitrary tolerance in case + // norms are not computed deterministically (which is possible + // even with MPI only, and more likely with threads). + const MagType tol = Teuchos::as (100) * STS::eps (); + + /* OPT Contract: + Apply() + MV: OP*zero == zero + Warn if OP is not deterministic (OP*A != OP*A) + Does not modify input arguments + *********************************************************************/ + + typedef MultiVecTraits MVT; + typedef Teuchos::ScalarTraits STS; + typedef OperatorTraits OPT; + typedef typename STS::magnitudeType MagType; + + const int numvecs = 10; + + Teuchos::RCP B = MVT::Clone(*A,numvecs), + C = MVT::Clone(*A,numvecs); + + std::vector normsB1(numvecs), normsB2(numvecs), + normsC1(numvecs), normsC2(numvecs); + bool NonDeterministicWarning; + + /*********** Apply() ************************************************* + Verify: + 1) OP*B == OP*B; OP is deterministic (just warn on this) + 2) OP*zero == 0 + 3) OP*B doesn't modify B + 4) OP*B is invariant under initial state of destination vectors + *********************************************************************/ + MVT::MvInit(*B); + MVT::MvRandom(*C); + MVT::MvNorm(*B,normsB1); + OPT::Apply(*M,*B,*C); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC2); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** OperatorTraits::Apply() [1]" << endl + << "Apply() modified the input vectors." << endl + << "Original: " << normsB1[i] << "; After: " << normsB2[i] << endl + << "Difference " << STS::magnitude (normsB2[i] - normsB1[i]) + << " exceeds the tolerance 100*eps = " << tol << endl; + return false; + } + if (normsC2[i] != STS::zero()) { + om->stream(Warnings) + << "*** ERROR *** OperatorTraits::Apply() [1]" << endl + << "Operator applied to zero did not return zero." << endl; + return false; + } + } + + // If we send in a random matrix, we should not get a zero return + MVT::MvRandom(*B); + MVT::MvNorm(*B,normsB1); + OPT::Apply(*M,*B,*C); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC2); + bool ZeroWarning = false; + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** OperatorTraits::Apply() [2]" << endl + << "Apply() modified the input vectors." << endl + << "Original: " << normsB1[i] << "; After: " << normsB2[i] << endl + << "Difference " << STS::magnitude (normsB2[i] - normsB1[i]) + << " exceeds the tolerance 100*eps = " << tol << endl; + return false; + } + if (normsC2[i] == STS::zero() && ZeroWarning==false ) { + om->stream(Warnings) + << "*** ERROR *** OperatorTraits::Apply() [2]" << endl + << "Operator applied to random vectors returned zero." << endl; + ZeroWarning = true; + } + } + + // Apply operator with C init'd to zero + MVT::MvRandom(*B); + MVT::MvNorm(*B,normsB1); + MVT::MvInit(*C); + OPT::Apply(*M,*B,*C); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC1); + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** OperatorTraits::Apply() [3]" << endl + << "Apply() modified the input vectors." << endl + << "Original: " << normsB1[i] << "; After: " << normsB2[i] << endl + << "Difference " << STS::magnitude (normsB2[i] - normsB1[i]) + << " exceeds the tolerance 100*eps = " << tol << endl; + return false; + } + } + + // Apply operator with C init'd to random + // Check that result is the same as before; warn if not. + // This could be a result of a bug, or a stochastic + // operator. We do not want to prejudice against a + // stochastic operator. + MVT::MvRandom(*C); + OPT::Apply(*M,*B,*C); + MVT::MvNorm(*B,normsB2); + MVT::MvNorm(*C,normsC2); + NonDeterministicWarning = false; + for (int i=0; i tol) { + om->stream(Warnings) + << "*** ERROR *** OperatorTraits::Apply() [4]" << endl + << "Apply() modified the input vectors." << endl + << "Original: " << normsB1[i] << "; After: " << normsB2[i] << endl + << "Difference " << STS::magnitude (normsB2[i] - normsB1[i]) + << " exceeds the tolerance 100*eps = " << tol << endl; + return false; + } + if (normsC1[i] != normsC2[i] && !NonDeterministicWarning) { + om->stream(Warnings) + << endl + << "*** WARNING *** OperatorTraits::Apply() [4]" << endl + << "Apply() returned two different results." << endl << endl; + NonDeterministicWarning = true; + } + } + + return true; + + } + +} + +#endif diff --git a/packages/belos/kokkos/test/CMakeLists.txt b/packages/belos/kokkos/test/CMakeLists.txt new file mode 100644 index 000000000000..2a37af1bd697 --- /dev/null +++ b/packages/belos/kokkos/test/CMakeLists.txt @@ -0,0 +1,8 @@ +INCLUDE_DIRECTORIES(${Belos_SOURCE_DIR}/kokkos/src) + +TRIBITS_ADD_EXECUTABLE_AND_TEST( + KokkosMVOPTest + SOURCES KokkosMVOPTest.cpp + COMM serial mpi + ) + diff --git a/packages/belos/kokkos/test/KokkosMVOPTest.cpp b/packages/belos/kokkos/test/KokkosMVOPTest.cpp new file mode 100644 index 000000000000..1f64b64354f4 --- /dev/null +++ b/packages/belos/kokkos/test/KokkosMVOPTest.cpp @@ -0,0 +1,553 @@ +//@HEADER +// ************************************************************************ +// +// Belos: Block Linear Solvers Package +// Copyright 2004 Sandia Corporation +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Michael A. Heroux (maherou@sandia.gov) +// +// ************************************************************************ +//@HEADER +// +// This test uses the MVOPTester.hpp functions to test the Belos adapters +// to Kokkos. +// + + +#include "BelosConfigDefs.hpp" +#include "BelosKokkosMVOPTester.hpp" +#include "BelosOutputManager.hpp" +#include "BelosKokkosAdapter.hpp" + +#include "Teuchos_StandardCatchMacros.hpp" +#ifdef HAVE_MPI + #include +#endif + +using std::cout; +using std::endl; +using Teuchos::RCP; +using Belos::Warnings; +using Belos::OutputManager; + +template +bool TestKokkosMultiVecOneScalar(const Teuchos::RCP >& ); + +template +bool TestKokkosMultiVecTwoScalar(const Teuchos::RCP > & outputMgr); + +int main(int argc, char *argv[]) +{ +#ifdef HAVE_MPI + MPI_Init(&argc,&argv); +#endif + bool isPassed; + bool success = true; + Kokkos::initialize(); + { + bool verbose = false; + if (argc>1) { + if (argv[1][0]=='-' && argv[1][1]=='v') { + verbose = true; + } + } + typedef double ScalarType; + typedef float ScalarType2; + typedef Belos::MultiVec KMV; + typedef Belos::Operator KOP; + typedef Belos::MultiVec KMV2; + typedef Belos::Operator KOP2; + typedef Kokkos::DefaultExecutionSpace EXSP; + + // Create an output manager to handle the I/O from the solver (defaults to std::cout). + Teuchos::RCP > myOutputMgr = Teuchos::rcp( new Belos::OutputManager() ); + Teuchos::RCP > myOutputMgr2 = Teuchos::rcp( new Belos::OutputManager() ); + if (verbose) { + myOutputMgr->setVerbosity( Warnings ); + myOutputMgr2->setVerbosity( Warnings ); + } +try { + // number of global elements + int dim = 10; + int blockSize = 5; + std::vector norms(blockSize); + + + //***************************************************************************************************************************** + //***************************************************************************************************************************** + // Run native Kokkos::Multivec test (double). + isPassed = TestKokkosMultiVecOneScalar(myOutputMgr); + success = isPassed && success; + if (isPassed) { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter PASSED TestKokkosMultiVecOneScalar() double. \n"); + } + else { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter FAILED TestKokkosMultiVecOneScalar() double. ***\n\n"); + } + + // Run native Kokkos::Multivec test (single). + isPassed = TestKokkosMultiVecOneScalar(myOutputMgr2); + success = isPassed && success; + if (isPassed) { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter PASSED TestKokkosMultiVecOneScalar() single. \n"); + } + else { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter FAILED TestKokkosMultiVecOneScalar() single. ***\n\n"); + } + + //***************************************************************************************************************************** + //***************************************************************************************************************************** + // Run native Kokkos::Multivec tests with mixed single-double precisions: + isPassed = TestKokkosMultiVecTwoScalar(myOutputMgr); + success = isPassed && success; + if (isPassed) { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter PASSED TestKokkosMultiVecTwoScalar() \n"); + } + else { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter FAILED TestKokkosMultiVecTwoScalar() ***\n\n"); + } + + //***************************************************************************************************************************** + //***************************************************************************************************************************** + // Run Belos tests on Kokkos MultiVec (double precision): + Teuchos::RCP> ivec = Teuchos::rcp( new Belos::KokkosMultiVec(dim, blockSize) ); + ivec->MvRandom(); + isPassed = Belos::TestKokkosMultiVecTraits(myOutputMgr,ivec); + success = isPassed && success; + if (isPassed) { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter PASSED TestMultiVecTraits() double.\n"); + } + else { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter FAILED TestMultiVecTraits() double. ***\n\n"); + } + + // Run Belos tests on Kokkos MultiVec (single precision): + Teuchos::RCP> ivec_2 = Teuchos::rcp( new Belos::KokkosMultiVec(dim, blockSize) ); + ivec_2->MvRandom(); + isPassed = Belos::TestKokkosMultiVecTraits(myOutputMgr2,ivec_2); + success = isPassed && success; + if (isPassed) { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter PASSED TestMultiVecTraits() single. \n"); + } + else { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter FAILED TestMultiVecTraits() single. ***\n\n"); + } + //***************************************************************************************************************************** + //***************************************************************************************************************************** + // Read in a matrix Market file and use it to test the Kokkos Operator (double precision). + KokkosSparse::CrsMatrix crsMat = + KokkosKernels::Impl::read_kokkos_crst_matrix>("bcsstk13.mtx"); + Teuchos::RCP> myOp = + Teuchos::rcp(new Belos::KokkosCrsOperator(crsMat)); + + Teuchos::RCP> ivec3 = Teuchos::rcp( new Belos::KokkosMultiVec(2003, 2) ); + + isPassed = Belos::TestKokkosOperatorTraits(myOutputMgr,ivec3,myOp); + success = isPassed && success; + if (isPassed) { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter PASSED TestOperatorTraits() double. \n"); + } + else { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter FAILED TestOperatorTraits() double ***\n\n"); + } + // Read in a matrix Market file and use it to test the Kokkos Operator (single precision). + KokkosSparse::CrsMatrix crsMat2 = + KokkosKernels::Impl::read_kokkos_crst_matrix>("bcsstk13.mtx"); + Teuchos::RCP> myOp2 = + Teuchos::rcp(new Belos::KokkosCrsOperator(crsMat2)); + + Teuchos::RCP> ivec3_2 = Teuchos::rcp( new Belos::KokkosMultiVec(2003, 2) ); + + isPassed = Belos::TestKokkosOperatorTraits(myOutputMgr2,ivec3_2,myOp2); + success = isPassed && success; + if (isPassed) { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter PASSED TestOperatorTraits() single\n"); + } + else { + myOutputMgr->print(Belos::Warnings,"*** KokkosAdapter FAILED TestOperatorTraits() single ***\n\n"); + } + //***************************************************************************************************************************** + //***************************************************************************************************************************** + + if (!success) { + myOutputMgr->print(Belos::Warnings,"End Result: TEST FAILED\n"); + } else { + myOutputMgr->print(Belos::Warnings,"End Result: TEST PASSED\n"); + } + }//end try block + + + TEUCHOS_STANDARD_CATCH_STATEMENTS(verbose,std::cerr,success); + } + Kokkos::finalize(); +#ifdef HAVE_MPI + MPI_Finalize(); +#endif + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} + +template +bool TestKokkosMultiVecOneScalar(const Teuchos::RCP > & outputMgr){ + int dim = 10; + int blockSize = 5; + std::vector norms(blockSize); + + /// Test KokkosMultiVec constructors: + // Test constructor #1: + Belos::KokkosMultiVec myVec1("myLabel", dim, blockSize); + if ( myVec1.GetNumberVecs() != blockSize ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec constructor 1 returned wrong value " + << "for GetNumberVecs()." << endl; + return false; + } + if ( myVec1.GetGlobalLength() != dim ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec constructor 1 returned wrong value " + << "for GetGlobalLength()." << endl; + return false; + } + myVec1.MvNorm(norms); + for(int i = 0; i < blockSize; i++){ + if( norms[i] != 0 ){ + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec constructor 1 returned wrong nrm2 value. " + << "Vector was not initialized to zeros." << endl; + return false; + } + } + // Test constructor #2: + Belos::KokkosMultiVec myVec2(dim, blockSize); + if ( myVec2.GetNumberVecs() != blockSize ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec constructor 2 returned wrong value " + << "for GetNumberVecs()." << endl; + return false; + } + if ( myVec2.GetGlobalLength() != dim ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec constructor 2 returned wrong value " + << "for GetGlobalLength()." << endl; + return false; + } + myVec2.MvNorm(norms); + for(int i = 0; i < blockSize; i++){ + if( norms[i] != 0 ){ + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec constructor 2 returned wrong nrm2 value. " + << "Vector was not initialized to zeros." << endl; + return false; + } + } + // Test constructor #3: + Belos::KokkosMultiVec myVec3(2*dim); + if ( myVec3.GetNumberVecs() != 1 ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec constructor 3 returned wrong value " + << "for GetNumberVecs()." << endl; + return false; + } + if ( myVec3.GetGlobalLength() != 2*dim ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec constructor 3 returned wrong value " + << "for GetGlobalLength()." << endl; + return false; + } + myVec3.MvNorm(norms); + if( norms[0] != 0 ){ + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec constructor 3 returned wrong nrm2 value. " + << "Vector was not initialized to zeros." << endl; + return false; + } + // Test copy constructor (should deep copy). + Belos::KokkosMultiVec myVecCopy(myVec3); + if ( myVecCopy.GetNumberVecs() != 1 ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec copy constructor returned wrong value " + << "for GetNumberVecs()." << endl; + return false; + } + if ( myVecCopy.GetGlobalLength() != 2*dim ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec copy constructor returned wrong value " + << "for GetGlobalLength()." << endl; + return false; + } + myVecCopy.MvRandom(); + myVecCopy.MvNorm(norms); + if( norms[0] == 0 ){ + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec MvRandom did not fill with random values. " << endl; + return false; + } + myVec3.MvNorm(norms); + if( norms[0] != 0 ){ + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec copy constructor did not deep copy. " << endl; + return false; + } + // Test assignment operator (should also deep copy). + myVecCopy = myVec2; + if ( myVecCopy.GetNumberVecs() != blockSize ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec assignment = returned wrong value " + << "for GetNumberVecs()." << endl; + return false; + } + if ( myVecCopy.GetGlobalLength() != dim ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec assignment = returned wrong value " + << "for GetGlobalLength()." << endl; + return false; + } + myVec2.MvInit(3.0); + myVecCopy.MvNorm(norms); + for(int i = 0; i < blockSize; i++){ + if( norms[i] != 0 ){ + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec assignment = returned wrong nrm2 value. " + << "Vector was not deep copied." << endl; + return false; + } + } + // Test view to multivec: + int numCols2 = 4; + int numRows2 = 60; + Kokkos::View myView("View2MV", numRows2, numCols2); + typename Kokkos::View::HostMirror myView_h("View2MV_host", numRows2, numCols2); + Kokkos::deep_copy(myView, 42); + Belos::KokkosMultiVec myVec4( myView ); + if ( myVec4.GetNumberVecs() != (int)myView.extent(1) ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec view to multivec returned wrong value " + << "for GetNumberVecs()." << endl; + return false; + } + if ( myVec4.GetGlobalLength() != (int)myView.extent(0) ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec view to multivec returned wrong value " + << "for GetGlobalLength()." << endl; + return false; + } + Kokkos::deep_copy(myView, 55); + Kokkos::deep_copy(myView_h, myVec4.GetInternalViewConst()); + if ( myView_h(5,1) != 42 ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultivec view to multivec did not make a deep copy!" << endl; + return false; + } + // Tesst view to multivec with shallow copy: + Kokkos::deep_copy(myView, 100); + Belos::KokkosMultiVec myVec5( myView, false ); + if ( myVec5.GetNumberVecs() != (int)myView.extent(1) ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec view to multivec shallow returned wrong value " + << "for GetNumberVecs()." << endl; + return false; + } + if ( myVec5.GetGlobalLength() != (int)myView.extent(0) ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec view to multivec shallow returned wrong value " + << "for GetGlobalLength()." << endl; + return false; + } + Kokkos::deep_copy(myView, 500); + Kokkos::deep_copy(myView_h, myVec5.GetInternalViewConst()); + if ( myView_h(5,1) != 500 ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultivec view to multivec shallow made a deep copy!" << endl; + return false; + } + // Test GetInternalViewNonConst: + auto myView2 = myVec5.GetInternalViewNonConst(); + Kokkos::deep_copy(myView2, 0); + std::vector norms2(4); + myVec5.MvNorm(norms2); + for(int i = 0; i < (int)myView2.extent(1); i++){ + if( norms[i] != 0 ){ + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec GetInternalViewNonConst returned wrong nrm2 value. " + << "Vector was not editable." << endl; + return false; + } + } + + return true; +} + + +template +bool TestKokkosMultiVecTwoScalar(const Teuchos::RCP > & outputMgr){ + // Test view to multivec ST1 to ST2: + int numCols = 4; + int numRows = 60; + Kokkos::View myViewST1("View2MV1", numRows, numCols); + typename Kokkos::View::HostMirror myViewST2_h("View2MV1_host", numRows, numCols); + Kokkos::deep_copy(myViewST1, 42); + Belos::KokkosMultiVec myVecST2A( myViewST1 ); + if ( myVecST2A.GetNumberVecs() != (int)myViewST1.extent(1) ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec view to multivec 2 scalar returned wrong value " + << "for GetNumberVecs()." << endl; + return false; + } + if ( myVecST2A.GetGlobalLength() != (int)myViewST1.extent(0) ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec view to multivec 2 scalar returned wrong value " + << "for GetGlobalLength()." << endl; + return false; + } + Kokkos::deep_copy(myViewST1, 55); + std::cout << "At line 424. " << std::endl; + Kokkos::deep_copy(myViewST2_h, myVecST2A.GetInternalViewConst()); + if ( myViewST2_h(5,1) != 42 ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultivec view to multivec 2 scalar did not make a deep copy!" << endl; + return false; + } + // Test view to multivec ST2 to ST1: + Kokkos::View myViewST2("View2MV2", numRows, numCols); + typename Kokkos::View::HostMirror myViewST1_h("View2MV2_host", numRows, numCols); + Kokkos::deep_copy(myViewST2, 56); + Belos::KokkosMultiVec myVecST1A( myViewST2 ); + if ( myVecST1A.GetNumberVecs() != (int)myViewST2.extent(1) ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec view to multivec 2 scalar returned wrong value " + << "for GetNumberVecs()." << endl; + return false; + } + if ( myVecST1A.GetGlobalLength() != (int)myViewST2.extent(0) ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec view to multivec 2 scalar returned wrong value " + << "for GetGlobalLength()." << endl; + return false; + } + Kokkos::deep_copy(myViewST2, 22); + std::cout << "At line 449. " << std::endl; + Kokkos::deep_copy(myViewST1_h, myVecST1A.GetInternalViewConst()); + if ( myViewST1_h(5,1) != 56 ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultivec view to multivec 2 scalar did not make a deep copy!" << endl; + return false; + } + //Test multivec copy constructor ST1 to ST2: + Belos::KokkosMultiVec myVecST2B( myVecST1A ); + if ( myVecST1A.GetNumberVecs() != myVecST2B.GetNumberVecs() ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec templated copy constructor returned wrong value " + << "for GetNumberVecs()." << endl; + return false; + } + if ( myVecST1A.GetGlobalLength() != myVecST2B.GetGlobalLength() ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec templated copy constructor returned wrong value " + << "for GetGlobalLength()." << endl; + return false; + } + myVecST1A.MvInit(3.0); + std::cout << "At line 471. " << std::endl; + Kokkos::deep_copy(myViewST2_h, myVecST2B.GetInternalViewConst()); + if ( myViewST2_h(5,1) != 56 ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultivec templated copy constructor did not make a deep copy!" << endl; + return false; + } + //Test multivec copy constructor ST2 to ST1: + Belos::KokkosMultiVec myVecST1B( myVecST2A ); + if ( myVecST1B.GetNumberVecs() != myVecST2A.GetNumberVecs() ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec templated copy constructor returned wrong value " + << "for GetNumberVecs()." << endl; + return false; + } + if ( myVecST1B.GetGlobalLength() != myVecST2A.GetGlobalLength() ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec templated copy constructor returned wrong value " + << "for GetGlobalLength()." << endl; + return false; + } + myVecST2A.MvInit(3.0); + Kokkos::deep_copy(myViewST1_h, myVecST1B.GetInternalViewConst()); + if ( myViewST1_h(5,1) != 42 ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultivec templated copy constructor did not make a deep copy!" << endl; + return false; + } + //Test assignment operator ST1 to ST2: + Belos::KokkosMultiVec myVecST2C = myVecST1A; + if ( myVecST1A.GetNumberVecs() != myVecST2C.GetNumberVecs() ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec templated assignment operator returned wrong value " + << "for GetNumberVecs()." << endl; + return false; + } + if ( myVecST1A.GetGlobalLength() != myVecST2C.GetGlobalLength() ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec templated copy constructor returned wrong value " + << "*** ERROR *** KokkosMultiVec templated assignment operator returned wrong value " + << "for GetGlobalLength()." << endl; + return false; + } + myVecST1A.MvInit(1.0); + std::cout << "At line 515. " << std::endl; + Kokkos::deep_copy(myViewST2_h, myVecST2C.GetInternalViewConst()); + if ( myViewST2_h(5,1) != 3 ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultivec templated assign op did not make a deep copy!" << endl; + return false; + } + //Test assignement operator ST2 to ST1: + Belos::KokkosMultiVec myVecST1C = myVecST2A; + if ( myVecST1C.GetNumberVecs() != myVecST2A.GetNumberVecs() ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec templated assignment operator returned wrong value " + << "for GetNumberVecs()." << endl; + return false; + } + if ( myVecST1C.GetGlobalLength() != myVecST2A.GetGlobalLength() ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultiVec templated assignment operator returned wrong value " + << "for GetGlobalLength()." << endl; + return false; + } + myVecST2A.MvInit(2.0); + Kokkos::deep_copy(myViewST1_h, myVecST1C.GetInternalViewConst()); + if ( myViewST1_h(5,1) != 3 ) { + outputMgr->stream(Warnings) + << "*** ERROR *** KokkosMultivec templated assign op did not make a deep copy!" << endl; + return false; + } + + return true; +} diff --git a/packages/framework/pr_tools/trilinos_pr.ini b/packages/framework/pr_tools/trilinos_pr.ini index cd4e817353a5..570296de3400 100644 --- a/packages/framework/pr_tools/trilinos_pr.ini +++ b/packages/framework/pr_tools/trilinos_pr.ini @@ -76,7 +76,7 @@ # ===================================================================================================================== [load-env] -supported-systems : ../../../../GenConfig/LoadEnv/examples/trilinos/supported-systems.ini -supported-envs : ../../../../GenConfig/LoadEnv/examples/trilinos/supported-envs.ini -environment-specs : ../../../../GenConfig/LoadEnv/examples/trilinos/environment-specs.ini +supported-systems : ../../../../GenConfig/LoadEnv/examples/supported-systems.ini +supported-envs : ../../../../GenConfig/LoadEnv/examples/supported-envs.ini +environment-specs : ../../../../GenConfig/LoadEnv/examples/environment-specs.ini pullrequest-specs : ./pullrequest-specs.ini diff --git a/packages/krino/CMakeLists.txt b/packages/krino/CMakeLists.txt new file mode 100644 index 000000000000..2f2c2ab9c734 --- /dev/null +++ b/packages/krino/CMakeLists.txt @@ -0,0 +1,13 @@ + +message("Building Krino as a Trilinos package") +TRIBITS_PACKAGE(Krino) + +TRIBITS_ADD_DEBUG_OPTION() +TRIBITS_ADD_SHOW_DEPRECATED_WARNINGS_OPTION() + +if (${Trilinos_ENABLE_Krino}) + add_subdirectory(krino) + add_subdirectory(delete_small_elements) +endif() + +TRIBITS_PACKAGE_POSTPROCESS() diff --git a/packages/krino/LICENSE b/packages/krino/LICENSE new file mode 100644 index 000000000000..33d63e2fe002 --- /dev/null +++ b/packages/krino/LICENSE @@ -0,0 +1,30 @@ +Copyright 2002 - 2008, 2010, 2011 National Technology & Engineering +Solutions of Sandia, LLC (NTESS). + +Under the terms of Contract DE-NA0003525 with NTESS, the U.S. Government +retains certain rights in this software. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/krino/cmake/Dependencies.cmake b/packages/krino/cmake/Dependencies.cmake new file mode 100644 index 000000000000..76d77c60821f --- /dev/null +++ b/packages/krino/cmake/Dependencies.cmake @@ -0,0 +1,8 @@ +SET(LIB_REQUIRED_DEP_PACKAGES SEACASIoss SEACASExodus STKBalance STKMath STKIO STKSearch STKTopology STKUtil STKTools STKEmend Percept Intrepid) +SET(LIB_OPTIONAL_DEP_PACKAGES) +SET(TEST_REQUIRED_DEP_PACKAGES Gtest STKUnit_test_utils) +SET(TEST_OPTIONAL_DEP_PACKAGES) +SET(LIB_REQUIRED_DEP_TPLS Boost) +SET(LIB_OPTIONAL_DEP_TPLS MPI yaml-cpp) +SET(TEST_REQUIRED_DEP_TPLS) +SET(TEST_OPTIONAL_DEP_TPLS) diff --git a/packages/krino/cmake_install_test/build_sierra_krino_as_Trilinos_package.sh b/packages/krino/cmake_install_test/build_sierra_krino_as_Trilinos_package.sh new file mode 100755 index 000000000000..650cc502eca4 --- /dev/null +++ b/packages/krino/cmake_install_test/build_sierra_krino_as_Trilinos_package.sh @@ -0,0 +1,108 @@ +#!/bin/sh + +function execute() { + stdbuf -o0 -e0 echo "% $@" ; + eval "$@" ; + if [ $? -ne 0 ] ; then + echo "'$@' failed."; + exit 1; + fi +} + +function cd_to_new_dir() +{ + execute rm -rf $1 + execute mkdir $1 + execute cd $1 +} + +function exit_with_message() +{ + echo $1 + exit 1 +} + +function make_and_install() +{ + make -j 16 || exit_with_message "Failed building $1" + make install || exit_with_message "Failed installing $1" +} + +function build_yaml() +{ + productName=yaml + + cd_to_new_dir ${output_dir}/${productName} + execute tar -xzf ${sierra_proj}/TPLs_src/spack/spack_tpls/yaml-cpp/*.tar.gz + cd_to_new_dir ${productName}_build + + export CC=gcc + export CXX=g++ + execute cmake -DCMAKE_BUILD_TYPE=${build_type^^} -DYAML_CPP_BUILD_TESTS=false -DCMAKE_INSTALL_PREFIX=../${productName}_install ../yaml-cpp + make_and_install $productName + unset CC + unset CXX +} + +function build_trilinos_with_krino() +{ + productName=trilinos + + trilinos_dir=${output_dir}/${productName} + if [ ! -d ${trilinos_dir} ] ; then + execute mkdir ${trilinos_dir} + fi + execute cd ${trilinos_dir} + + if [ ! -d Trilinos ] ; then + execute git clone -b develop https://github.com/trilinos/Trilinos.git Trilinos + #else + #execute cd Trilinos + #execute git checkout develop + #execute git reset --hard origin/develop + #execute git pull + #execute cd .. + fi + + if [ -d Trilinos/packages/krino ] ; then + execute rm -rf Trilinos/packages/krino; + fi + if [ ! -L Trilinos/packages/krino ] ; then + execute ln -s ${sierra_proj}/krino Trilinos/packages + fi + + rm -rf ${productName}_install + cd_to_new_dir ${productName}_build + + export TRILINOS_INSTALL_DIR=../${productName}_install + $sierra_proj/krino/cmake_install_test/run_cmake_krino + make_and_install $productName +} + +function runTests() +{ + cd $1 + ctest -j 16 || exit_with_message "$2 tests failed" + cd ../.. +} + +sierra_proj=${SIERRA_PROJ:-${PWD}} +output_dir=${OUTPUT_DIR:-${PWD}/../krino-cmake-testing} + +cuda_on_or_off=${CUDA:-OFF} +build_type=${CMAKE_BUILD_TYPE:-release} +date_suffix=`date +%F_%H-%M-%S` + +source $sierra_proj/krino/cmake_install_test/load_gcc_modules + +if [ ! -d ${output_dir} ] ; then + execute mkdir ${output_dir} +fi + +build_yaml +build_trilinos_with_krino + +#runTests morph/morph_build "Morph" +#runTests morph_and_sgm/morph_and_sgm_build "Morph and SGM" +#runTests morphWithExe/morphWithExe_build "MorphWithExe" + diff --git a/packages/krino/cmake_install_test/load_gcc_modules b/packages/krino/cmake_install_test/load_gcc_modules new file mode 100644 index 000000000000..7b57261e312e --- /dev/null +++ b/packages/krino/cmake_install_test/load_gcc_modules @@ -0,0 +1,13 @@ +#!/bin/bash + +module load cde/v2/cmake/3.19.2 +module load cde/v2/compiler/gcc/7.2.0 +module load cde/v2/gcc/7.2.0/openmpi/4.0.5 +module load cde/v2/gcc/7.2.0/netlib-lapack/3.8.0 +module load cde/v2/gcc/7.2.0/boost/1.73.0 +module load cde/v2/gcc/7.2.0/hdf5/1.10.6 +module load cde/v2/gcc/7.2.0/netcdf-c/4.7.3 +module load cde/v2/gcc/7.2.0/parallel-netcdf/1.12.1 +module load cde/v2/gcc/7.2.0/metis/5.1.0 +module load cde/v2/gcc/7.2.0/parmetis/4.0.3 + diff --git a/packages/krino/cmake_install_test/run_cmake_krino b/packages/krino/cmake_install_test/run_cmake_krino new file mode 100755 index 000000000000..9f052313e1f0 --- /dev/null +++ b/packages/krino/cmake_install_test/run_cmake_krino @@ -0,0 +1,52 @@ +#!/bin/bash + +trilinos_src_dir=${TRILINOS_DIR:-${PWD}/../Trilinos} +build_dir=${BUILD_DIR:-${PWD}} +build_type=${CMAKE_BUILD_TYPE:-release} +trilinos_install_dir=${TRILINOS_INSTALL_DIR:-${PWD}/../trilinos_install_dir} + +yaml_install_dir=${YAML_INSTALL_DIR:-${build_dir}/../../yaml/yaml_install} + +printf "\nTRILINOS_DIR=${trilinos_src_dir}\n"; +printf "BUILD_DIR=${build_dir}\n"; +printf "CMAKE_BUILD_TYPE=${build_type}\n"; +printf "TRILINOS_INSTALL_DIR=${trilinos_install_dir}\n"; +printf "YAML_INSTALL_DIR=${yaml_install_dir}\n"; +printf "\nTo change these vars, set as env vars or pass to this script like 'VAR=value run_cmake_stk'\n\n"; + +if [ ! -d ${trilinos_src_dir}/packages/seacas ] && [ ! -L ${trilinos_src_dir}/packages/seacas ] ; then + echo "Trilinos dir (${trilinos_src_dir}) doesn't have packages/seacas directory. If using a Sierra project, make a soft-link to Sierra's seacas directory."; + exit 1; +fi +if [ ! -d ${trilinos_src_dir}/packages/stk ] && [ ! -L ${trilinos_src_dir}/packages/stk ]; then + echo "Trilinos dir (${trilinos_src_dir}) doesn't have packages/stk directory. If using a Sierra project, make a soft-link to Sierra's stk directory."; + exit 1; +fi +if [ ! -d ${trilinos_src_dir}/packages/krino ] && [ ! -L ${trilinos_src_dir}/packages/krino ]; then + echo "Trilinos dir (${trilinos_src_dir}) doesn't have packages/krino directory. If using a Sierra project, make a soft-link to Sierra's krino directory."; + exit 1; +fi + +mkdir -p $trilinos_install_dir +mkdir -p $build_dir + +cd ${build_dir} + +# Cleanup old cache before we configure +rm -rf CMakeFiles CMakeCache.txt + +cmake \ +-DCMAKE_INSTALL_PREFIX=$trilinos_install_dir \ +-DCMAKE_BUILD_TYPE=${build_type^^} \ +-DKrino_ENABLE_TESTS:BOOL=ON \ +-DTrilinos_ENABLE_Krino:BOOL=ON \ +-DTrilinos_ENABLE_Fortran:BOOL=ON \ +-DTPL_ENABLE_yaml-cpp=ON \ +-Dyaml-cpp_INCLUDE_DIRS=${yaml_install_dir}/include \ +-Dyaml-cpp_LIBRARY_DIRS=${yaml_install_dir}/lib \ +-DTPL_ENABLE_Boost:BOOL=ON \ +-DTPL_ENABLE_MPI=ON \ +-DTPL_ENABLE_Pnetcdf:BOOL=ON \ +-DTPL_ENABLE_HDF5:BOOL=ON \ +${trilinos_src_dir}/ + diff --git a/packages/krino/delete_small_elements/Akri_DeleteSmallElementsMain.cpp b/packages/krino/delete_small_elements/Akri_DeleteSmallElementsMain.cpp new file mode 100644 index 000000000000..1a5fd03eaabc --- /dev/null +++ b/packages/krino/delete_small_elements/Akri_DeleteSmallElementsMain.cpp @@ -0,0 +1,187 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino { + +struct DeleteSmallElementsInputData +{ + static constexpr const char * mDefaultString{"None"}; + std::string meshIn{mDefaultString}; + std::string meshOut{mDefaultString}; + bool blockNameSpecified{false}; + std::string blockName{mDefaultString}; + double minNodalVolume{0.0}; + bool minNodalVolumeSpecified{false}; + double minRelativeNodalVolume{-1.0}; + bool minRelativeNodalVolumeSpecified{false}; +}; + +bool read_command_line( int argc, char *argv[], DeleteSmallElementsInputData & inputData, stk::ParallelMachine comm) +{ + DeleteSmallElementsInputData defaultValues; + + const stk::CommandLineOption inmeshOption{"inmesh", "i", "Filename of input genesis mesh."}; + const stk::CommandLineOption outmeshOption{"outmesh", "o", "Filename of output genesis mesh to write."}; + + const stk::CommandLineOption minNodalVolumeOption{"min_nodal_volume", "m", "Remove all nodes and attached entities smaller than this size. Either --min_nodal_volume or --min_relative_nodal_volume can be specified, but not both."}; + const stk::CommandLineOption minRelativeNodalVolumeOption{"min_relative_nodal_volume", "r", "Remove all all nodes and attached entities smaller than this factor times the maximum element volume in the input mesh. Either --min_nodal_volume or --min_relative_nodal_volume can be specified, but not both"}; + const stk::CommandLineOption blockNameOption{"block", "b", "Confine consideration to the specified block."}; + + stk::CommandLineParserParallel commandLine(comm); + commandLine.disallow_unrecognized(); + + commandLine.add_required(inmeshOption); + commandLine.add_required(outmeshOption); + commandLine.add_optional(blockNameOption, inputData.blockName); + commandLine.add_optional(minNodalVolumeOption, inputData.minNodalVolume); + commandLine.add_optional(minRelativeNodalVolumeOption, inputData.minRelativeNodalVolume); + + + stk::CommandLineParser::ParseState state = commandLine.parse(argc, const_cast(argv)); + if(state == stk::CommandLineParser::ParseComplete) + { + inputData.meshIn = commandLine.get_option_value(inmeshOption.name); + inputData.meshOut = commandLine.get_option_value(outmeshOption.name); + + inputData.blockNameSpecified = commandLine.is_option_parsed(blockNameOption.name); + inputData.minNodalVolumeSpecified = commandLine.is_option_parsed(minNodalVolumeOption.name); + inputData.minRelativeNodalVolumeSpecified = commandLine.is_option_parsed(minRelativeNodalVolumeOption.name); + + if(inputData.blockNameSpecified) + { + inputData.blockName = commandLine.get_option_value(blockNameOption.name); + } + + if(inputData.minNodalVolumeSpecified && inputData.minRelativeNodalVolumeSpecified) + { + sierra::Env::outputP0() << "ERROR: You cannot specify both --"+minNodalVolumeOption.name+" and --"+minRelativeNodalVolumeOption.name+"." << std::endl; + state = stk::CommandLineParser::ParseError; + } + + if(inputData.minNodalVolumeSpecified) + { + inputData.minNodalVolume = commandLine.get_option_value(minNodalVolumeOption.name); + if (inputData.minNodalVolume < 0.0) + { + sierra::Env::outputP0() << "ERROR: size specified via --"+minNodalVolumeOption.name+" must be >= 0." << std::endl; + state = stk::CommandLineParser::ParseError; + } + } + + if(inputData.minRelativeNodalVolumeSpecified) + { + inputData.minRelativeNodalVolume = commandLine.get_option_value(minRelativeNodalVolumeOption.name); + if (inputData.minRelativeNodalVolume < 0.0) + { + sierra::Env::outputP0() << "ERROR: edge length ratio specified via --"+minRelativeNodalVolumeOption.name+" must be >= 0." << std::endl; + state = stk::CommandLineParser::ParseError; + } + } + } + + if(state != stk::CommandLineParser::ParseComplete) + sierra::Env::outputP0() << commandLine.get_usage() << std::endl; + + return state == stk::CommandLineParser::ParseComplete; +} + +static bool delete_small_elements(const DeleteSmallElementsInputData& inputData, + const stk::ParallelMachine comm) +{ + stk::mesh::MetaData meta; + stk::mesh::BulkData bulk(meta, comm); + + stk::io::fill_mesh_with_auto_decomp(inputData.meshIn, bulk); + + // delete infinitesimal elements + double minEdgeLength, maxEdgeLength, minElementVolume, maxElementVolume; + compute_element_quality(bulk, minEdgeLength, maxEdgeLength, minElementVolume, maxElementVolume); + sierra::Env::outputP0() << "Overall mesh size results: minEdgeLength=" << minEdgeLength << ", maxEdgeLength=" << maxEdgeLength << ", minElementVolume=" << minElementVolume << ", maxElementVolume=" << maxElementVolume << std::endl; + + stk::mesh::Selector blockSelector = meta.universal_part(); + if (inputData.blockNameSpecified) + { + stk::mesh::Part * block = meta.get_part(inputData.blockName); + if (!block) + { + sierra::Env::outputP0() << "Did not find part with name " << inputData.blockName << "." << std::endl; + return false; + } + blockSelector = *block; + } + + const double minRetainedElementVolume = inputData.minNodalVolumeSpecified ? inputData.minNodalVolume : (inputData.minRelativeNodalVolume*maxElementVolume); + if (minElementVolume < minRetainedElementVolume) + delete_all_entities_using_nodes_with_nodal_volume_below_threshold(bulk, blockSelector, minRetainedElementVolume); + else + sierra::Env::outputP0() << "All nodes already have nodal volume larger than " << minRetainedElementVolume << "." << std::endl; + + stk::io::StkMeshIoBroker io_broker; + io_broker.set_bulk_data(bulk); + size_t resultFileIndex = io_broker.create_output_mesh(inputData.meshOut, stk::io::WRITE_RESULTS); + io_broker.write_output_mesh(resultFileIndex); + + return true; +} + +static bool run_delete_small_elements(int argc, char **argv, stk::ParallelMachine comm) +{ + int proc = stk::parallel_machine_rank(comm); + if(proc != 0) + stk::EnvData::instance().m_outputP0 = &stk::EnvData::instance().m_outputNull; + + DeleteSmallElementsInputData inputData; + bool didParseOk = read_command_line(argc, argv, inputData, comm); + + bool successfullyRun{false}; + + if (didParseOk) + { + successfullyRun = delete_small_elements(inputData, comm); + + stk::parallel_machine_barrier(comm); + } + + return successfullyRun; +} + +} + +int main(int argc, char **argv) +{ + stk::ParallelMachine comm{stk::parallel_machine_init(&argc, &argv)}; + + bool successfullyRun{false}; + + try + { + successfullyRun = krino::run_delete_small_elements(argc, argv, comm); + } + catch(std::exception &e) + { + std::cerr << "Proc " << stk::parallel_machine_rank(comm) << ": " << e.what() << std::endl; + const int errorCode{-1}; + MPI_Abort(comm, errorCode); + } + + stk::parallel_machine_finalize(); + + return successfullyRun ? 0 : 1; +} diff --git a/packages/krino/delete_small_elements/CMakeLists.txt b/packages/krino/delete_small_elements/CMakeLists.txt new file mode 100644 index 000000000000..864b989c454e --- /dev/null +++ b/packages/krino/delete_small_elements/CMakeLists.txt @@ -0,0 +1,12 @@ +INCLUDE_DIRECTORIES(${${PACKAGE_NAME}_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +SET(SOURCES_MAIN Akri_DeleteSmallElementsMain.cpp) + +set(EXE_NAME delete_small_elements) +TRIBITS_ADD_EXECUTABLE( + ${EXE_NAME} + SOURCES ${SOURCES_MAIN} + NOEXEPREFIX INSTALLABLE + ) diff --git a/packages/krino/krino/Apps_krino.cpp b/packages/krino/krino/Apps_krino.cpp new file mode 100644 index 000000000000..dd24cd296feb --- /dev/null +++ b/packages/krino/krino/Apps_krino.cpp @@ -0,0 +1,47 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include + +int main( int argc, char ** argv ) +{ + krino::Startup startup(argc, argv); + + if (startup.exit_early()) { + return 0; + } + + bool is_parsing = true; + + try { + krino::YAML_Parser::parse(); + + is_parsing = false; + krino::Simulation & simulation = krino::Simulation::get(); + simulation.commit(); + simulation.initialize(); + simulation.execute(); + } + catch (std::exception &x) { + stk::diag::Trace::Preserve preserve__; + startup.handle_exception(x.what(),is_parsing); + } + catch (...) { + stk::diag::Trace::Preserve preserve__; + startup.handle_exception("Unknown",is_parsing); + } + + // call Simulation::reset() manually to make sure the associated static objects are destroyed before MPI_FINALIZE is called by ~Startup + krino::Simulation::reset(); + + // all done + return 0; +} diff --git a/packages/krino/krino/CMakeLists.txt b/packages/krino/krino/CMakeLists.txt new file mode 100644 index 000000000000..4e8c7ece3d53 --- /dev/null +++ b/packages/krino/krino/CMakeLists.txt @@ -0,0 +1,17 @@ +add_subdirectory(interface_geometry_interface) +add_subdirectory(master_element) +add_subdirectory(krino_lib) +add_subdirectory(adaptivity_interface) +add_subdirectory(region) +add_subdirectory(rebalance_utils) +add_subdirectory(parser) +add_subdirectory(unit_tests) + +SET(SOURCES_MAIN Apps_krino.cpp) + +set(EXE_NAME krino) +TRIBITS_ADD_EXECUTABLE( + ${EXE_NAME} + SOURCES ${SOURCES_MAIN} + NOEXEPREFIX NOEXESUFFIX INSTALLABLE + ) diff --git a/packages/krino/krino/adaptivity_interface/Akri_AdaptivityInterface.cpp b/packages/krino/krino/adaptivity_interface/Akri_AdaptivityInterface.cpp new file mode 100644 index 000000000000..47ea5ae3d3ed --- /dev/null +++ b/packages/krino/krino/adaptivity_interface/Akri_AdaptivityInterface.cpp @@ -0,0 +1,679 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include + +#ifdef __INTEL_COMPILER +#include // for ElementRefinePredicate +#include +#include +#include // for UniformRefiner +#include +#include +#include +#include +#include +#else +// this disables the checking of shadowed variables on GCC only +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#include // for ElementRefinePredicate +#include +#include +#include // for UniformRefiner +#include +#include +#include +#include +#include +#pragma GCC diagnostic pop +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino +{ + +namespace +{ + +class HAdaptImpl +{ +public: + HAdaptImpl(stk::mesh::MetaData & meta) + : my_meta(meta), + my_active_part(nullptr), + my_root_timer(nullptr) + { + } + void setup(stk::mesh::Part & active_part, stk::diag::Timer & root_timer); + void do_adaptive_refinement(const std::string & marker_field_name); + void do_initial_uniform_refinement(const int num_levels); + +private: + void get_counts(const stk::mesh::FieldBase & marker_field, + unsigned & refinement_count, + unsigned & unrefinement_count) const; + void check_supported_element_types() const; + + stk::mesh::MetaData & my_meta; + std::unique_ptr my_pMesh; + std::unique_ptr my_adaptive_refinement_pattern; + std::unique_ptr my_element_refine_predicate; + std::unique_ptr> my_breaker; + stk::mesh::Selector my_selector; + stk::mesh::Part * my_active_part; + stk::diag::Timer * my_root_timer; +}; + +HAdaptImpl & get(stk::mesh::MetaData & meta) +{ + HAdaptImpl * hadapt = const_cast(meta.get_attribute()); + if (nullptr == hadapt) + { + hadapt = new HAdaptImpl(meta); + meta.declare_attribute_with_delete(hadapt); + } + return *hadapt; +} + +void HAdaptImpl::check_supported_element_types() const +{ + const stk::mesh::PartVector & all_parts = my_meta.get_parts(); + for (size_t i = 0; i < all_parts.size(); ++i) + { + const stk::mesh::Part & part = *all_parts[i]; + if (stk::io::is_part_io_part(part) && part.primary_entity_rank() == stk::topology::ELEMENT_RANK) + { + if ((2 == my_meta.spatial_dimension() && + part.topology() == stk::topology::TRIANGLE_3_2D) || + (3 == my_meta.spatial_dimension() && + part.topology() == stk::topology::TETRAHEDRON_4) || + part.topology() == stk::topology::PARTICLE) + { + continue; + } + ThrowErrorMsg("Elements in block " + << part.name() << " have topology " << part.topology().name() + << " which is currently not supported for adaptive refinement."); + } + } +} + +void HAdaptImpl::get_counts(const stk::mesh::FieldBase & marker_field, + unsigned & refinement_count, + unsigned & unrefinement_count) const +{ + stk::mesh::BulkData & mesh = marker_field.get_mesh(); + stk::mesh::Part & active_part = *my_active_part; + stk::mesh::Selector field_selector = stk::mesh::selectField(marker_field) & active_part & mesh.mesh_meta_data().locally_owned_part(); + + unsigned local_refinement_count = 0; + unsigned local_unrefinement_count = 0; + + const stk::mesh::BucketVector & buckets = + mesh.get_buckets(stk::topology::ELEMENT_RANK, field_selector); + + for (stk::mesh::BucketVector::const_iterator ib = buckets.begin(); ib != buckets.end(); ++ib) + { + const stk::mesh::Bucket & b = **ib; + const int length = b.size(); + + for (int i = 0; i < length; ++i) + { + stk::mesh::Entity elem = b[i]; + + const int & marker = *((int *)stk::mesh::field_data(marker_field, elem)); + if (marker < 0) + { + ++local_unrefinement_count; + } + else if (marker > 0) + { + ++local_refinement_count; + } + } + } + refinement_count = 0; + unrefinement_count = 0; + stk::all_reduce_sum( + mesh.parallel(), (int *)&local_refinement_count, (int *)&refinement_count, 1); + stk::all_reduce_sum(mesh.parallel(), + (int *)&local_unrefinement_count, + (int *)&unrefinement_count, + 1); +} + +void HAdaptImpl::setup(stk::mesh::Part & active_part, stk::diag::Timer & root_timer) +{ + /* %TRACE[ON]% */ Trace trace__( + "void HAdapt::setup(stk::mesh::Part & active_part)"); /* %TRACE% */ + + my_active_part = &active_part; + my_root_timer = &root_timer; + + static stk::diag::Timer timerAdapt_("Adapt", *my_root_timer); + static stk::diag::Timer timerSetup_("Setup", timerAdapt_); + stk::diag::TimeBlock tbTimerSetup_(timerSetup_); + + const unsigned nDim = my_meta.spatial_dimension(); + + my_pMesh = std::make_unique(&my_meta, nullptr, false); + + typedef stk::mesh::Field PerceptVector; + const stk::mesh::FieldBase & coordinates_base = *my_meta.coordinate_field(); + PerceptVector & typed_coordinates = + reinterpret_cast(const_cast(coordinates_base)); + my_pMesh->setCoordinatesField(&typed_coordinates); + + my_pMesh->register_and_set_refine_fields(); + my_pMesh->add_field_int("refine_field", stk::topology::NODE_RANK, 0); + + if (2 == nDim) + { + my_adaptive_refinement_pattern = std::make_unique(*my_pMesh); + } + else + { + my_adaptive_refinement_pattern = std::make_unique(*my_pMesh); + } + + my_pMesh->output_active_children_only(true); + my_pMesh->setProperty("Refiner_skip_side_part_fixes", "true"); +} + +namespace +{ +void limit_coarsening_to_child_elements_with_all_siblings_marked_for_coarsening_and_no_children( + percept::PerceptMesh & eMesh, + const stk::mesh::FieldBase & marker_field) +{ + const stk::mesh::BulkData & mesh = *eMesh.get_bulk_data(); + const auto & local_buckets = + mesh.get_buckets(stk::topology::ELEMENT_RANK, mesh.mesh_meta_data().locally_owned_part()); + + std::vector children; + std::vector childrenOfChild; + + // Only mark element for COARSEN if all children are marked COARSEN + for (auto && b_ptr : local_buckets) + { + for (auto && elem : *b_ptr) + { + eMesh.getChildren(elem, children, false, false); + if (!children.empty()) + { + bool all_children_marked_COARSEN = true; + for (auto && child : children) + { + int & marker = (*((int *)stk::mesh::field_data( marker_field, child ))); + + if (marker == -1) + eMesh.getChildren(child, childrenOfChild, false, false); + if (marker != -1 || !childrenOfChild.empty()) + { + all_children_marked_COARSEN = false; + break; + } + } + if (!all_children_marked_COARSEN) + { + for (auto && child : children) + { + int & marker = (*((int *)stk::mesh::field_data( marker_field, child ))); + if (marker == -1) + { + marker = 0; + } + } + } + } + } + } +} + + +void fill_percept_refine_field(percept::PerceptMesh & eMesh, + const stk::mesh::FieldBase & marker_field) +{ + limit_coarsening_to_child_elements_with_all_siblings_marked_for_coarsening_and_no_children(eMesh, marker_field); + + stk::mesh::BulkData & mesh = *eMesh.get_bulk_data(); + stk::mesh::communicate_field_data(mesh, {&marker_field}); + + stk::mesh::Part &parent_part = *mesh.mesh_meta_data().get_part("refine_inactive_elements_part_3"); + stk::mesh::FieldBase & refine_field = *eMesh.m_refine_field; + + for (auto && b_ptr : mesh.get_buckets(stk::topology::ELEMENT_RANK, stk::mesh::selectField(marker_field))) + { + const stk::mesh::Bucket & b = *b_ptr; + const bool is_parent = b.member(parent_part); + const int length = b.size(); + const unsigned marker_field_length = stk::mesh::field_scalars_per_entity(marker_field, b); + ThrowRequire(1 == stk::mesh::field_scalars_per_entity(refine_field, b)); + + int * marker = (int *)stk::mesh::field_data(marker_field, b); + int * refine = (int *)stk::mesh::field_data(refine_field, b); + if (is_parent) + { + for (int i = 0; i < length; ++i) + { + marker[i*marker_field_length] = 0; + } + } + + for (int i = 0; i < length; ++i) + { + refine[i] = marker[i*marker_field_length]; + } + } +} + +void delete_partless_faces_and_edges(stk::mesh::BulkData & mesh) +{ + std::vector entities; + std::vector relatives; + std::vector relative_ordinals; + + mesh.modification_begin(); + + for (stk::mesh::EntityRank entity_rank : {stk::topology::FACE_RANK, stk::topology::EDGE_RANK}) + { + stk::mesh::get_entities(mesh, entity_rank, entities); + + for (auto && entity : entities) + { + stk::mesh::Bucket & bucket = mesh.bucket(entity); + + const stk::mesh::PartVector & bucket_parts = bucket.supersets(); + bool bucket_has_same_rank_part = + std::find_if(bucket_parts.begin(), + bucket_parts.end(), + [&](const stk::mesh::Part * bucket_part) + { + return (bucket_part->primary_entity_rank() == bucket.entity_rank() && + !stk::mesh::is_auto_declared_part(*bucket_part)); + }) != bucket_parts.end(); + + if (!bucket_has_same_rank_part && + mesh.num_connectivity(entity, stk::topology::CONSTRAINT_RANK) == 0) + { + for (stk::mesh::EntityRank irank = stk::topology::ELEMENT_RANK; irank != entity_rank; + --irank) + { + // Previously this attempted to delete forward or backward and still the list got + // corrupted, + // so just copy into vector and delete from there. + relatives.assign(mesh.begin(entity, irank), mesh.end(entity, irank)); + relative_ordinals.assign( + mesh.begin_ordinals(entity, irank), mesh.end_ordinals(entity, irank)); + + for (size_t irel = 0; irel < relatives.size(); ++irel) + { + ThrowRequireMsg(mesh.destroy_relation(relatives[irel], entity, relative_ordinals[irel]), + "Could not destroy relation between " << mesh.entity_key(relatives[irel]) << " and " + << mesh.entity_key(entity)); + } + } + ThrowRequireMsg( + mesh.destroy_entity(entity), "Could not destroy entity " << mesh.entity_key(entity)); + } + } + } + + mesh.modification_end(); +} + +void update_active_inactive_entities(stk::mesh::BulkData & mesh, + stk::mesh::Part & active_part) +{ + const auto & meta = mesh.mesh_meta_data(); + stk::mesh::PartVector active_part_vec(1, &active_part); + stk::mesh::PartVector inactive_part_vec; + stk::mesh::Selector select_locally_owned(meta.locally_owned_part()); + stk::mesh::Selector percept_active_selector = get_refinement_active_part(meta, stk::topology::ELEMENT_RANK); + + mesh.modification_begin(); + std::vector entities; + for (stk::mesh::EntityRank entity_rank = stk::topology::NODE_RANK; + entity_rank <= stk::topology::ELEMENT_RANK; + ++entity_rank) + { + stk::mesh::get_selected_entities(select_locally_owned, mesh.buckets(entity_rank), entities); + for (auto && entity : entities) + { + if (percept_active_selector(mesh.bucket(entity))) + mesh.change_entity_parts(entity, active_part_vec, inactive_part_vec); + else + mesh.change_entity_parts(entity, inactive_part_vec, active_part_vec); + } + } + mesh.modification_end(); +} + +void +fixup_side_permutation(stk::mesh::BulkData & mesh) +{ + mesh.modification_begin(); + stk::mesh::MetaData & meta = mesh.mesh_meta_data(); + + const stk::mesh::BucketVector & buckets = mesh.buckets(stk::topology::ELEMENT_RANK); + + for (auto&& bucket_ptr : buckets) + { + for (auto&& elem : *bucket_ptr) + { + const stk::mesh::Entity* elem_sides = mesh.begin(elem, meta.side_rank()); + const unsigned num_elem_sides = mesh.num_connectivity(elem, meta.side_rank()); + + for (size_t it_side = 0; it_side < num_elem_sides; ++it_side) + { + auto relationship = determine_ordinal_and_permutation(mesh, elem, elem_sides[it_side]); + set_relation_permutation(mesh, elem, elem_sides[it_side], relationship.first, relationship.second); + } + } + } + mesh.modification_end(); +} + +} + +void HAdaptImpl::do_adaptive_refinement(const std::string & marker_field_name) +{ + /* %TRACE[ON]% */ Trace trace__( + "void HAdapt::do_adaptive_refinement(const std::string &marker_field)"); /* %TRACE% */ + + ThrowAssertMsg(my_root_timer != nullptr, "HAdapt::setup() not called."); + static stk::diag::Timer timerAdapt_("Adapt", *my_root_timer); + static stk::diag::Timer timerSetup_("Setup", timerAdapt_); + static stk::diag::Timer timerFmwkUpdating_("Update Active Part", timerAdapt_); + static stk::diag::Timer timerRefine_("Refine", timerAdapt_); + static stk::diag::Timer timerUnrefine_("Unrefine", timerAdapt_); + + stk::diag::TimeBlock tbTimerAdapt_(timerAdapt_); + + constexpr bool debug = false; + + check_supported_element_types(); + + stk::mesh::BulkData & bulk = my_meta.mesh_bulk_data(); + if (!my_pMesh->get_bulk_data()) + { + my_pMesh->set_bulk_data(&bulk); + } + + const stk::mesh::FieldBase * element_marker_field = + my_meta.get_field(stk::topology::ELEMENT_RANK, marker_field_name); + ThrowRequire(element_marker_field); + my_selector = stk::mesh::selectField(*element_marker_field); + + if (!my_breaker) + { + const double tolerance = 0.0; // not used + stk::mesh::FieldBase * refine_field = my_pMesh->m_refine_field; + my_element_refine_predicate = std::make_unique( + *my_pMesh, &my_selector, refine_field, tolerance); // Note that this stores pointer to selector so temporary is not ok + my_breaker = std::make_unique>( + *my_element_refine_predicate, *my_pMesh, *my_adaptive_refinement_pattern, nullptr, debug); + my_breaker->setRemoveOldElements(false); + my_breaker->setAlwaysInitializeNodeRegistry(false); + } + + my_breaker->initializeRefine(); + + unsigned refinement_count = 0; + unsigned unrefinement_count = 0; + + fill_percept_refine_field(*my_pMesh, *element_marker_field); + + get_counts(*element_marker_field, refinement_count, unrefinement_count); + krinolog << "Number of elements marked for refinement = " << refinement_count << "\n"; + krinolog << "Number of elements marked for unrefinement = " << unrefinement_count << stk::diag::dendl; + + const unsigned total_marked_count = refinement_count + unrefinement_count; + if (0 == total_marked_count) + { + krinolog << "Adapt: No elements marked for refinement or unrefinement. " + "Skipping adaptivity" << stk::diag::dendl; + return; + } + + delete_partless_faces_and_edges(bulk); + + { + stk::diag::TimeBlock tbTimerRefine_(timerRefine_); + my_breaker->setAlternateRootTimer(&timerRefine_); + my_breaker->setModBegEndRootTimer(&timerAdapt_); + + std::vector counts; + stk::mesh::comm_mesh_counts(bulk, counts); + + krinolog << "Adapt: before refine, mesh has " << counts[0] << " nodes, " << counts[1] + << " edges, " << counts[2] << " faces, " << counts[3] << " elements" << stk::diag::dendl; + + my_breaker->refine(); + + stk::mesh::comm_mesh_counts(bulk, counts); + + krinolog << "Adapt: after refine, mesh has " << counts[0] << " nodes, " << counts[1] + << " edges, " << counts[2] << " faces, " << counts[3] << " elements" << stk::diag::dendl; + + my_breaker->setAlternateRootTimer(0); + my_breaker->setModBegEndRootTimer(0); + } + + // update refine field to include newly created children + fill_percept_refine_field(*my_pMesh, *element_marker_field); + + { + stk::diag::TimeBlock tbTimerUnrefine_(timerUnrefine_); + my_breaker->setAlternateRootTimer(&timerUnrefine_); + my_breaker->setModBegEndRootTimer(&timerAdapt_); + my_breaker->unrefine(); + my_breaker->setAlternateRootTimer(0); + my_breaker->setModBegEndRootTimer(0); + } + + { + stk::diag::TimeBlock tbTimerRebuilding_(timerFmwkUpdating_); + // The Encore-Percept interface also called induce_nodal_unranked_superset_parts + // and topology nodeset inducer but I don't believe those are needed here. + ThrowRequireMsg(my_active_part != nullptr, "Active part not set for krino::HAdapt"); + update_active_inactive_entities(bulk, *my_active_part); + fixup_side_permutation(bulk); + } +} + +void HAdaptImpl::do_initial_uniform_refinement(const int num_levels) +{ + /* %TRACE[ON]% */ Trace trace__( + "void HAdapt::do_uniform_refinement()"); /* %TRACE% */ + + ThrowAssertMsg(my_root_timer != nullptr, "HAdapt::setup() not called."); + static stk::diag::Timer timerAdapt_("Adapt", *my_root_timer); + static stk::diag::Timer timerSetup_("Setup", timerAdapt_); + static stk::diag::Timer timerFmwkUpdating_("Update Active Part", timerAdapt_); + static stk::diag::Timer timerRefine_("Refine", timerAdapt_); + + stk::diag::TimeBlock tbTimerAdapt_(timerAdapt_); + + check_supported_element_types(); + + stk::mesh::BulkData & bulk = my_meta.mesh_bulk_data(); + if (!my_pMesh->get_bulk_data()) + { + my_pMesh->set_bulk_data(&bulk); + } + + percept::UniformRefiner urefine(*my_pMesh, *my_adaptive_refinement_pattern, nullptr); + urefine.setAlternateRootTimer(&timerAdapt_); + + { + stk::diag::TimeBlock tbTimerRefine_(timerRefine_); + + std::vector counts; + stk::mesh::comm_mesh_counts(bulk, counts); + + krinolog << "Adapt: before refine, mesh has " << counts[0] << " nodes, " << counts[1] + << " edges, " << counts[2] << " faces, " << counts[3] << " elements" << stk::diag::dendl; + + for (int level=0; level counts; + stk::mesh::comm_mesh_counts(bulk, counts); + num_initial_elements = counts[3]; + } + + const auto & meta = bulk.mesh_meta_data(); + percept::MarkerInfo markerInfo; + markerInfo.errorIndicator_ = meta.get_field(stk::topology::ELEMENT_RANK, indicator_field_name); + markerInfo.refineField_ = meta.get_field(stk::topology::ELEMENT_RANK, marker_field_name); + markerInfo.refineFieldOrig_ = meta.get_field(stk::topology::ELEMENT_RANK, "refine_field_orig"); + markerInfo.refineLevelField_ = meta.get_field(stk::topology::ELEMENT_RANK, "refine_level"); + /* + markerInfo.useMarker_ = true; + + markerInfo.numInitialElements_ = num_initial_elements; + markerInfo.maxRefinementLevel_ = max_refinement_level; + markerInfo.maxRefinementNumberOfElementsFraction_ = max_element_growth_factor; + markerInfo.debug_ = false; + + markerInfo.refineFraction_ = refine_fraction; + markerInfo.unrefineFraction_ = unrefine_fraction; + + percept::MarkerUsingErrIndFraction marker(const_cast(bulk), markerInfo); + marker.mark();*/ + + const auto & percept_parent_part = get_refinement_inactive_part(meta, stk::topology::ELEMENT_RANK); + const auto & active_buckets = + bulk.get_buckets(stk::topology::ELEMENT_RANK, + stk::mesh::selectField(*markerInfo.errorIndicator_) & + !percept_parent_part & + meta.locally_owned_part()); + const int buckets_to_sample = std::min(1000ul, active_buckets.size()); + std::vector sample_values; + sample_values.reserve(512*buckets_to_sample); + int j=0; + for(int i=0; i < buckets_to_sample; ++i) + { + const double * ind_data = field_data(*markerInfo.errorIndicator_, *active_buckets[i]); + const int size = active_buckets[i]->size(); + j += size; + sample_values.insert(sample_values.end(), ind_data, ind_data + size); + } + + // This is not scalable + std::vector global_vals; + stk::parallel_vector_concat(bulk.parallel(), sample_values, global_vals); + std::sort(global_vals.begin(), global_vals.end()); + + const auto current_active_elements = global_vals.size(); + + const double remaining_levels = max_refinement_level - current_refinement_level; + const double fraction_to_refine = + current_active_elements >= target_elem_count ? 0: + 1./12. * ( + std::pow( + static_cast(target_elem_count) / static_cast(current_active_elements), + 1./remaining_levels) + - 1.); + + const int global_fraction_index = (1.-fraction_to_refine) * current_active_elements; + const double threshold_val = global_vals[global_fraction_index]; + + for(auto && b_ptr : active_buckets) + { + const auto & bucket = *b_ptr; + const double * ind_data = field_data(*markerInfo.errorIndicator_, bucket); + int * marker_data = field_data(*markerInfo.refineField_, bucket); + const int size = bucket.size(); + for(int i=0; i < size; ++i) + { + marker_data[i] = ind_data[i] > threshold_val ? 1 : 0; + } + } +} + +} +} // namespace krino diff --git a/packages/krino/krino/adaptivity_interface/Akri_AdaptivityInterface.hpp b/packages/krino/krino/adaptivity_interface/Akri_AdaptivityInterface.hpp new file mode 100644 index 000000000000..84c5b21c340d --- /dev/null +++ b/packages/krino/krino/adaptivity_interface/Akri_AdaptivityInterface.hpp @@ -0,0 +1,36 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_ADAPTIVITYINTERFACE_H_ +#define KRINO_INCLUDE_AKRI_ADAPTIVITYINTERFACE_H_ + +#include + +namespace stk { namespace mesh { class BulkData; } } +namespace stk { namespace mesh { class MetaData; } } +namespace stk { namespace mesh { class Part; } } +namespace stk { namespace diag { class Timer; } } + +namespace krino { + +namespace HAdapt +{ + void setup(stk::mesh::MetaData & meta, stk::mesh::Part & active_part, stk::diag::Timer & root_timer); + void do_adaptive_refinement(stk::mesh::MetaData & meta, const std::string & marker_field_name); + void do_uniform_refinement(stk::mesh::MetaData & meta, const int num_levels); + void mark_based_on_indicator_field(const stk::mesh::BulkData & bulk, + const std::string & marker_field_name, + const std::string & indicator_field_name, + const int max_refinement_level, + const int current_refinement_level, + const uint64_t target_elem_count); +} + +} // namespace krino + +#endif /* KRINO_INCLUDE_AKRI_ADAPTIVITYINTERFACE_H_ */ diff --git a/packages/krino/krino/adaptivity_interface/CMakeLists.txt b/packages/krino/krino/adaptivity_interface/CMakeLists.txt new file mode 100644 index 000000000000..6c8d3a739a32 --- /dev/null +++ b/packages/krino/krino/adaptivity_interface/CMakeLists.txt @@ -0,0 +1,19 @@ +SET(HEADERS "") +SET(SOURCES "") + +INCLUDE_DIRECTORIES(${${PACKAGE_NAME}_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +FILE(GLOB HEADERS *.hpp) +FILE(GLOB SOURCES *.cpp) + +TRIBITS_ADD_LIBRARY( + krino_adaptivity_interface_lib + HEADERS ${HEADERS} + SOURCES ${SOURCES} + DEPLIBS krino_lib) + +INSTALL(FILES ${HEADERS} DESTINATION + ${CMAKE_INSTALL_PREFIX}/${${PROJECT_NAME}_INSTALL_INCLUDE_DIR}/krino_adaptivity_interface_lib) + diff --git a/packages/krino/krino/interface_geometry_interface/Akri_InterfaceGeometry.hpp b/packages/krino/krino/interface_geometry_interface/Akri_InterfaceGeometry.hpp new file mode 100644 index 000000000000..2f2a3a32d69d --- /dev/null +++ b/packages/krino/krino/interface_geometry_interface/Akri_InterfaceGeometry.hpp @@ -0,0 +1,79 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef AKRI_INTERFACEGEOMETRY_H_ +#define AKRI_INTERFACEGEOMETRY_H_ +#include +#include +#include +#include +#include +#include + +namespace krino { + +class SnapInfo; + +class ElementCutter +{ +public: + virtual ~ElementCutter() {} + virtual std::vector get_sorted_cutting_interfaces() const = 0; + virtual std::vector get_interface_signs_based_on_crossings(const std::vector & elemNodesCoords, + const std::vector *> & elemNodesSnappedDomains) const = 0; + virtual void fill_tetrahedron_face_interior_intersections(const std::array & faceNodes, + const InterfaceID & interface1, + const InterfaceID & interface2, + const ElementIntersectionPointFilter & intersectionPointFilter, + std::vector & intersections) const = 0; + virtual bool might_have_interior_or_face_intersections() const = 0; + virtual void fill_interior_intersections(const ElementIntersectionPointFilter & intersectionPointFilter, std::vector & intersections) const = 0; + virtual std::string visualize(const stk::mesh::BulkData & mesh) const = 0; + virtual bool have_crossing(const InterfaceID interface, const Segment3d & edge) const = 0; + virtual double interface_crossing_position(const InterfaceID interface, const Segment3d & edge) const = 0; + virtual int sign_at_position(const InterfaceID interface, const Vector3d & paramCoords) const = 0; + virtual int get_starting_phase_for_cutting_surfaces() const = 0; +}; + +class InterfaceGeometry { +public: + InterfaceGeometry() {} + + virtual ~InterfaceGeometry() {} + virtual void prepare_to_process_elements(const stk::mesh::BulkData & mesh, const NodeToCapturedDomainsMap & nodesToCapturedDomains) const = 0; + virtual void prepare_to_process_elements(const stk::mesh::BulkData & mesh, + const std::vector & elementsToIntersect, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const = 0; + + virtual std::vector get_edge_intersection_points(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const = 0; + + virtual void append_element_intersection_points(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToCapturedDomains, + const std::vector & elementsToIntersect, + const IntersectionPointFilter & intersectionPointFilter, + std::vector & intersectionPoints) const = 0; + + // FIXME: Temporary methods + virtual void store_phase_for_uncut_elements(const stk::mesh::BulkData & mesh) const = 0; + virtual void store_phase_for_elements_that_will_be_uncut_after_snapping(const stk::mesh::BulkData & mesh, + const std::vector & intersectionPoints, + const std::vector & independentSnapInfos, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const = 0; + + virtual const ElementToDomainMap & get_phase_for_uncut_elements() const = 0; + + virtual std::unique_ptr build_element_cutter(const stk::mesh::BulkData & mesh, + stk::mesh::Entity element, + const std::function &)> & intersectingPlanesDiagonalPicker) const = 0; + + virtual PhaseTag get_starting_phase(const ElementCutter * cutter) const = 0; +}; +} + +#endif // AKRI_INTERFACEGEOMETRY_H_ diff --git a/packages/krino/krino/interface_geometry_interface/Akri_InterfaceGeometry_dummy.cpp b/packages/krino/krino/interface_geometry_interface/Akri_InterfaceGeometry_dummy.cpp new file mode 100644 index 000000000000..5879b01607a9 --- /dev/null +++ b/packages/krino/krino/interface_geometry_interface/Akri_InterfaceGeometry_dummy.cpp @@ -0,0 +1,3 @@ +namespace krino { +void InterfaceGeometryDummy() {} +} diff --git a/packages/krino/krino/interface_geometry_interface/CMakeLists.txt b/packages/krino/krino/interface_geometry_interface/CMakeLists.txt new file mode 100644 index 000000000000..917ba031808d --- /dev/null +++ b/packages/krino/krino/interface_geometry_interface/CMakeLists.txt @@ -0,0 +1,18 @@ +SET(HEADERS "") +SET(SOURCES "") + +INCLUDE_DIRECTORIES(${${PACKAGE_NAME}_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +FILE(GLOB HEADERS *.hpp) +FILE(GLOB SOURCES *.cpp) + +TRIBITS_ADD_LIBRARY( + interface_geometry_interface_lib + HEADERS ${HEADERS} + SOURCES ${SOURCES}) + +INSTALL(FILES ${HEADERS} DESTINATION + ${CMAKE_INSTALL_PREFIX}/${${PROJECT_NAME}_INSTALL_INCLUDE_DIR}/interface_geometry_interface_lib) + diff --git a/packages/krino/krino/krino_lib/Akri_AdaptivityHelpers.cpp b/packages/krino/krino/krino_lib/Akri_AdaptivityHelpers.cpp new file mode 100644 index 000000000000..cd9709bd0f33 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_AdaptivityHelpers.cpp @@ -0,0 +1,138 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino +{ + +stk::mesh::Part & get_refinement_active_part(const stk::mesh::MetaData & meta, stk::mesh::EntityRank rank) +{ + const std::string active_part_name = "refine_active_elements_part_"+std::to_string((int)rank); + stk::mesh::Part* active_part = meta.get_part(active_part_name); + ThrowRequireMsg(nullptr != active_part, "Active part not found: " << active_part_name); + return *active_part; +} +stk::mesh::Part & get_refinement_inactive_part(const stk::mesh::MetaData & meta, stk::mesh::EntityRank rank) +{ + const std::string inactive_part_name = "refine_inactive_elements_part_"+std::to_string((int)rank); + stk::mesh::Part* inactive_part = meta.get_part(inactive_part_name); + ThrowRequireMsg(nullptr != inactive_part, "Inactive part not found: " << inactive_part_name); + return *inactive_part; +} + +void filter_refinement_marker(const stk::mesh::BulkData & mesh, FieldRef elem_marker, const stk::mesh::Selector & do_not_refine_or_unrefine_selector) +{ + const auto & perceptParentPart = get_refinement_inactive_part(mesh.mesh_meta_data(), stk::topology::ELEMENT_RANK); + + for (auto && bucketPtr : mesh.get_buckets(stk::topology::ELEMENT_RANK, mesh.mesh_meta_data().locally_owned_part())) + { + int * markers = field_data(elem_marker, *bucketPtr); + const int size = bucketPtr->size(); + if (do_not_refine_or_unrefine_selector(*bucketPtr)) + { + for (int i = 0; i < size; ++i) + if (markers[i] == Refinement_Marker::REFINE || markers[i] == Refinement_Marker::COARSEN) + markers[i] = Refinement_Marker::NOTHING; + } + else if (bucketPtr->member(perceptParentPart)) + { + for (int i = 0; i < size; ++i) + if (markers[i] == Refinement_Marker::REFINE) + markers[i] = Refinement_Marker::NOTHING; + } + } +} + +stk::mesh::Selector cdfem_do_not_refine_or_unrefine_selector(const CDFEM_Support & cdfem_support) +{ + const stk::mesh::Selector parent_or_child_selector = + cdfem_support.get_child_part() | cdfem_support.get_parent_part(); + const stk::mesh::Selector decomposed_blocks_selector = + krino::Phase_Support::get(cdfem_support.get_mesh_meta()).get_all_decomposed_blocks_selector(); + const stk::mesh::Selector do_not_refine_selector = (!decomposed_blocks_selector) | parent_or_child_selector; + return do_not_refine_selector; +} + + +void perform_multilevel_adaptivity(stk::mesh::BulkData & mesh, + const std::string & marker_field_name, + const std::function & marker_function, + const std::function & adapt_function, + const stk::mesh::Selector & do_not_refine_selector) +{ + Tracespec trace__("perform_multilevel_adaptivity()"); + + const auto & aux_meta = AuxMetaData::get(mesh.mesh_meta_data()); + + const FieldRef elem_marker = aux_meta.get_field( + stk::topology::ELEMENT_RANK, marker_field_name, stk::mesh::StateNew); + + const stk::mesh::Selector active_selector = aux_meta.active_part(); + const stk::mesh::Selector locally_owned_selector = mesh.mesh_meta_data().locally_owned_part(); + + std::vector children; + + bool done = false; + int num_refinements = 0; + while (!done) + { + marker_function(marker_field_name, num_refinements); + filter_refinement_marker(mesh, elem_marker, do_not_refine_selector); + + const auto & local_buckets = mesh.get_buckets(stk::topology::ELEMENT_RANK, locally_owned_selector); + unsigned num_marked_refine = 0; + for (auto && b_ptr : local_buckets) + { + int * markers = field_data(elem_marker, *b_ptr); + for (size_t i = 0; i < b_ptr->size(); ++i) + { + if (markers[i] == Refinement_Marker::REFINE) ++num_marked_refine; + } + } + + // Only worth cost of adaptive refinement if any elements are marked for refinement + unsigned global_num_marked_refine = 0; + stk::all_reduce_sum(mesh.parallel(), &num_marked_refine, &global_num_marked_refine, 1); + if (global_num_marked_refine > 0) + { + const int debug_level = 0; + adapt_function(marker_field_name, debug_level); + + ++num_refinements; + } + else + { + done = true; + } + } + + // This probably should not be needed. + CDMesh::fixup_adapted_element_parts(mesh); + + // This probably should not be needed. + attach_sides_to_elements(mesh); +} +} diff --git a/packages/krino/krino/krino_lib/Akri_AdaptivityHelpers.hpp b/packages/krino/krino/krino_lib/Akri_AdaptivityHelpers.hpp new file mode 100644 index 000000000000..17a29e7ccafb --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_AdaptivityHelpers.hpp @@ -0,0 +1,41 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_ADAPTIVITYHELPERS_H_ +#define KRINO_INCLUDE_AKRI_ADAPTIVITYHELPERS_H_ + +#include +#include +#include +#include + +namespace krino { + +class CDFEM_Support; + +stk::mesh::Part & get_refinement_active_part(const stk::mesh::MetaData & meta, stk::mesh::EntityRank rank); +stk::mesh::Part & get_refinement_inactive_part(const stk::mesh::MetaData & meta, stk::mesh::EntityRank rank); +stk::mesh::Selector cdfem_do_not_refine_or_unrefine_selector(const CDFEM_Support & cdfem_support); + +enum Refinement_Marker + { + COARSEN = -1, + NOTHING = 0, + REFINE = 1 + }; + +void +perform_multilevel_adaptivity(stk::mesh::BulkData & mesh, + const std::string & marker_field_name, + const std::function & marker_function, + const std::function & adapt_function, + const stk::mesh::Selector & do_not_refine_selector = stk::mesh::Selector()); + +} + +#endif /* KRINO_INCLUDE_AKRI_ADAPTIVITYHELPERS_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_AnalyticSurf.cpp b/packages/krino/krino/krino_lib/Akri_AnalyticSurf.cpp new file mode 100644 index 000000000000..374396fb7bbf --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_AnalyticSurf.cpp @@ -0,0 +1,323 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include + +#include + +namespace krino{ + +Cylinder::Cylinder(const std::string & n, // surface name + const double e1[3], // first endpoint of axis + const double e2[3], // second endpoint of axis + const double r, // radius of cylinder + const int sign) + : SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign(), + dist_sign(sign) +{ + // initialize internal description of cylinder + radius = r; + + p1 = Vector3d(e1); + p2 = Vector3d(e2); + + xi = p2 - p1; + + length = xi.length(); + + xi.unitize(); +} + +void +Cylinder::prepare_to_compute(const double time, const BoundingBox & point_bbox, const double truncation_length) +{ + if (NULL == my_transformation) + { + return; + } + + my_transformation->update(time); + my_transformation->apply(p1); + my_transformation->apply(p2); + + xi = p2 - p1; + + length = xi.length(); + + xi.unitize(); +} + +BoundingBox +Cylinder::get_bounding_box() +{ + BoundingBox bbox; + bbox.accommodate(p1); + bbox.accommodate(p2); + bbox.pad(radius); + return bbox; +} + +double +Cylinder::point_signed_distance(const Vector3d &x) const +{ + double D = std::numeric_limits::max(); + + // convert x to cylindrical coordinates + + // v is the vector from p1 to x. + Vector3d v = x - p1; + + // Xix is the projection of v along the axis of the cylinder + const double Xix = Dot(v,xi); + + // u is the associated vector + Vector3d u = Xix*xi; + + // Rx is the distance from the axis of the cylinder to x + + const double Rx = (v - u).length(); + + if ( Xix <= 0 ) + { + // x is behind the cylinder. + // The closest point on the cylinder lies on the rear circular face. + + if ( Rx <= radius ) + { + D = -Xix; + } + else + { + D = std::sqrt(Xix*Xix + (Rx-radius)*(Rx-radius)); + } + } + else if( Xix > 0 && Xix < length) + { + // x is not in front of or behind the cylinder: it is alongside the cylinder. + // the signed distance is given by Rx - radius unless this point is closer + // to one of the circular faces + + if ( Rx >= radius ) + { + D = Rx - radius; + } + else + { + D = std::max( std::max(Rx - radius, -Xix), Xix-length ); + } + } + else + { + // x is in front of the cylinder. + // The closest point on the cylinder lies on the front circular face. + + if ( Rx <= radius ) + { + D = Xix-length; + } + else + { + D = std::sqrt((Xix-length)*(Xix-length) + (Rx-radius)*(Rx-radius)); + } + } + + return dist_sign*D; +} + +Point::Point(const std::string & n, // surface name + const Vector3d & coords) + : SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign(), + my_coords(coords) +{ +} + +double +Point::point_signed_distance(const Vector3d &x) const +{ + return (x-my_coords).length_squared(); +} + +BoundingBox +Point::get_bounding_box() +{ + BoundingBox bbox; + bbox.accommodate(my_coords); + return bbox; +} + +Sphere::Sphere(const std::string & n, // surface name + const Vector3d & center, + const double radius, + const int sign) + : SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign(), + myDistSign(sign), + myCenter(center), + myRadius(radius) +{ +} + +void +Sphere::prepare_to_compute(const double time, const BoundingBox & point_bbox, const double truncation_length) +{ + if (nullptr != my_transformation) + { + my_transformation->update(time); + my_transformation->apply(myCenter); + } +} + +BoundingBox +Sphere::get_bounding_box() +{ + return BoundingBox( + Vector3d(myCenter[0]-myRadius, myCenter[1]-myRadius, myCenter[2]-myRadius), + Vector3d(myCenter[0]+myRadius, myCenter[1]+myRadius, myCenter[2]+myRadius) + ); +} + +double +Sphere::point_signed_distance(const Vector3d &x) const +{ + return (myDistSign*((x-myCenter).length() - myRadius)); +} + +Ellipsoid::Ellipsoid( + const std::string & name, // surface name + const std::vector & center, + const std::vector & semiAxes, + const std::vector & rotationVec, + const int sign) +: SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign(), + mySign(sign) +{ + ThrowAssert(center.size() == 3 && semiAxes.size() == 3); + mySemiAxesNorm = 0.0; + for(unsigned i = 0; i < 3; ++i) { + myCenter[i] = center[i]; + mySemiAxes[i] = semiAxes[i]; + mySemiAxesNorm += mySemiAxes[i]*mySemiAxes[i]; + } + mySemiAxesNorm = std::sqrt(mySemiAxesNorm); + + if (!rotationVec.empty()) + { + ThrowAssert(rotationVec.size() == 3); + Vector3d pointRotation(-rotationVec[0], -rotationVec[1], -rotationVec[2]); // myRotation is the rotation used for the query point locations, which is the opposite of the rotation of the ellipsoid + myRotation = std::make_unique(); + myRotation->set_from_rotation_vector(pointRotation); + } +} + +BoundingBox +Ellipsoid::get_bounding_box() +{ + if (!myRotation) + return BoundingBox(myCenter-mySemiAxes, myCenter+mySemiAxes); + + const Vector3d min = myCenter-mySemiAxes; + const Vector3d max = myCenter+mySemiAxes; + BoundingBox rotatedBbox; + rotatedBbox.accommodate(myRotation->reverse_rotate_3d_vector(Vector3d(min[0],min[1],min[2]))); + rotatedBbox.accommodate(myRotation->reverse_rotate_3d_vector(Vector3d(min[0],min[1],max[2]))); + rotatedBbox.accommodate(myRotation->reverse_rotate_3d_vector(Vector3d(min[0],max[1],min[2]))); + rotatedBbox.accommodate(myRotation->reverse_rotate_3d_vector(Vector3d(min[0],max[1],max[2]))); + rotatedBbox.accommodate(myRotation->reverse_rotate_3d_vector(Vector3d(max[0],min[1],min[2]))); + rotatedBbox.accommodate(myRotation->reverse_rotate_3d_vector(Vector3d(max[0],min[1],max[2]))); + rotatedBbox.accommodate(myRotation->reverse_rotate_3d_vector(Vector3d(max[0],max[1],min[2]))); + rotatedBbox.accommodate(myRotation->reverse_rotate_3d_vector(Vector3d(max[0],max[1],max[2]))); + + return rotatedBbox; +} + +double +Ellipsoid::point_signed_distance(const Vector3d &x) const +{ + Vector3d delta = x-myCenter; + if (myRotation) + delta = myRotation->rotate_3d_vector(delta); + // Not an exact distance except in the case of a sphere + const double dx = delta[0]/mySemiAxes[0]; + const double dy = delta[1]/mySemiAxes[1]; + const double dz = delta[2]/mySemiAxes[2]; + return mySign*(mySemiAxesNorm*std::sqrt(dx*dx+dy*dy+dz*dz)-mySemiAxesNorm); +} + +Plane::Plane(const std::string & n, // surface name + const double normal[3], + const double offset, + const double multiplier) + : SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign(), + myMultiplier(multiplier), + myNormal(normal), + myOffset(offset) +{ + myNormal.unitize(); +} + +double +Plane::point_signed_distance(const Vector3d &x) const +{ + return myMultiplier*(Dot(myNormal, x) + myOffset); +} + +BoundingBox +Plane::get_bounding_box() +{ + //bounding box is entire domain + return BoundingBox(Vector3d(-std::numeric_limits::max(), -std::numeric_limits::max(), -std::numeric_limits::max()), + Vector3d(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max())); +} + +Random::Random(const unsigned long seed) + : SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign(), + my_amplitude(1.0) +{ + my_srand(seed); +} + +double +Random::point_signed_distance(const Vector3d &x) const +{ + // generate random number between -my_amplitude and my_amplitude + return my_amplitude * (-1.0 + 2.0 * my_rand()); +} + +BoundingBox +Random::get_bounding_box() +{ + //bounding box is entire domain + return BoundingBox(Vector3d(-std::numeric_limits::max(), -std::numeric_limits::max(), -std::numeric_limits::max()), + Vector3d(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max())); +} + +Analytic_Isosurface::Analytic_Isosurface() + : SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign() +{ +} + +BoundingBox +Analytic_Isosurface::get_bounding_box() +{ + return BoundingBox( + Vector3d(-1.,-1.,-1.), + Vector3d(1.,1.,1.) + ); +} + +double +Analytic_Isosurface::point_signed_distance(const Vector3d &coord) const +{ + const double x = coord[0]; + const double y = coord[1]; + const double z = coord[2]; + return 2.*y*(y*y-3.*x*x)*(1.-z*z) + std::pow(x*x+y*y,2) - (9.*z*z-1.)*(1.-z*z); +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_AnalyticSurf.hpp b/packages/krino/krino/krino_lib/Akri_AnalyticSurf.hpp new file mode 100644 index 000000000000..18f70a5bb0e1 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_AnalyticSurf.hpp @@ -0,0 +1,186 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_AnalyticSurf_h +#define Akri_AnalyticSurf_h + +#include +#include +#include + +#include +#include + +#include + +namespace stk { namespace mesh { class BulkData; } } +namespace stk { namespace mesh { class Entity; } } +namespace stk { namespace mesh { class MetaData; } } +namespace stk { namespace mesh { class Selector; } } + +namespace krino { + +class Cylinder: public SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign { +public: + Cylinder(const std::string & n, // surface name + const double e1[3], // first endpoint of axis + const double e2[3], // second endpoint of axis + const double r, // radius of cylinder + const int sign); + + virtual ~Cylinder() {} + + virtual Surface_Type type() const override { return CYLINDER; } + virtual size_t storage_size() const override { return sizeof(Cylinder); } + + virtual void prepare_to_compute(const double time, const BoundingBox & point_bbox, const double truncation_length) override; + virtual double point_signed_distance(const Vector3d &x) const override; + virtual BoundingBox get_bounding_box() override; + +private: + int dist_sign; + double radius; + double length; + + // two end points + Vector3d p1, p2; + + // unit vector in direction of axis. + Vector3d xi; +}; + +class Sphere: public SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign { +public: + Sphere(const std::string & n, // surface name + const Vector3d & center, + const double radius, + const int sign = 1); + + virtual ~Sphere() {} + + virtual Surface_Type type() const override { return SPHERE; } + virtual size_t storage_size() const override { return sizeof(Sphere); } + + virtual void prepare_to_compute(const double time, const BoundingBox & point_bbox, const double truncation_length) override; + virtual double point_signed_distance(const Vector3d &x) const override; + virtual BoundingBox get_bounding_box() override; + +private: + int myDistSign; + Vector3d myCenter; + double myRadius; +}; + +class Ellipsoid : public SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign { +public: + Ellipsoid( + const std::string & name, // surface name + const std::vector & center, + const std::vector & semiAxes, + const std::vector & rotationVec, + const int sign); + + virtual ~Ellipsoid() {} + + virtual Surface_Type type() const override { return ELLIPSOID; } + virtual size_t storage_size() const override { return sizeof(Ellipsoid); } + + virtual double point_signed_distance(const Vector3d &x) const override; + virtual BoundingBox get_bounding_box() override; + +private: + int mySign; + + //center of ellipsoid + Vector3d myCenter; + + Vector3d mySemiAxes; + double mySemiAxesNorm; + std::unique_ptr myRotation; +}; + +class Plane: public SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign { +public: + Plane(const std::string & n, // surface name + const double normal[3], + const double offset, + const double multiplier); + + virtual ~Plane() {} + + virtual Surface_Type type() const override { return PLANE; } + virtual size_t storage_size() const override { return sizeof(Plane); } + + virtual double point_signed_distance(const Vector3d &x) const override; + virtual BoundingBox get_bounding_box() override; + +private: + double myMultiplier; + + Vector3d myNormal; //unit normal to plane + double myOffset; //eq of plane is 0 = offset + normal \dot x +}; + +class Point: public SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign { +public: + Point(const std::string & n, // surface name + const Vector3d & coords); + + virtual ~Point() {} + + virtual Surface_Type type() const override { return POINT; } + virtual size_t storage_size() const override { return sizeof(Point); } + + virtual double point_signed_distance(const Vector3d &x) const override; + virtual BoundingBox get_bounding_box() override; + +private: + Vector3d my_coords; +}; + +class Random : public SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign { +public: + Random(const unsigned long seed); + + virtual ~Random() {} + + virtual Surface_Type type() const override { return RANDOM; } + virtual size_t storage_size() const override { return sizeof(Random); } + virtual void prepare_to_compute(const double time, const BoundingBox & point_bbox, const double truncation_length) override + { + if (truncation_length > 0.0) my_amplitude = truncation_length; + } + + virtual double point_signed_distance(const Vector3d &x) const override; + virtual BoundingBox get_bounding_box() override; + +private: + mutable unsigned long iseed; + double my_amplitude; + + double my_rand() const { iseed = (1664525L*iseed + 1013904223L) & 0xffffffff; + return( ((double) iseed) / ((double) 0xffffffff) ); } + void my_srand(unsigned int seed) const {iseed = seed;} +}; + +class Analytic_Isosurface: public SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign { +public: + Analytic_Isosurface(); + + virtual ~Analytic_Isosurface() {} + + virtual Surface_Type type() const override { return SPHERE; } + virtual size_t storage_size() const override { return sizeof(Analytic_Isosurface); } + + virtual double point_signed_distance(const Vector3d &x) const override; + virtual BoundingBox get_bounding_box() override; +}; + +} // namespace krino + +#endif // Akri_AnalyticSurf_h diff --git a/packages/krino/krino/krino_lib/Akri_AnalyticSurfaceInterfaceGeometry.cpp b/packages/krino/krino/krino_lib/Akri_AnalyticSurfaceInterfaceGeometry.cpp new file mode 100644 index 000000000000..5062a28d0c4b --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_AnalyticSurfaceInterfaceGeometry.cpp @@ -0,0 +1,203 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include + +namespace krino { + +static int surface_sign_at_position(const Surface & surface, const Vector3d & pt) +{ + const double phi = surface.point_signed_distance(pt); + return ( (phi < 0.) ? -1 : 1 ); // GOMA sign convention +} + +static std::function build_edge_distance_function(const Surface & surface, const Segment3d & edge) +{ + std::function distanceFunction = + [&surface, &edge](const double x) + { + return surface.point_signed_distance((1.-x)*edge.GetNode(0) + x*edge.GetNode(1)); + }; + return distanceFunction; +} + +static double find_crossing_position(const Surface & surface, const Segment3d & edge) +{ + const double phi0 = surface.point_signed_distance(edge.GetNode(0)); + const double phi1 = surface.point_signed_distance(edge.GetNode(1)); + const auto result = find_root(build_edge_distance_function(surface, edge), 0., 1., phi0, phi1); + ThrowRequire(result.first); + return result.second; +} + +SurfaceElementCutter::SurfaceElementCutter(const stk::mesh::BulkData & mesh, + stk::mesh::Entity element, + const Surface & surface) +: myMasterElem(MasterElementDeterminer::getMasterElement(mesh.bucket(element).topology())), + mySurface(surface) +{ + const FieldRef coordsField(mesh.mesh_meta_data().coordinate_field()); + fill_element_node_coordinates(mesh, element, coordsField, myElementNodeCoords); +} + +bool SurfaceElementCutter::have_crossing(const InterfaceID interface, const Segment3d & edge) const +{ + return surface_sign_at_position(mySurface, parametric_to_global_coordinates(edge.GetNode(0))) != + surface_sign_at_position(mySurface, parametric_to_global_coordinates(edge.GetNode(1))); +} + +double SurfaceElementCutter::interface_crossing_position(const InterfaceID interface, const Segment3d & edge) const +{ + const Segment3d globalEdge(parametric_to_global_coordinates(edge.GetNode(0)), parametric_to_global_coordinates(edge.GetNode(1))); + return find_crossing_position(mySurface, globalEdge); +} + +int SurfaceElementCutter::sign_at_position(const InterfaceID interface, const Vector3d & paramCoords) const +{ + return surface_sign_at_position(mySurface, parametric_to_global_coordinates(paramCoords)); +} + +Vector3d SurfaceElementCutter::parametric_to_global_coordinates(const Vector3d & pCoords) const +{ + std::vector nodalShapeFunctions(myMasterElem.num_nodes()); + myMasterElem.shape_fcn(1, pCoords.data(), nodalShapeFunctions.data()); + Vector3d pt(Vector3d::ZERO); + for (unsigned n=0; n & elementsToIntersect, + const Surface & surface, + const IntersectionPointFilter & intersectionPointFilter, + std::vector & intersectionPoints) +{ + const bool intersectionPointIsOwned = true; + std::vector intersectionPointSortedDomains; + const int dim = mesh.mesh_meta_data().spatial_dimension(); + const FieldRef coordsField(mesh.mesh_meta_data().coordinate_field()); + std::set> edgesAlreadyChecked; + for (auto && elem : elementsToIntersect) + { + const stk::topology topology = mesh.bucket(elem).topology(); + const stk::mesh::Entity* elem_nodes = mesh.begin_nodes(elem); + const unsigned numEdges = topology.num_edges(); + + for (unsigned iedge = 0; iedge < numEdges; ++iedge) + { + const unsigned * edge_node_ordinals = get_edge_node_ordinals(topology, iedge); + const stk::mesh::Entity node0 = elem_nodes[edge_node_ordinals[0]]; + const stk::mesh::Entity node1 = elem_nodes[edge_node_ordinals[1]]; + const stk::mesh::EntityId node0Id = mesh.identifier(node0); + const stk::mesh::EntityId node1Id = mesh.identifier(node1); + const std::array edgeNodeIds = (node0Id < node1Id) ? std::array{node0Id, node1Id} : std::array{node1Id, node0Id}; + auto iter = edgesAlreadyChecked.lower_bound(edgeNodeIds); + if (iter == edgesAlreadyChecked.end() || edgeNodeIds != *iter) + { + edgesAlreadyChecked.insert(iter, edgeNodeIds); + const Vector3d node0Coords(field_data(coordsField, node0), dim); + const Vector3d node1Coords(field_data(coordsField, node1), dim); + const double phi0 = surface.point_signed_distance(node0Coords); + const double phi1 = surface.point_signed_distance(node1Coords); + const bool haveCrossing = (phi0 < 0.) ? (phi1 >= 0.) : (phi1 < 0.); + if (haveCrossing) + { + const InterfaceID interface(0,0); + const double location = find_crossing_position(surface, Segment3d(node0Coords, node1Coords)); + interface.fill_sorted_domains(intersectionPointSortedDomains); + const std::vector intersectionPointNodes{node0,node1}; + if (intersectionPointFilter(intersectionPointNodes, intersectionPointSortedDomains)) + intersectionPoints.emplace_back(intersectionPointIsOwned, intersectionPointNodes, std::vector{1.-location, location}, intersectionPointSortedDomains); + } + } + } + } +} + +static BoundingBox compute_nodal_bounding_box(const stk::mesh::BulkData & mesh) +{ + const int nDim = mesh.mesh_meta_data().spatial_dimension(); + const FieldRef coordsField(mesh.mesh_meta_data().coordinate_field()); + + BoundingBox nodeBbox; + for ( auto && bucket : mesh.buckets(stk::topology::NODE_RANK) ) + { + double *coord = field_data(coordsField, *bucket); + for (size_t n = 0; n < bucket->size(); ++n) + nodeBbox.accommodate( Vector3d(coord+n*nDim, nDim) ); + } + + return nodeBbox; +} + +static void prepare_to_compute_with_surface(const stk::mesh::BulkData & mesh, const Surface & surface) +{ + const BoundingBox nodeBbox = compute_nodal_bounding_box(mesh); + Surface & nonConstSurface = const_cast(surface); + nonConstSurface.prepare_to_compute(0.0, nodeBbox, 0.); // Setup including communication of facets that are within this processors narrow band +} + +void AnalyticSurfaceInterfaceGeometry::prepare_to_process_elements(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const +{ + myElementsToIntersect = get_owned_parent_elements(mesh, myActivePart, myCdfemSupport, myPhaseSupport); + prepare_to_compute_with_surface(mesh, mySurface); +} + +void AnalyticSurfaceInterfaceGeometry::prepare_to_process_elements(const stk::mesh::BulkData & mesh, + const std::vector & elementsToIntersect, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const +{ + myElementsToIntersect = elementsToIntersect; + prepare_to_compute_with_surface(mesh, mySurface); +} + +std::vector AnalyticSurfaceInterfaceGeometry::get_edge_intersection_points(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const +{ + const IntersectionPointFilter intersectionPointFilter = keep_all_intersection_points_filter(); + std::vector intersectionPoints; + append_surface_edge_intersection_points(mesh, myElementsToIntersect, mySurface, intersectionPointFilter, intersectionPoints); + return intersectionPoints; +} + +void AnalyticSurfaceInterfaceGeometry::append_element_intersection_points(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToCapturedDomains, + const std::vector & elementsToIntersect, + const IntersectionPointFilter & intersectionPointFilter, + std::vector & intersectionPoints) const +{ + prepare_to_process_elements(mesh, elementsToIntersect, nodesToCapturedDomains); + append_surface_edge_intersection_points(mesh, myElementsToIntersect, mySurface, intersectionPointFilter, intersectionPoints); +} + +std::unique_ptr AnalyticSurfaceInterfaceGeometry::build_element_cutter(const stk::mesh::BulkData & mesh, + stk::mesh::Entity element, + const std::function &)> & intersectingPlanesDiagonalPicker) const +{ + std::unique_ptr cutter; + cutter.reset( new SurfaceElementCutter(mesh, element, mySurface) ); + return cutter; +} + +PhaseTag AnalyticSurfaceInterfaceGeometry::get_starting_phase(const ElementCutter * cutter) const +{ + PhaseTag phase; + ThrowRequire(1 == myCdfemSupport.num_ls_fields()); + phase.add(myCdfemSupport.ls_field(0).identifier, 0); + return phase; +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_AnalyticSurfaceInterfaceGeometry.hpp b/packages/krino/krino/krino_lib/Akri_AnalyticSurfaceInterfaceGeometry.hpp new file mode 100644 index 000000000000..f72ce103d4d1 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_AnalyticSurfaceInterfaceGeometry.hpp @@ -0,0 +1,111 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_AnalyticSurfaceInterfaceGeometry_h +#define Akri_AnalyticSurfaceInterfaceGeometry_h + +#include +#include +#include +#include +#include + +#include "../interface_geometry_interface/Akri_InterfaceGeometry.hpp" + +namespace krino { + +class CDFEM_Support; +class Phase_Support; + +class SurfaceElementCutter : public ElementCutter +{ +public: + SurfaceElementCutter(const stk::mesh::BulkData & mesh, + stk::mesh::Entity element, + const Surface & surface); + virtual ~SurfaceElementCutter() {} + + virtual bool might_have_interior_or_face_intersections() const override { return false; } + virtual void fill_interior_intersections(const ElementIntersectionPointFilter & intersectionPointFilter, std::vector & intersections) const override {} + virtual std::vector get_sorted_cutting_interfaces() const override + { std::vector interfaces; interfaces.push_back(InterfaceID(0,0)); return interfaces; } + virtual std::vector get_interface_signs_based_on_crossings(const std::vector & elemNodesCoords, + const std::vector *> & elemNodesSnappedDomains) const override + { std::vector interfaceSigns(1,0); return interfaceSigns; } + virtual void fill_tetrahedron_face_interior_intersections(const std::array & faceNodes, + const InterfaceID & interface1, + const InterfaceID & interface2, + const ElementIntersectionPointFilter & intersectionPointFilter, + std::vector & intersections) const override {} + virtual std::string visualize(const stk::mesh::BulkData & mesh) const override { std::string empty; return empty; } + virtual bool have_crossing(const InterfaceID interface, const Segment3d & edge) const override; + virtual double interface_crossing_position(const InterfaceID interface, const Segment3d & edge) const override; + virtual int sign_at_position(const InterfaceID interface, const Vector3d & paramCoords) const override; + virtual int get_starting_phase_for_cutting_surfaces() const override { return 0; } + +private: + Vector3d parametric_to_global_coordinates(const Vector3d & pCoords) const; + + const MasterElement & myMasterElem; + std::vector myElementNodeCoords; + const Surface & mySurface; +}; + +class AnalyticSurfaceInterfaceGeometry : public InterfaceGeometry { + +public: + AnalyticSurfaceInterfaceGeometry(const Surface & surface, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport) +: mySurface(surface), + myActivePart(activePart), + myCdfemSupport(cdfemSupport), + myPhaseSupport(phaseSupport) {} + + virtual ~AnalyticSurfaceInterfaceGeometry() {} + + virtual void prepare_to_process_elements(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToSnappedDomains) const override; + virtual void prepare_to_process_elements(const stk::mesh::BulkData & mesh, + const std::vector & elementsToIntersect, + const NodeToCapturedDomainsMap & nodesToSnappedDomains) const override; + + virtual std::vector get_edge_intersection_points(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToSnappedDomains) const override; + virtual void append_element_intersection_points(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToSnappedDomains, + const std::vector & elementsToIntersect, + const IntersectionPointFilter & intersectionPointFilter, + std::vector & intersectionPoints) const override; + + virtual void store_phase_for_uncut_elements(const stk::mesh::BulkData & mesh) const override {} + virtual void store_phase_for_elements_that_will_be_uncut_after_snapping(const stk::mesh::BulkData & mesh, + const std::vector & intersectionPoints, + const std::vector & independentSnapInfos, + const NodeToCapturedDomainsMap & nodesToSnappedDomains) const override {} + virtual const ElementToDomainMap & get_phase_for_uncut_elements() const override { return myUncutElementPhases; } + + virtual std::unique_ptr build_element_cutter(const stk::mesh::BulkData & mesh, + stk::mesh::Entity element, + const std::function &)> & intersectingPlanesDiagonalPicker) const override; + + virtual PhaseTag get_starting_phase(const ElementCutter * cutter) const override; + +private: + const Surface & mySurface; + const stk::mesh::Part & myActivePart; + const CDFEM_Support & myCdfemSupport; + const Phase_Support & myPhaseSupport; + ElementToDomainMap myUncutElementPhases; + mutable std::vector myElementsToIntersect; +}; + +} // namespace krino + +#endif diff --git a/packages/krino/krino/krino_lib/Akri_AuxMetaData.cpp b/packages/krino/krino/krino_lib/Akri_AuxMetaData.cpp new file mode 100644 index 000000000000..99584a22279a --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_AuxMetaData.cpp @@ -0,0 +1,396 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include "Akri_FieldRef.hpp" // for FieldRef + +#include +#include "stk_mesh/base/FieldBase.hpp" // for FieldState +#include "stk_mesh/base/MetaData.hpp" +#include "stk_mesh/base/Part.hpp" // for Part +#include "stk_topology/topology.hpp" // for topology, etc +#include +#include "stk_util/util/ReportHandler.hpp" // for ThrowAssert, etc + +namespace krino +{ +const FieldType FieldType::UNSIGNED_INTEGER_64("UNSIGNED INTEGER 64", typeid(uint64_t), 1); +const FieldType FieldType::UNSIGNED_INTEGER("UNSIGNED INTEGER", typeid(unsigned), 1); +const FieldType FieldType::INTEGER("INTEGER", typeid(int), 1); +const FieldType FieldType::REAL("REAL", typeid(double), 1); + +const FieldType FieldType::VECTOR_2D("VECTOR_2D", typeid(double), 2); +const FieldType FieldType::VECTOR_3D("VECTOR_3D", typeid(double), 3); +const FieldType FieldType::MATRIX_22("MATRIX_22", typeid(double), 4); +const FieldType FieldType::MATRIX_33("MATRIX_33", typeid(double), 9); + +AuxMetaData & +AuxMetaData::get(const stk::mesh::MetaData & stk_meta) +{ + AuxMetaData * aux_meta = const_cast(stk_meta.get_attribute()); + ThrowRequireMsg(nullptr != aux_meta, "AuxMetaData not found on MetaData."); + return *aux_meta; +} + +AuxMetaData & +AuxMetaData::create(stk::mesh::MetaData & stk_meta) +{ + AuxMetaData * aux_meta = const_cast(stk_meta.get_attribute()); + ThrowRequireMsg(nullptr == aux_meta, "AuxMetaData::create should be caled only once per MetaData."); + if (nullptr == aux_meta) + { + aux_meta = new AuxMetaData(stk_meta); + stk_meta.declare_attribute_with_delete(aux_meta); + } + return *aux_meta; +} + +AuxMetaData::AuxMetaData(stk::mesh::MetaData & stk_meta) + : my_meta(stk_meta), + is_fmwk(false), + my_assert_32bit_flag(false), + my_force_64bit_flag(true), // just to guarantee testing 64 bit as much as possible + my_active_part(nullptr), + my_exposed_boundary_part(nullptr), + my_block_boundary_part(nullptr) +{ + stk::mesh::Part * existing_active_part = my_meta.get_part("ACTIVE_CONTEXT_BIT"); + if (nullptr != existing_active_part) + { + my_active_part = existing_active_part; + my_exposed_boundary_part = my_meta.get_part("EXPOSED_BOUNDARY_CONTEXT_BIT"); + my_block_boundary_part = my_meta.get_part("BLOCK_BOUNDARY_CONTEXT_BIT"); + } + else + { + ThrowRequireMsg(my_meta.spatial_dimension() > 0, "For non-Fmwk usage, AuxMetaData cannot be created until after the spatial_dimension is set on the stk::mesh::MetaData."); + ThrowRequireMsg(!my_meta.is_commit(), "For non-Fmwk usage, AuxMetaData must be created before the stk::mesh::MetaData is committed."); + my_active_part = &my_meta.declare_part("ACTIVE_CONTEXT_BIT"); + my_exposed_boundary_part = &my_meta.declare_part("EXPOSED_BOUNDARY_CONTEXT_BIT", my_meta.side_rank()); + my_block_boundary_part = &my_meta.declare_part("BLOCK_BOUNDARY_CONTEXT_BIT", my_meta.side_rank()); + } + + my_active_globally_shared_selector = stk::mesh::Selector(*my_active_part & my_meta.globally_shared_part()); + my_active_not_ghost_selector = stk::mesh::Selector(*my_active_part & (my_meta.locally_owned_part() | my_meta.globally_shared_part())); + my_active_locally_owned_selector = stk::mesh::Selector(*my_active_part & my_meta.locally_owned_part()); +} + +void +AuxMetaData::set_fmwk_functions( + const std::function & in_fmwk_get_iopart, + const std::function & in_fmwk_iopart, + const std::function & in_fmwk_define_iopart_alias, + const std::function & in_fmwk_register_field, + stk::mesh::Part * in_exposed_boundary_part, + stk::mesh::Part * in_block_boundary_part) +{ + is_fmwk = true; + clear_force_64bit_flag(); + fmwk_get_iopart = in_fmwk_get_iopart; + fmwk_iopart = in_fmwk_iopart; + fmwk_define_iopart_alias = in_fmwk_define_iopart_alias; + fmwk_register_field = in_fmwk_register_field; + my_exposed_boundary_part = in_exposed_boundary_part; + my_block_boundary_part = in_block_boundary_part; +} + +void +AuxMetaData::set_inducer_functions( + const std::function & in_inducer_induce_topology_nodesets, + const std::function & in_inducer_get_nodal_field_topology, + const std::function & in_inducer_selectField) +{ + fn_induce_topology_nodesets = in_inducer_induce_topology_nodesets; + fn_get_nodal_field_topology = in_inducer_get_nodal_field_topology; + fn_selectField = in_inducer_selectField; +} + +stk::mesh::Part & AuxMetaData::block_boundary_part() const +{ + ThrowAssert(nullptr != my_block_boundary_part); return *my_block_boundary_part; +} + +stk::mesh::Part & AuxMetaData::exposed_boundary_part() const +{ + ThrowAssert(nullptr != my_exposed_boundary_part); return *my_exposed_boundary_part; +} + +bool +AuxMetaData::has_field( const stk::mesh::EntityRank obj_type, const std::string& name ) const +{ + return NULL != my_meta.get_field(obj_type, name); +} + +bool +AuxMetaData::has_field( const stk::mesh::EntityRank obj_type, const std::string& name, stk::mesh::FieldState state ) const +{ + stk::mesh::FieldBase* field_ptr = my_meta.get_field(obj_type, name); + if (NULL == field_ptr) return false; + return NULL != field_ptr->field_state(state); +} + +FieldRef +AuxMetaData::get_field( const stk::mesh::EntityRank obj_type, const std::string& name ) const +{ + stk::mesh::FieldBase* field_ptr = my_meta.get_field(obj_type, name); + ThrowRequireMsg(NULL != field_ptr, "Field \"" << name << "\" not found."); + return FieldRef(field_ptr); +} + + +FieldRef +AuxMetaData::get_field( const stk::mesh::EntityRank obj_type, const std::string& name, stk::mesh::FieldState state ) const +{ + stk::mesh::FieldBase* field_ptr = my_meta.get_field(obj_type, name); + if(field_ptr == nullptr) + { + std::ostringstream err_msg; + err_msg << "Field \"" << name << "\" not found.\n"; + err_msg << "Registered fields are:\n"; + for(auto && field : my_meta.get_fields(obj_type)) + { + err_msg << field->name() << "\n"; + } + throw std::runtime_error(err_msg.str()); + } + return FieldRef(field_ptr, state); +} + +bool +AuxMetaData::has_part( const std::string& name ) const +{ + stk::mesh::Part * part = my_meta.get_part(name); + // If the part is not found see if the name is actually a Fmwk alias + if (!part && is_fmwk) part = fmwk_get_iopart(name); + return NULL != part; +} + +stk::mesh::Part& +AuxMetaData::get_part( const std::string& name ) const +{ + stk::mesh::Part * part = my_meta.get_part(name); + // If the part is not found see if the name is actually a Fmwk alias + if (!part && is_fmwk) part = fmwk_get_iopart(name); + ThrowRequireMsg(part, "Could not find part " << name;); + return *part; +} + +void +AuxMetaData::define_part_alias( stk::mesh::Part & part , const std::string & alias ) +{ + if (is_fmwk) + { + fmwk_define_iopart_alias(part, alias); + return; + } + // Silent no-op +} + +void +AuxMetaData::assign_part_id(stk::mesh::Part& part) +{ + static int64_t max_existing_part_id = stk::mesh::Part::INVALID_ID; + if (max_existing_part_id == stk::mesh::Part::INVALID_ID) + { + max_existing_part_id = 0; + for (auto && existing_part : my_meta.get_parts()) + { + max_existing_part_id = std::max(max_existing_part_id, existing_part->id()); + } + } + if (part.id() == stk::mesh::Part::INVALID_ID) + { + const int64_t part_id = ++max_existing_part_id; + my_meta.set_part_id(part, part_id); + } +} + +stk::mesh::Part& +AuxMetaData::declare_io_part( const std::string& name, const stk::mesh::EntityRank obj_type, const bool restartOnlyIOPart ) +{ + if (is_fmwk) + { + stk::mesh::Part & part = fmwk_iopart(name, obj_type); + if (restartOnlyIOPart) + { + myRestartOnlyIOParts.push_back(&part); + } + assign_part_id(part); + return part; + } + + stk::mesh::Part & part = my_meta.declare_part(name, obj_type); + + if (restartOnlyIOPart) + { + myRestartOnlyIOParts.push_back(&part); + } + else + { + if (!stk::io::is_part_io_part(part)) + { + stk::io::put_io_part_attribute(part); + } + } + + assign_part_id(part); + + return part; +} + +stk::mesh::Part& +AuxMetaData::declare_io_part_with_topology( const std::string& name, const stk::topology topology, const bool restartOnlyIOPart ) +{ + if (is_fmwk) + { + stk::mesh::Part & root_part = my_meta.get_topology_root_part(topology); + stk::mesh::Part & part = fmwk_iopart(name, root_part.primary_entity_rank()); + my_meta.declare_part_subset(root_part, part); + if (restartOnlyIOPart) + { + myRestartOnlyIOParts.push_back(&part); + } + assign_part_id(part); + return part; + } + + stk::mesh::Part & part = my_meta.declare_part_with_topology(name, topology); + + if (restartOnlyIOPart) + { + myRestartOnlyIOParts.push_back(&part); + } + else + { + if (!stk::io::is_part_io_part(part)) + { + stk::io::put_io_part_attribute(part); + } + } + + assign_part_id(part); + + return part; +} + +FieldRef +AuxMetaData::declare_field( + const std::string & fld_name, + const FieldType & field_type, + const stk::mesh::EntityRank entity_rank, + const unsigned num_states) +{ + stk::mesh::FieldBase * field = NULL; + const std::type_info & value_type = field_type.type_info(); + if (value_type == typeid(int)) + field = &my_meta.declare_field< stk::mesh::Field >(entity_rank, fld_name, num_states); + else if (value_type == typeid(double)) + field = &my_meta.declare_field< stk::mesh::Field >(entity_rank, fld_name, num_states); + else if (value_type == typeid(unsigned)) + field = &my_meta.declare_field< stk::mesh::Field >(entity_rank, fld_name, num_states); + else if (value_type == typeid(int64_t)) + field = &my_meta.declare_field< stk::mesh::Field >(entity_rank, fld_name, num_states); + else if (value_type == typeid(uint64_t)) + field = &my_meta.declare_field< stk::mesh::Field >(entity_rank, fld_name, num_states); + else { + ThrowRequireMsg(false, "Unhandled primitive type " << value_type.name()); + } + + return FieldRef(field); +} + +FieldRef +AuxMetaData::register_field( + const std::string & fld_name, + const FieldType & field_type, + const stk::mesh::EntityRank entity_rank, + const unsigned num_states, + const unsigned dimension, + const stk::mesh::Part & part, + const void * value_type_init) +{ + // don't rely on induction of non-ranked superset parts + // Note that we are only checking nodal fields here, but this could also be a problem if we are counting on a non-ranked superset part to be induced on faces or edges (unlikely?). + //ThrowRequire(entity_rank != stk::topology::NODE_RANK || !is_unranked_superset_part(part)); + + if (is_fmwk) + { + return FieldRef(fmwk_register_field(fld_name, field_type.name(), field_type.type_info(), field_type.dimension(), entity_rank, num_states, dimension, part, value_type_init)); + } + + const unsigned field_length = field_type.dimension()*dimension; + if (field_type.name() == FieldType::VECTOR_2D.name()) + { + auto & field = my_meta.declare_field< stk::mesh::Field >(entity_rank, fld_name, num_states); + stk::mesh::put_field_on_mesh(field, part, field_length, nullptr); + return FieldRef(field); + } + else if (field_type.name() == FieldType::VECTOR_3D.name()) + { + auto & field = my_meta.declare_field< stk::mesh::Field >(entity_rank, fld_name, num_states); + stk::mesh::put_field_on_mesh(field, part, field_length, nullptr); + return FieldRef(field); + } + + FieldRef field = declare_field(fld_name, field_type, entity_rank, num_states); + stk::mesh::put_field_on_mesh(field.field(), part, field_length, value_type_init); + return field; +} + +void +AuxMetaData::induce_topology_nodesets(stk::mesh::Selector selector) const +{ + if (fn_induce_topology_nodesets) + { + fn_induce_topology_nodesets(selector); + } + // no-op if inducer is not set + return; +} + +stk::topology +AuxMetaData::get_nodal_field_topology( const stk::mesh::FieldBase & field, stk::mesh::Entity entity ) const +{ + return get_nodal_field_topology(field, my_meta.mesh_bulk_data().bucket(entity)); +} + +stk::topology AuxMetaData::get_nodal_field_topology( const stk::mesh::FieldBase & field, const stk::mesh::Bucket & bucket ) const +{ + if (fn_get_nodal_field_topology) + { + return fn_get_nodal_field_topology(field, bucket); + } + // return bucket topology if inducer is not set and field is defined on bucket + const stk::mesh::Selector field_selector = stk::mesh::selectField(field); + stk::topology field_topology = stk::topology::INVALID_TOPOLOGY; + if (field_selector(bucket)) field_topology = bucket.topology(); + return field_topology; +} + +stk::mesh::Selector AuxMetaData::selectField( const stk::mesh::FieldBase & field, const stk::mesh::EntityRank target_rank ) const +{ + if (fn_selectField && field.entity_rank() == stk::topology::NODE_RANK) + { + return fn_selectField( field, target_rank ); + } + // return stk::mesh::selectField if inducer is not set + return stk::mesh::selectField(field); +} + +//---------------------------------------------------------------------- + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_AuxMetaData.hpp b/packages/krino/krino/krino_lib/Akri_AuxMetaData.hpp new file mode 100644 index 000000000000..e7590663a4be --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_AuxMetaData.hpp @@ -0,0 +1,180 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_AuxMetaData_h +#define Akri_AuxMetaData_h + +#include +#include // for operator<<, basic_string, etc +#include "stk_mesh/base/FieldState.hpp" // for FieldState +#include "stk_mesh/base/Selector.hpp" // for Selector +#include "stk_mesh/base/Entity.hpp" // for Entity + +namespace krino { class FieldRef; } + +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 9)) || (__GNUC__ == 5) +// Looks like there is an issue with these compilers +// related to instatiating std::function with an incomplete +// type leading to undefine behavior +#include "stk_mesh/base/Part.hpp" // for Part +#include "stk_mesh/base/MetaData.hpp" +#include "stk_mesh/base/FieldBase.hpp" // for FieldState +#else +namespace stk { namespace mesh { class MetaData; } } +namespace stk { namespace mesh { class Part; } } +namespace stk { namespace mesh { class FieldBase; } } +#endif + +namespace krino +{ + +class FieldType { +public: + static const FieldType UNSIGNED_INTEGER_64; + static const FieldType UNSIGNED_INTEGER; + static const FieldType INTEGER; + static const FieldType REAL; + + static const FieldType VECTOR_2D; + static const FieldType VECTOR_3D; + static const FieldType MATRIX_22; + static const FieldType MATRIX_33; + + const std::string & name() const { return my_name; } + const std::type_info & type_info() const { return my_type_info; } + unsigned dimension() const { return my_dimension; } + + FieldType(const std::string & nme, const std::type_info & type, const unsigned dim) : my_name(nme), my_type_info(type), my_dimension(dim) {} + +private: + const std::string my_name; + const std::type_info & my_type_info; + const unsigned my_dimension; +}; + +class AuxMetaData final +{ +public: + static AuxMetaData & get(const stk::mesh::MetaData & stk_meta); + + static AuxMetaData & create(stk::mesh::MetaData & stk_meta); // must be called before calling get + + AuxMetaData ( const AuxMetaData & ) = delete; + AuxMetaData & operator= ( const AuxMetaData & ) = delete; + + bool using_fmwk() const { return is_fmwk; } + + stk::mesh::Part & active_part() const { return *my_active_part; } + stk::mesh::Part & block_boundary_part() const; + stk::mesh::Part & exposed_boundary_part() const; + + const stk::mesh::Selector & active_not_ghost_selector() const { return my_active_not_ghost_selector; } + const stk::mesh::Selector & active_locally_owned_selector() const { return my_active_locally_owned_selector; } + const stk::mesh::Selector & active_globally_shared_selector() const { return my_active_globally_shared_selector; } + + bool get_force_64bit_flag() const { return my_force_64bit_flag; } + void clear_force_64bit_flag() { my_force_64bit_flag = false; } + bool get_assert_32bit_flag() { return my_assert_32bit_flag; } + void set_assert_32bit_flag() { my_force_64bit_flag = false; my_assert_32bit_flag = true; } + + FieldRef declare_field( + const std::string & fld_name, + const FieldType & field_type, + const stk::mesh::EntityRank entity_rank, + const unsigned num_states); + + FieldRef register_field( + const std::string & fld_name, + const FieldType & field_type, + const stk::mesh::EntityRank entity_rank, + const unsigned num_states, + const unsigned dimension, + const stk::mesh::Part & part, + const void * value_type_init = nullptr); + + void assign_part_id(stk::mesh::Part& part); + stk::mesh::Part & declare_io_part(const std::string & name, stk::mesh::EntityRank entityRank, const bool restartOnlyIOPart=false); + stk::mesh::Part & declare_io_part_with_topology(const std::string & name, const stk::topology topology, const bool restartOnlyIOPart=false); + + const std::vector & get_restart_only_io_parts() const { return myRestartOnlyIOParts; } + + bool has_part( const std::string& name ) const; + stk::mesh::Part& get_part( const std::string& name ) const; + + void define_part_alias( stk::mesh::Part & part, const std::string & alias ); + + bool has_field( const stk::mesh::EntityRank obj_type, const std::string& name ) const; + bool has_field( const stk::mesh::EntityRank obj_type, const std::string& name, stk::mesh::FieldState state ) const; + + FieldRef get_field( const stk::mesh::EntityRank obj_type, const std::string& name ) const; + FieldRef get_field( const stk::mesh::EntityRank obj_type, const std::string& name, stk::mesh::FieldState state ) const; + + void induce_topology_nodesets(stk::mesh::Selector selector = stk::mesh::Selector()) const; + stk::topology get_nodal_field_topology( const stk::mesh::FieldBase & field, stk::mesh::Entity entity ) const; + stk::topology get_nodal_field_topology( const stk::mesh::FieldBase & field, const stk::mesh::Bucket & bucket ) const; + stk::mesh::Selector selectField( const stk::mesh::FieldBase & field, const stk::mesh::EntityRank target_rank ) const; + bool is_cell_edge(stk::mesh::Entity node0, stk::mesh::Entity node1) const { return fn_is_cell_edge(node0, node1); } + + void set_fmwk_functions( + const std::function & in_fmwk_get_iopart, + const std::function & in_fmwk_iopart, + const std::function & in_fmwk_define_iopart_alias, + const std::function & in_fmwk_register_field, + stk::mesh::Part * in_exposed_boundary_part, + stk::mesh::Part * in_block_boundary_part); + void set_inducer_functions( + const std::function & in_inducer_induce_topology_nodesets, + const std::function & in_inducer_get_nodal_field_topology, + const std::function & in_inducer_selectField); + void set_is_cell_edge_function(const std::function & in_is_cell_edge) { fn_is_cell_edge = in_is_cell_edge; } + +private: + explicit AuxMetaData(stk::mesh::MetaData & stk_meta); +private: + stk::mesh::MetaData & my_meta; + bool is_fmwk; + bool my_assert_32bit_flag; + bool my_force_64bit_flag; + stk::mesh::Part * my_active_part; + stk::mesh::Part * my_exposed_boundary_part; + stk::mesh::Part * my_block_boundary_part; + stk::mesh::Selector my_active_not_ghost_selector; + stk::mesh::Selector my_active_locally_owned_selector; + stk::mesh::Selector my_active_globally_shared_selector; + std::function fmwk_get_iopart; + std::function fmwk_iopart; + std::function fmwk_define_iopart_alias; + std::function fmwk_register_field; + std::function fn_induce_topology_nodesets; + std::function fn_get_nodal_field_topology; + std::function fn_selectField; + std::function fn_is_cell_edge; + std::vector myRestartOnlyIOParts; +}; + +} // namespace krino + +#endif // Akri_AuxMetaData_h diff --git a/packages/krino/krino/krino_lib/Akri_BoundingBox.cpp b/packages/krino/krino/krino_lib/Akri_BoundingBox.cpp new file mode 100644 index 000000000000..f456350ca065 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_BoundingBox.cpp @@ -0,0 +1,214 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include + +#include +#include +#include + +#include + +namespace krino{ + +template +void +BoundingBox_T::pad_epsilon() +{ + if (!valid()) return; + + const double eps = std::numeric_limits::epsilon(); + + for (int i = 0; i < DIM; i++ ) + { + min[i] -= std::abs(min[i])*eps; + max[i] += std::abs(max[i])*eps; + } +} + +template +void +BoundingBox_T::gather_bboxes( const BoundingBox_T & local_bbox, + std::vector < BoundingBox_T > & all_bboxes ) +{ /* %TRACE% */ /* %TRACE% */ + + // + // globally communicate the bounding box sizes for all procs + // + + all_bboxes.clear(); + all_bboxes.reserve( stk::EnvData::parallel_size() ); + + // put bbox data in Real vector for communication + + std::vector bbox_data( 2*DIM ); + for (int i = 0, j = 0; i < DIM; i++ ) + { + bbox_data[j++] = local_bbox.min[i]; + bbox_data[j++] = local_bbox.max[i]; + } + + std::vector bboxes_data( 2*DIM * stk::EnvData::parallel_size() ); + stk::parallel_vector_concat(stk::EnvData::parallel_comm(), bbox_data, bboxes_data); + + VecType remote_min, remote_max; + for ( int i = 0; i < stk::EnvData::parallel_size(); i++ ) + { + int index = 2*DIM * i; + for (int j = 0; j < DIM; j++ ) + { + remote_min[j] = bboxes_data[index++]; + remote_max[j] = bboxes_data[index++]; + } + all_bboxes.emplace_back( remote_min, remote_max ); + } +} + +template +void +BoundingBox_T::global_reduce() +{ + const VecType local_min = min; + const VecType local_max = max; + + stk::all_reduce_min(stk::EnvData::parallel_comm(), local_min.data(), min.data(), 3); + stk::all_reduce_max(stk::EnvData::parallel_comm(), local_max.data(), max.data(), 3); +} + +template +REAL +BoundingBox_T::SqrDistLowerBnd( const VecType & pt ) const +{ + // make sure bbox is valid + ThrowAssert( valid() ); + + Real delta, SqrDist = 0.; + + for ( int i = 0; i < DIM; i++ ) + { + if ( pt[i] < min[i] ) + { + delta = min[i] - pt[i]; + SqrDist += delta * delta; + } + else if ( pt[i] > max[i] ) + { + delta = pt[i] - max[i]; + SqrDist += delta * delta; + } + } + return ( SqrDist ); +} + +template +REAL +BoundingBox_T::SqrDistUpperBnd( const VecType & pt ) const +{ /* %TRACE% */ /* %TRACE% */ +// make sure bbox is valid + ThrowAssert( valid() ); + + // We are guaranteed that there is a point on the surface on each face of the + // bounding box. So we know that the upper bound for the distance to a face is + // the distance to the farthest point on that face. So the upper bound for this + // bounding box is the minimum of the upper bounds for each face. In other words, + // the upper bound is the minimum distance to the farthest point on each face. + + VecType close_pt; + VecType far_pt; + + for ( int i = 0; i < DIM; i++ ) + { + if ( pt[i] < min[i] ) + { + close_pt[i] = min[i]; + far_pt[i] = max[i]; + } + else if ( pt[i] > max[i] ) + { + close_pt[i] = max[i]; + far_pt[i] = min[i]; + } + else + { + if (pt[i]-min[i] < max[i]-pt[i]) + { + close_pt[i] = min[i]; + far_pt[i] = max[i]; + } + else + { + close_pt[i] = max[i]; + far_pt[i] = min[i]; + } + } + } + + Real SqrDistMin; + if (3 == DIM) + { + SqrDistMin = (pt-VecType(close_pt[0],far_pt[1],far_pt[2])).length_squared(); + SqrDistMin = std::min(SqrDistMin,(pt-VecType(far_pt[0],close_pt[1],far_pt[2])).length_squared()); + SqrDistMin = std::min(SqrDistMin,(pt-VecType(far_pt[0],far_pt[1],close_pt[2])).length_squared()); + } + else + { + ThrowAssert(2 == DIM); + const Real zero = 0.0; + SqrDistMin = (pt-VecType(close_pt[0],far_pt[1],zero)).length_squared(); + SqrDistMin = std::min(SqrDistMin,(pt-VecType(far_pt[0],close_pt[1],zero)).length_squared()); + } + return ( SqrDistMin ); +} + +template +REAL +BoundingBox_T::SqrDistUpperBnd( const BoundingBox_T & pt_box ) const +{ /* %TRACE% */ /* %TRACE% */ + // This is somewhat conservative. More aggressive methods might be able to reduce this estimate + // while still always being an upper bound. + // Here we just estimate the upper bound of this distance from any point in pt_bbox to the + // surface contained in *this by the following: + // Loop the faces of *this. + // Find the maximum distance from any point in pt_bbox to any point of the face. + // Take the minimum of these maximum face distances. + + if( !pt_box.valid() ) + return std::numeric_limits::max(); + + // make sure bbox is valid + ThrowAssert( valid() ); + + Real SqrDistMin = 0.0; + for ( int j = 0; j < DIM; j++ ) + { + for ( int side = 0; side < 2; ++side) + { + Real SqrDistSideMin = 0.0; + for ( int i = 0; i < DIM; i++ ) + { + const Real delta = (i!=j) ? std::max(pt_box.max[i]-min[i],max[i]-pt_box.min[i]) : + ((side==0) ? std::max(min[i]-pt_box.min[i], pt_box.max[i]-min[i]) : std::max(max[i]-pt_box.min[i], pt_box.max[i]-max[i])); + ThrowAssert(delta >= 0.0); + SqrDistSideMin += delta*delta; + } + if (SqrDistMin==0.0 || SqrDistSideMin; +template class BoundingBox_T; + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_BoundingBox.hpp b/packages/krino/krino/krino_lib/Akri_BoundingBox.hpp new file mode 100644 index 000000000000..e64ce337df88 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_BoundingBox.hpp @@ -0,0 +1,212 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_BoundingBox_h +#define Akri_BoundingBox_h + +#include + +#include + +#include +#include + +namespace krino { + +template +class BoundingBox_T { + +public: + typedef REAL Real; + typedef Vec VecType; + +private: + VecType min; + VecType max; + +public: + static void gather_bboxes( const BoundingBox_T & local_bbox, + std::vector< BoundingBox_T > & all_bboxes ); + + void global_reduce(); + + template + void accommodate( const VECTYPE & pt ); + + template + void accommodate( const BoundingBox_T & bbox ); + + const VecType & get_min() const { return min; } + const VecType & get_max() const { return max; } + + template + bool contains( const VECTYPE & pt ) const; + + // Lower bound on the square of the distance from the point pt to surfaces contained in *this + Real SqrDistLowerBnd( const VecType & pt ) const; + + // Upper bound on the square of the distance from the point pt to surfaces contained in *this + Real SqrDistUpperBnd( const VecType & pt ) const; + + // Upper bound on the square of the distance from any point in pt_bbox to surfaces contained in *this + Real SqrDistUpperBnd( const BoundingBox_T & pt_box ) const; + + void pad( const Real & dist ); + void pad_epsilon(); + + void scale( const Real & fraction ); + + Real SqrSize() const; + + bool valid() const + { + bool is_not_valid = (min[0]>max[0]); + return !is_not_valid; + } + + bool intersects( const BoundingBox_T & bbox ) const; + bool contains( const BoundingBox_T & bbox ) const; + + int max_span_direction() const; + + VecType center() const { return Real(0.5)*(min+max); } + + void clear() { + min = VecType(std::numeric_limits::max(),std::numeric_limits::max(),std::numeric_limits::max()); + max = VecType(-std::numeric_limits::max(),-std::numeric_limits::max(),-std::numeric_limits::max()); + } + + template + BoundingBox_T( const VECTYPE & pt_min, + const VECTYPE & pt_max ) + : min(pt_min[0], pt_min[1], pt_min[2]), + max(pt_max[0], pt_max[1], pt_max[2]) {} + + BoundingBox_T() + : min(std::numeric_limits::max(),std::numeric_limits::max(),std::numeric_limits::max()), + max(-std::numeric_limits::max(),-std::numeric_limits::max(),-std::numeric_limits::max()) {} +}; + +template +template +inline void +BoundingBox_T::accommodate( const VECTYPE & pt ) +{ + for ( int i = 0; i < DIM; ++i ) + { + if ( pt[i] < min[i] ) min[i] = pt[i]; + if ( pt[i] > max[i] ) max[i] = pt[i]; + } +} + +template +template +inline void +BoundingBox_T::accommodate( const BoundingBox_T & bbox ) +{ + for ( int i = 0; i < DIM; ++i ) + { + if ( bbox.get_min()[i] < min[i] ) min[i] = bbox.get_min()[i]; + if ( bbox.get_max()[i] > max[i] ) max[i] = bbox.get_max()[i]; + } +} + +template +template +inline bool +BoundingBox_T::contains( const VECTYPE & pt ) const +{ + for ( int i = 0; i < DIM; ++i ) + { + if ( pt[i] < min[i] || pt[i] > max[i] ) return false; + } + return true; +} + +template +inline bool +BoundingBox_T::intersects( const BoundingBox_T & bbox ) const +{ + for ( int i = 0; i < DIM; ++i ) + { + if ( bbox.max[i] < min[i] || bbox.min[i] > max[i] ) return false; + } + return true; +} + +template +inline bool +BoundingBox_T::contains( const BoundingBox_T & bbox ) const +{ + for ( int i = 0; i < DIM; ++i ) + { + if ( bbox.min[i] < min[i] || bbox.max[i] > max[i] ) return false; + } + return true; +} + +template +inline int +BoundingBox_T::max_span_direction() const +{ + int max_dir = 0; + for ( int i = 1; i < DIM; ++i ) + { + if ( max[i] - min[i] > max[max_dir] - min[max_dir] ) max_dir = i; + } + return max_dir; +} + +template +inline REAL +BoundingBox_T::SqrSize() const +{ + Real sqrSize = 0.0; + if (valid()) + { + for ( int i = 0; i < DIM; ++i ) + { + sqrSize += (max[i]-min[i])*(max[i]-min[i]); + } + } + return sqrSize; +} + +template +inline void +BoundingBox_T::pad( const Real & dist ) +{ + if (!valid()) return; + + VecType extension; + for (int i = 0; i < DIM; i++ ) + extension[i] = dist; + + min -= extension; + max += extension; +} + +template +inline void +BoundingBox_T::scale( const Real & scale_factor ) +{ + if (!valid()) return; + + Real fraction = 0.5 * ( scale_factor - 1. ); + VecType extension; + for (int i = 0; i < DIM; i++ ) + extension[i] = fraction * ( max[i] - min[i] ); + min -= extension; + max += extension; +} + +typedef BoundingBox_T BoundingBox; + +} // namespace krino + +#endif // Akri_BoundingBox_h diff --git a/packages/krino/krino/krino_lib/Akri_BoundingBoxMesh.cpp b/packages/krino/krino/krino_lib/Akri_BoundingBoxMesh.cpp new file mode 100644 index 000000000000..32bd6e2b02b6 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_BoundingBoxMesh.cpp @@ -0,0 +1,697 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace krino{ + +BoundingBoxMesh::BoundingBoxMesh(stk::topology element_topology, const std::vector & entity_rank_names) +: m_element_topology(element_topology), m_nx(0), m_ny(0), m_nz(0) +{ + ThrowRequire(element_topology == stk::topology::TRIANGLE_3_2D || + element_topology == stk::topology::QUADRILATERAL_4_2D || + element_topology == stk::topology::TETRAHEDRON_4 || + element_topology == stk::topology::HEXAHEDRON_8); + + m_meta = std::make_unique(element_topology.dimension(), entity_rank_names); + AuxMetaData & aux_meta = AuxMetaData::create(*m_meta); + stk::mesh::Part & block_part = m_meta->declare_part_with_topology( "block_1", element_topology ); + stk::io::put_io_part_attribute(block_part); + m_elem_parts.push_back(&block_part); + m_elem_parts.push_back(&aux_meta.active_part()); + m_node_parts.push_back(&aux_meta.active_part()); + + declare_domain_side_parts(block_part); +} + +void +BoundingBoxMesh::set_domain(const BoundingBoxType & mesh_bbox, const double mesh_size, const int pad_size) +{ + m_mesh_bbox = mesh_bbox; + const Vector3d padding(0.5*mesh_size*pad_size, 0.5*mesh_size*pad_size, 0.5*mesh_size*pad_size); + m_mesh_bbox = BoundingBoxType(mesh_bbox.get_min() - padding, mesh_bbox.get_max() + padding); + + const typename BoundingBoxType::VecType min = m_mesh_bbox.get_min(); + const typename BoundingBoxType::VecType max = m_mesh_bbox.get_max(); + const typename BoundingBoxType::VecType span = max-min; + m_nx = (size_t) (0.5 + span[0] / mesh_size); + const bool isTriangularLattice = (TRIANGULAR_LATTICE_BOUNDING_BOX_MESH == myMeshStructureType || FLAT_WALLED_TRIANGULAR_LATTICE_BOUNDING_BOX_MESH == myMeshStructureType); + const double dy = isTriangularLattice ? (0.5*mesh_size*std::sqrt(3.0)) : mesh_size; + m_ny = (size_t) (0.5 + span[1] / dy); + m_nz = (m_element_topology.dimension() == 2) ? 1 : ((size_t) (0.5 + span[2] / mesh_size)); + + if (m_element_topology.dimension() == 2) + { + krinolog << "Generated mesh with for domain " + << "(min,max) = ((" << min[0] << "," << min[1] << "),("<< max[0] << "," << max[1] << ")" + << ", (nx,ny) = (" << m_nx << "," << m_ny << ")" << stk::diag::dendl; + } + else + { + krinolog << "Generated mesh with for domain " + << "(min,max) = ((" << min[0] << "," << min[1] << "," << min[2] << "),("<< max[0] << "," << max[1] << "," << max[2] << ")" + << ", (nx,ny,nz) = (" << m_nx << "," << m_ny << "," << m_nz << ")" << stk::diag::dendl; + } + +} + +void BoundingBoxMesh::set_is_cell_edge_function_for_cell_based_mesh() const +{ + const std::array N = {m_nx, m_ny, m_nz}; + const int dim = m_meta->spatial_dimension(); + ThrowRequireMsg(m_mesh, "Cannot call set_is_cell_edge_function() before BulkData is created."); + const stk::mesh::BulkData & mesh = *m_mesh; + + auto is_cell_edge = [N,dim,&mesh](stk::mesh::Entity node0, stk::mesh::Entity node1) + { + const auto node0Indices = get_node_x_y_z(mesh.identifier(node0), N); + const auto node1Indices = get_node_x_y_z(mesh.identifier(node1), N); + if (dim == 2) + { + return node0Indices[0] == node1Indices[0] || node0Indices[1] == node1Indices[1]; + } + const bool matchX = node0Indices[0] == node1Indices[0]; + const bool matchY = node0Indices[1] == node1Indices[1]; + const bool matchZ = node0Indices[2] == node1Indices[2]; + return (matchX && matchY) || (matchX && matchZ) || (matchY && matchZ); + }; + + AuxMetaData & aux_meta = AuxMetaData::get(*m_meta); + aux_meta.set_is_cell_edge_function(is_cell_edge); +} + +void BoundingBoxMesh::set_is_cell_edge_function_for_BCC_mesh() const +{ + auto is_cell_edge = [](stk::mesh::Entity node0, stk::mesh::Entity node1) + { + return true; + }; + + AuxMetaData & aux_meta = AuxMetaData::get(*m_meta); + aux_meta.set_is_cell_edge_function(is_cell_edge); +} + +void +BoundingBoxMesh::populate_mesh(stk::ParallelMachine pm, const stk::mesh::BulkData::AutomaticAuraOption auto_aura_option) +{ /* %TRACE[ON]% */ Trace trace__("krino::BoundingBoxMesh::populate_mesh()"); /* %TRACE% */ + ThrowRequireMsg(m_mesh_bbox.valid(), "Must call set_domain() before populate_mesh()"); + m_mesh = std::make_unique(*m_meta, pm, auto_aura_option); + if (CUBIC_BOUNDING_BOX_MESH == myMeshStructureType) + populate_cell_based_mesh(); + else if (TRIANGULAR_LATTICE_BOUNDING_BOX_MESH == myMeshStructureType || FLAT_WALLED_TRIANGULAR_LATTICE_BOUNDING_BOX_MESH == myMeshStructureType) + populate_2D_triangular_lattice_based_mesh(); + else if (BCC_BOUNDING_BOX_MESH == myMeshStructureType || FLAT_WALLED_BCC_BOUNDING_BOX_MESH == myMeshStructureType) + populate_BCC_mesh(); + else + ThrowRequireMsg(false, "Unsupported or unrecognized mesh structure type " << myMeshStructureType); +} + +enum BCCNode { BCC_NODE=8, BCC_NODE_XMINUS=9, BCC_NODE_XPLUS=10, BCC_NODE_YMINUS=11, BCC_NODE_YPLUS=12, BCC_NODE_ZMINUS=13, BCC_NODE_ZPLUS=14 }; + +void +BoundingBoxMesh::build_face_tets( size_t cell_id, size_t ix , size_t iy , size_t iz, int iface, const std::vector & cell_node_ids ) +{ + std::vector>> faceTets = { + {{BCC_NODE, BCC_NODE_XMINUS, 0, 4}, + {BCC_NODE, BCC_NODE_XMINUS, 4, 7}, + {BCC_NODE, BCC_NODE_XMINUS, 7, 3}, + {BCC_NODE, BCC_NODE_XMINUS, 3, 0}}, + {{BCC_NODE, BCC_NODE_XPLUS, 1, 2}, + {BCC_NODE, BCC_NODE_XPLUS, 2, 6}, + {BCC_NODE, BCC_NODE_XPLUS, 6, 5}, + {BCC_NODE, BCC_NODE_XPLUS, 5, 1}}, + {{BCC_NODE, BCC_NODE_YMINUS, 0, 1}, + {BCC_NODE, BCC_NODE_YMINUS, 1, 5}, + {BCC_NODE, BCC_NODE_YMINUS, 5, 4}, + {BCC_NODE, BCC_NODE_YMINUS, 4, 0}}, + {{BCC_NODE, BCC_NODE_YPLUS, 2, 3}, + {BCC_NODE, BCC_NODE_YPLUS, 3, 7}, + {BCC_NODE, BCC_NODE_YPLUS, 7, 6}, + {BCC_NODE, BCC_NODE_YPLUS, 6, 2}}, + {{BCC_NODE, BCC_NODE_ZMINUS, 0, 3}, + {BCC_NODE, BCC_NODE_ZMINUS, 3, 2}, + {BCC_NODE, BCC_NODE_ZMINUS, 2, 1}, + {BCC_NODE, BCC_NODE_ZMINUS, 1, 0}}, + {{BCC_NODE, BCC_NODE_ZPLUS, 4, 5}, + {BCC_NODE, BCC_NODE_ZPLUS, 5, 6}, + {BCC_NODE, BCC_NODE_ZPLUS, 6, 7}, + {BCC_NODE, BCC_NODE_ZPLUS, 7, 4}} + }; + + const stk::mesh::EntityId elem_id_start = 1; + const int num_elem_per_face = 4; + const int num_faces_per_cell = 6; + const int num_nodes_per_elem = 4; + std::vector elemNodes(num_nodes_per_elem); + + const auto & faceTet = faceTets[iface]; + for (int ielem = 0; ielem < num_elem_per_face; ++ielem) + { + stk::mesh::EntityId elem_id = num_elem_per_face*num_faces_per_cell*cell_id + num_elem_per_face*iface + ielem + elem_id_start; // Uses twice as many IDs as necessary + for (int elem_node_index = 0; elem_node_indexcoordinate_field(); + stk::mesh::EntityId nodeId = get_node_id(ix, iy, iz); + stk::mesh::Entity const node = m_mesh->get_entity( stk::topology::NODE_RANK, nodeId ); + if (m_mesh->is_valid(node)) + { + m_mesh->change_entity_parts(node, m_node_parts); + + double * coord_data = static_cast(stk::mesh::field_data( *coord_field, node )); + my_coord_mapping->get_node_coordinates(coord_data, m_meta->spatial_dimension(), {ix, iy, iz}); + } +} + +void +BoundingBoxMesh::setup_BCC_node( size_t ix , size_t iy , size_t iz, int dx, int dy, int dz ) +{ + stk::mesh::FieldBase const* coord_field = m_meta->coordinate_field(); + stk::mesh::EntityId nodeId = get_BCC_node_id(ix, iy, iz, dx, dy, dz); + stk::mesh::Entity const node = m_mesh->get_entity( stk::topology::NODE_RANK, nodeId ); + if (m_mesh->is_valid(node)) + { + m_mesh->change_entity_parts(node, m_node_parts); + + const bool flattenBoundaries = FLAT_WALLED_BCC_BOUNDING_BOX_MESH == myMeshStructureType; + double * coord_data = static_cast(stk::mesh::field_data( *coord_field, node )); + my_coord_mapping->get_BCC_node_coordinates(coord_data, m_meta->spatial_dimension(), flattenBoundaries, {ix, iy, iz}, {dx, dy, dz}); + } +} + +size_t get_triangle_lattice_node_id(size_t ix, size_t iy, size_t nx, size_t ny) +{ + return 1 + iy*(nx+1) + ix + ((iy%2 == 0) ? (iy/2) : ((iy-1)/2)); +} + +void fill_triangle_lattice_node_indices(const size_t ix, const size_t iy, std::array,3> & triNodeIndices) +{ + if (iy%2 == 0) + { + if (ix%2 == 0) + triNodeIndices = {{ {{ix/2,iy}}, {{ix/2+1,iy+1}}, {{ix/2,iy+1}} }}; + else + triNodeIndices = {{ {{(ix-1)/2,iy}}, {{(ix-1)/2+1,iy}}, {{(ix-1)/2+1,iy+1}} }}; + } + else + { + if (ix%2 == 0) + triNodeIndices = {{ {{ix/2,iy}}, {{ix/2+1,iy}}, {{ix/2,iy+1}} }}; + else + triNodeIndices = {{ {{(ix-1)/2+1,iy}}, {{(ix-1)/2+1,iy+1}}, {{(ix-1)/2,iy+1}} }}; + } +} + +void +BoundingBoxMesh::populate_2D_triangular_lattice_based_mesh() +{ /* %TRACE[ON]% */ Trace trace__("krino::BoundingBoxMesh::populate_2D_triangular_lattice_based_mesh()"); /* %TRACE% */ + ThrowRequire(m_mesh); + + const int p_size = m_mesh->parallel_size(); + const int p_rank = m_mesh->parallel_rank(); + stk::mesh::FieldBase const* coord_field = m_meta->coordinate_field(); + my_coord_mapping = std::make_unique(m_nx, m_ny, m_nz, m_mesh_bbox); + + ThrowRequire(m_element_topology == stk::topology::TRIANGLE_3_2D); + const bool flattenBoundaries = FLAT_WALLED_TRIANGULAR_LATTICE_BOUNDING_BOX_MESH == myMeshStructureType; + + const size_t NelemPerSlabInY = (2*m_nx+1); + const size_t Nelem = m_ny*NelemPerSlabInY; + const size_t beg_elem = ( Nelem * p_rank ) / p_size ; + const size_t end_elem = ( Nelem * ( p_rank + 1 ) ) / p_size ; + + const size_t num_local_cells = end_elem-beg_elem; + krinolog << "BoundingBoxMesh creating " << num_local_cells << " local cells." << stk::diag::dendl; + + std::vector triNodes(3); + std::array,3> triNodeIndices; + + m_mesh->modification_begin(); + + { + for (size_t elemIndex=beg_elem; elemIndex!=end_elem; ++elemIndex) + { + const size_t iy = elemIndex / NelemPerSlabInY; + const size_t ix = elemIndex - iy*NelemPerSlabInY; + + fill_triangle_lattice_node_indices(ix, iy, triNodeIndices); + + triNodes = {get_triangle_lattice_node_id(triNodeIndices[0][0], triNodeIndices[0][1], m_nx, m_ny), + get_triangle_lattice_node_id(triNodeIndices[1][0], triNodeIndices[1][1], m_nx, m_ny), + get_triangle_lattice_node_id(triNodeIndices[2][0], triNodeIndices[2][1], m_nx, m_ny)}; + + stk::mesh::declare_element( *m_mesh, m_elem_parts, elemIndex+1, triNodes ); + + for (int n=0; n<3; ++n) + { + stk::mesh::Entity const node = m_mesh->get_entity( stk::topology::NODE_RANK , triNodes[n] ); + m_mesh->change_entity_parts(node, m_node_parts); + + double * coord_data = static_cast(stk::mesh::field_data( *coord_field, node )); + + my_coord_mapping->get_triangle_lattice_node_coordinates(coord_data, flattenBoundaries, {{triNodeIndices[n][0], triNodeIndices[n][1]}}); + } + } + stk::tools::fix_node_sharing_via_search(*m_mesh); + } + m_mesh->modification_end(); + + std::vector counts; + stk::mesh::count_entities( m_meta->locally_owned_part(), *m_mesh, counts ); + krinolog << "Generated mesh with " << counts[stk::topology::ELEM_RANK] << " local elements and " << counts[stk::topology::NODE_RANK] << " local nodes." << stk::diag::dendl; +} + +void +BoundingBoxMesh::populate_BCC_mesh() +{ /* %TRACE[ON]% */ Trace trace__("krino::BoundingBoxMesh::populate_BCC_mesh()"); /* %TRACE% */ + ThrowRequire(m_mesh); + set_is_cell_edge_function_for_BCC_mesh(); + + const int p_size = m_mesh->parallel_size(); + const int p_rank = m_mesh->parallel_rank(); + + std::vector> hex_cell_node_locations = { {0,0,0}, {1,0,0}, {1,1,0}, {0,1,0}, {0,0,1}, {1,0,1}, {1,1,1}, {0,1,1} }; + std::vector>> BCC_cell_neighbors = { {BCC_NODE,{0,0,0}}, + {BCC_NODE_XMINUS,{-1,0,0}}, {BCC_NODE_XPLUS,{+1,0,0}}, + {BCC_NODE_YMINUS,{0,-1,0}}, {BCC_NODE_YPLUS,{0,+1,0}}, + {BCC_NODE_ZMINUS,{0,0,-1}}, {BCC_NODE_ZPLUS,{0,0,+1}}}; + + my_coord_mapping = std::make_unique(m_nx, m_ny, m_nz, m_mesh_bbox); + + std::pair proc_cell_range = determine_processor_cells(p_size, p_rank); + const size_t num_local_cells = proc_cell_range.second-proc_cell_range.first; + krinolog << "BoundingBoxMesh creating " << num_local_cells << " local cells." << stk::diag::dendl; + + const int num_nodes_per_cell = 8; + const int num_nodes_per_cell_plus_BCC_nodes = 15; + std::vector cell_node_ids(num_nodes_per_cell_plus_BCC_nodes); + + m_mesh->modification_begin(); + + { + size_t count = 0; + for (size_t cell_id=proc_cell_range.first; cell_id!=proc_cell_range.second; ++cell_id) + { + size_t ix = 0, iy = 0, iz = 0; + get_cell_x_y_z(cell_id, ix, iy, iz); + + if ( count != 0 && count % 1000000 == 0) + { + krinolog << "Creating local cell " << count << " ." << stk::diag::dendl; + } + ++count; + + for (int cell_node_index=0; cell_node_indexmodification_end(); + + std::vector counts; + stk::mesh::count_entities( m_meta->locally_owned_part(), *m_mesh, counts ); + krinolog << "Generated mesh with " << counts[stk::topology::ELEM_RANK] << " local elements and " << counts[stk::topology::NODE_RANK] << " local nodes." << stk::diag::dendl; +} + +void +BoundingBoxMesh::populate_cell_based_mesh() +{ /* %TRACE[ON]% */ Trace trace__("krino::BoundingBoxMesh::populate_cell_based_mesh()"); /* %TRACE% */ + ThrowRequire(m_mesh); + set_is_cell_edge_function_for_cell_based_mesh(); + + const int p_size = m_mesh->parallel_size(); + const int p_rank = m_mesh->parallel_rank(); + const int dim = m_element_topology.dimension(); + stk::mesh::FieldBase const* coord_field = m_meta->coordinate_field(); + + std::vector> hex_cell_node_locations = { {0,0,0}, {1,0,0}, {1,1,0}, {0,1,0}, {0,0,1}, {1,0,1}, {1,1,1}, {0,1,1} }; + std::vector> hex_cell_elem_nodes = {{0, 1, 2, 3, 4, 6, 7}}; + + std::vector> tet_even_cell_elem_nodes = {{0, 1, 2, 5}, + {0, 2, 7, 5}, + {0, 2, 3, 7}, + {0, 5, 7, 4}, + {2, 7, 5, 6}}; + std::vector> tet_odd_cell_elem_nodes = {{0, 1, 3, 4}, + {1, 2, 3, 6}, + {1, 3, 4, 6}, + {3, 4, 6, 7}, + {1, 6, 4, 5}}; + + std::vector> quad_cell_node_locations = { {0,0,0}, {1,0,0}, {1,1,0}, {0,1,0} }; + std::vector> quad_cell_elem_nodes = {{0, 1, 2, 3}}; + + std::vector> tri_even_cell_elem_nodes = {{0, 1, 2}, {0, 2, 3}}; + std::vector> tri_odd_cell_elem_nodes = {{0, 1, 3}, {1, 2, 3}}; + + const std::vector> & cell_node_locations = (dim == 2) ? quad_cell_node_locations : hex_cell_node_locations; + const std::vector> & even_cell_elem_nodes = + (m_element_topology == stk::topology::TRIANGLE_3_2D) ? tri_even_cell_elem_nodes : + ((m_element_topology == stk::topology::QUADRILATERAL_4) ? quad_cell_elem_nodes : + ((m_element_topology == stk::topology::TETRAHEDRON_4) ? tet_even_cell_elem_nodes : + hex_cell_elem_nodes)); + const std::vector> & odd_cell_elem_nodes = + (m_element_topology == stk::topology::TRIANGLE_3_2D) ? tri_odd_cell_elem_nodes : + ((m_element_topology == stk::topology::QUADRILATERAL_4) ? quad_cell_elem_nodes : + ((m_element_topology == stk::topology::TETRAHEDRON_4) ? tet_odd_cell_elem_nodes : + hex_cell_elem_nodes)); + + std::unordered_map> nodes_to_procs; + generate_node_to_processor_map(p_size, p_rank, cell_node_locations, nodes_to_procs); + const int num_elem_per_cell = even_cell_elem_nodes.size(); + const int num_nodes_per_cell = cell_node_locations.size(); + const int num_nodes_per_elem = even_cell_elem_nodes[0].size(); + const stk::mesh::EntityId elem_id_start = 1; + std::vector cell_node_ids(num_nodes_per_cell); + std::vector elem_nodes(num_nodes_per_elem); + + my_coord_mapping = std::make_unique(m_nx, m_ny, m_nz, m_mesh_bbox); + + std::pair proc_cell_range = determine_processor_cells(p_size, p_rank); + const size_t num_local_cells = proc_cell_range.second-proc_cell_range.first; + krinolog << "BoundingBoxMesh creating " << num_local_cells << " local cells." << stk::diag::dendl; + + m_mesh->modification_begin(); + + { + size_t count = 0; + for (size_t cell_id=proc_cell_range.first; cell_id!=proc_cell_range.second; ++cell_id) + { + size_t ix = 0, iy = 0, iz = 0; + get_cell_x_y_z(cell_id, ix, iy, iz); + + const std::vector> & cell_elem_nodes = ((ix+iy+iz)%2 == 0) ? even_cell_elem_nodes : odd_cell_elem_nodes; + + if ( count % 1000000 == 0) + { + krinolog << "Creating local cell " << count << " ." << stk::diag::dendl; + } + ++count; + + for (int cell_node_index=0; cell_node_indexget_entity( stk::topology::NODE_RANK , node_id ); + m_mesh->change_entity_parts(node, m_node_parts); + + auto map_it = nodes_to_procs.find(node_id); + if (map_it != nodes_to_procs.end()) + { + for (auto other_proc : map_it->second) + { + m_mesh->add_node_sharing(node, other_proc); + } + } + + // Compute and assign coordinates to the node + get_node_x_y_z(node_id, ix, iy, iz); + + double * coord_data = static_cast(stk::mesh::field_data( *coord_field, node )); + my_coord_mapping->get_node_coordinates(coord_data, dim, {ix, iy, iz}); + } + } + } + } + m_mesh->modification_end(); + + std::vector counts; + stk::mesh::count_entities( m_meta->locally_owned_part(), *m_mesh, counts ); + krinolog << "Generated mesh with " << counts[stk::topology::ELEM_RANK] << " local elements and " << counts[stk::topology::NODE_RANK] << " local nodes." << stk::diag::dendl; +} + +void +BoundingBoxMesh::declare_domain_side_parts(const stk::mesh::Part & blockPart) +{ + AuxMetaData & aux_meta = AuxMetaData::get(*m_meta); + + stk::topology side_topology = m_element_topology.side_topology(); + + mySideParts.clear(); + mySideParts.push_back(&aux_meta.declare_io_part_with_topology("Xminus", side_topology)); + mySideParts.push_back(&aux_meta.declare_io_part_with_topology("Xplus", side_topology)); + mySideParts.push_back(&aux_meta.declare_io_part_with_topology("Yminus", side_topology)); + mySideParts.push_back(&aux_meta.declare_io_part_with_topology("Yplus", side_topology)); + if (m_meta->spatial_dimension() == 3) + { + mySideParts.push_back(&aux_meta.declare_io_part_with_topology("Zminus", side_topology)); + mySideParts.push_back(&aux_meta.declare_io_part_with_topology("Zplus", side_topology)); + } + + for (auto && sidePart : mySideParts) + m_meta->set_surface_to_block_mapping(sidePart, {&blockPart}); +} + +void BoundingBoxMesh::require_has_flat_boundaries() const +{ + ThrowRequireMsg(has_flat_boundaries(), "Domain sides can only be added for CUBIC or FLAT_WALLED_BC generated meshes."); +} + +static bool equal_within_tol(const double x1, const double x2, const double tol) +{ + return std::abs(x1-x2) < tol; +} + +void +BoundingBoxMesh::create_domain_sides() +{ + if (mySideParts.empty()) + return; + + require_has_flat_boundaries(); + + AuxMetaData & aux_meta = AuxMetaData::get(*m_meta); + stk::mesh::create_exposed_block_boundary_sides(*m_mesh, m_meta->universal_part(), {&aux_meta.exposed_boundary_part()}); + + stk::topology side_topology = m_element_topology.side_topology(); + ThrowRequire(mySideParts.size() >= m_meta->spatial_dimension()*2); + + std::vector sides; + stk::mesh::get_selected_entities( aux_meta.exposed_boundary_part() & m_meta->locally_owned_part(), m_mesh->buckets( m_meta->side_rank() ), sides ); + std::vector add_parts(sides.size()); + std::vector remove_parts(sides.size()); + stk::mesh::FieldBase const* coord_field = m_meta->coordinate_field(); + + const auto & min = m_mesh_bbox.get_min(); + const auto & max = m_mesh_bbox.get_max(); + const double relativeTol = 0.01; // should not be at all sensitive to this tolerance + const std::array tol {(max[0]-min[0])/m_nx*relativeTol, (max[1]-min[1])/m_ny*relativeTol, ((m_meta->spatial_dimension() == 3) ? ((max[2]-min[2])/m_nz*relativeTol) : 0.)}; + + const unsigned num_side_nodes = side_topology.num_nodes(); + for (size_t iside=0; isidebegin_nodes(sides[iside]); + std::array domainSide {-2,-2,-2}; + for (unsigned n=0; n(stk::mesh::field_data( *coord_field, side_nodes[n] )); + for (unsigned i=0; ispatial_dimension(); ++i ) + { + if (equal_within_tol(coords[i],min[i],tol[i])) + { + ThrowRequire(domainSide[i] != 1); + if (domainSide[i] == -2) domainSide[i] = -1; + } + else if (equal_within_tol(coords[i],max[i],tol[i])) + { + ThrowRequire(domainSide[i] != -1); + if (domainSide[i] == -2) domainSide[i] = 1; + } + else + { + domainSide[i] = 0; + } + } + } + + const int num_sides_set = ((domainSide[0]==-1||domainSide[0]==1) ? 1 : 0) + ((domainSide[1]==-1||domainSide[1]==1) ? 1 : 0) + ((domainSide[2]==-1||domainSide[2]==1) ? 1 : 0); + ThrowRequire(num_sides_set == 1); + stk::mesh::Part * side_part = nullptr; + if (domainSide[0] == -1) side_part = mySideParts[0]; + else if (domainSide[0] == 1) side_part = mySideParts[1]; + else if (domainSide[1] == -1) side_part = mySideParts[2]; + else if (domainSide[1] == 1) side_part = mySideParts[3]; + else if (domainSide[2] == -1) side_part = mySideParts[4]; + else if (domainSide[2] == 1) side_part = mySideParts[5]; + + add_parts[iside].push_back(side_part); + } + + m_mesh->batch_change_entity_parts(sides, add_parts, remove_parts); +} + +void +BoundingBoxMesh::get_cell_x_y_z( stk::mesh::EntityId cell_id, size_t &ix , size_t &iy , size_t &iz ) const +{ + ix = cell_id % m_nx; + cell_id /= m_nx; + + iy = cell_id % m_ny; + cell_id /= m_ny; + + iz = cell_id; +} + +std::array +BoundingBoxMesh::get_node_x_y_z( stk::mesh::EntityId entity_id, const std::array & N ) +{ + const size_t node_id_start = 1; + entity_id -= node_id_start; + + std::array indices; + indices[0] = entity_id % (N[0]+1); + entity_id /= (N[0]+1); + + indices[1] = entity_id % (N[1]+1); + entity_id /= (N[2]+1); + + indices[2] = entity_id; + return indices; +} + +void +BoundingBoxMesh::get_node_x_y_z( stk::mesh::EntityId entity_id, size_t &ix , size_t &iy , size_t &iz ) const +{ + auto indices = get_node_x_y_z(entity_id, {{m_nx,m_ny,m_nz}}); + ix = indices[0]; + iy = indices[1]; + iz = indices[2]; +} + +std::pair +BoundingBoxMesh::determine_processor_cells(const int p_size, const int p_rank) const +{ + const size_t Ntot = m_nx*m_ny*m_nz; + const size_t beg_elem = ( Ntot * p_rank ) / p_size ; + const size_t end_elem = ( Ntot * ( p_rank + 1 ) ) / p_size ; + return std::make_pair(beg_elem, end_elem); +} + +void +BoundingBoxMesh::generate_node_to_processor_map(const int p_size, + const int p_rank, + const std::vector> & cell_node_locations, + std::unordered_map> & nodes_to_procs) const +{ + std::unordered_set locally_used_nodes; + + std::pair proc_cell_range = determine_processor_cells(p_size, p_rank); + + // First create set of all nodes used by local elements + for (size_t cell_id=proc_cell_range.first; cell_id!=proc_cell_range.second; ++cell_id) + { + size_t ix = 0, iy = 0, iz = 0; + get_cell_x_y_z(cell_id, ix, iy, iz); + + for (size_t cell_node_index=0; cell_node_index other_proc_cell_range = determine_processor_cells(p_size, other_p_rank); + + for (size_t cell_id=other_proc_cell_range.first; cell_id!=other_proc_cell_range.second; ++cell_id) + { + size_t ix = 0, iy = 0, iz = 0; + get_cell_x_y_z(cell_id, ix, iy, iz); + + for (size_t cell_node_index=0; cell_node_index & node_procs = nodes_to_procs[nodeId]; + if (std::find(node_procs.begin(), node_procs.end(), other_p_rank) == node_procs.end()) + { + node_procs.push_back(other_p_rank); + } + } + } + } + } +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_BoundingBoxMesh.hpp b/packages/krino/krino/krino_lib/Akri_BoundingBoxMesh.hpp new file mode 100644 index 000000000000..45572ed5ea79 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_BoundingBoxMesh.hpp @@ -0,0 +1,157 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_BoundingBoxMesh_h +#define Akri_BoundingBoxMesh_h + +#include +#include +#include +#include +#include + +namespace krino { + +class CartesianCoordinateMapping +{ +public: + CartesianCoordinateMapping(const size_t nx, const size_t ny, const size_t nz, + const BoundingBox_T & bbox) + : m_N{nx, ny, nz}, + m_bbox(bbox) + {} + void get_node_coordinates(double * node_coords, const int spatial_dim, const std::array & ijk) const + { + const Vector3d & min = m_bbox.get_min(); + const Vector3d & max = m_bbox.get_max(); + for (int i=0; i < spatial_dim; ++i) + node_coords[i] = min[i] + (max[i]-min[i])*ijk[i]/m_N[i]; + } + void get_triangle_lattice_node_coordinates(double * node_coords, const bool flattenBoundaries, const std::array & ij) const + { + const Vector3d & min = m_bbox.get_min(); + const Vector3d & max = m_bbox.get_max(); + const double offset = ij[1]%2==0 ? 0.0 : -0.5; + node_coords[0] = min[0] + (max[0]-min[0])/m_N[0] * (ij[0]+offset); + if (flattenBoundaries) + node_coords[0] = std::min(max[0], std::max(min[0], node_coords[0])); + node_coords[1] = min[1] + (max[1]-min[1])/m_N[1] * ij[1]; + } + void get_BCC_node_coordinates(double * node_coords, const int spatial_dim, const bool flattenBoundaries, const std::array & ijk, const std::array & dijk) const + { + const Vector3d & min = m_bbox.get_min(); + const Vector3d & max = m_bbox.get_max(); + for (int i=0; i < spatial_dim; ++i) + { + node_coords[i] = min[i] + (max[i]-min[i])*(0.5+ijk[i]+dijk[i])/m_N[i]; + if (flattenBoundaries) + node_coords[i] = std::min(max[i], std::max(min[i], node_coords[i])); + } + } +private: + const std::array m_N; + const BoundingBox_T m_bbox; +}; + +enum BoundingBoxMeshStructureType +{ + CUBIC_BOUNDING_BOX_MESH = 0, + BCC_BOUNDING_BOX_MESH = 1, + FLAT_WALLED_BCC_BOUNDING_BOX_MESH = 2, + TRIANGULAR_LATTICE_BOUNDING_BOX_MESH = 3, + FLAT_WALLED_TRIANGULAR_LATTICE_BOUNDING_BOX_MESH = 4 +}; + +class BoundingBoxMesh { +public: + typedef BoundingBox_T BoundingBoxType; + static std::array get_node_x_y_z( stk::mesh::EntityId entity_id, const std::array & N ); + +public: + BoundingBoxMesh(stk::topology element_topology, const std::vector& rank_names = std::vector()); + void set_domain(const BoundingBoxType & mesh_bbox, const double mesh_size, const int pad_cells = 0); + void populate_mesh(stk::ParallelMachine pm = MPI_COMM_WORLD, const stk::mesh::BulkData::AutomaticAuraOption auto_aura_option = stk::mesh::BulkData::AUTO_AURA); + stk::mesh::MetaData & meta_data() { ThrowAssert( nullptr != m_meta.get() ) ; return *m_meta; } + stk::mesh::BulkData & bulk_data() { ThrowAssert( nullptr != m_mesh.get() ) ; return *m_mesh; } + const stk::mesh::MetaData & meta_data() const { ThrowAssert( nullptr != m_meta.get() ) ; return *m_meta; } + const stk::mesh::BulkData & bulk_data() const { ThrowAssert( nullptr != m_mesh.get() ) ; return *m_mesh; } + + void create_domain_sides(); + const CartesianCoordinateMapping & get_coord_mapping() const { return *my_coord_mapping; } + void get_node_x_y_z( stk::mesh::EntityId entity_id, size_t &ix , size_t &iy , size_t &iz ) const; + void set_mesh_structure_type(BoundingBoxMeshStructureType type) { myMeshStructureType = type; } + bool has_flat_boundaries() const { return CUBIC_BOUNDING_BOX_MESH == myMeshStructureType || FLAT_WALLED_BCC_BOUNDING_BOX_MESH == myMeshStructureType || FLAT_WALLED_TRIANGULAR_LATTICE_BOUNDING_BOX_MESH == myMeshStructureType; } +private: + void declare_domain_side_parts(const stk::mesh::Part & blockPart); + void require_has_flat_boundaries() const; + void populate_2D_triangular_lattice_based_mesh(); + void populate_BCC_mesh(); + void populate_cell_based_mesh(); + void setup_cell_node( size_t ix , size_t iy , size_t iz ); + void setup_BCC_node( size_t ix , size_t iy , size_t iz, int dx, int dy, int dz ); + void build_face_tets( size_t cell_id, size_t ix , size_t iy , size_t iz, int iface, const std::vector & cell_node_ids ); + stk::mesh::EntityId get_node_id(const size_t ix, const size_t iy, const size_t iz ) const + { + const size_t node_id_start = 1; + return node_id_start + ix + ( m_nx + 1 ) * ( iy + ( m_ny + 1 ) * iz ); + } + stk::mesh::EntityId get_BCC_node_id(const size_t ix, const size_t iy, const size_t iz, const int dx, const int dy, const int dz ) const + { + const size_t node_id_start = 1; + const size_t num_nodes = ( m_nx + 1 ) * ( m_ny + 1 ) * ( m_nz + 1 ); + return node_id_start + num_nodes + (ix+1+dx) + ( m_nx + 2 ) * ( (iy+1+dy) + ( m_ny + 2 ) * (iz+1+dz) ); + } + void get_cell_x_y_z( stk::mesh::EntityId cell_id, size_t &ix , size_t &iy , size_t &iz ) const; + std::pair determine_processor_cells(const int p_size, const int p_rank) const; + void generate_node_to_processor_map(const int p_size, + const int p_rank, + const std::vector> & cell_node_locations, + std::unordered_map> & nodes_to_procs) const; + void set_is_cell_edge_function_for_BCC_mesh() const; + void set_is_cell_edge_function_for_cell_based_mesh() const; +private: + std::unique_ptr m_meta; + std::unique_ptr m_mesh; + std::unique_ptr my_coord_mapping; + stk::mesh::PartVector m_elem_parts; + stk::mesh::PartVector m_node_parts; + const stk::topology m_element_topology; + BoundingBoxMeshStructureType myMeshStructureType{CUBIC_BOUNDING_BOX_MESH}; + size_t m_nx, m_ny, m_nz; + BoundingBoxType m_mesh_bbox; + std::vector mySideParts; +}; + +class BoundingBoxMeshTri3 : public BoundingBoxMesh +{ +public: + BoundingBoxMeshTri3() : BoundingBoxMesh(stk::topology::TRIANGLE_3_2D) {} +}; + +class BoundingBoxMeshQuad4 : public BoundingBoxMesh +{ +public: + BoundingBoxMeshQuad4() : BoundingBoxMesh(stk::topology::QUADRILATERAL_4_2D) {} +}; + +class BoundingBoxMeshTet4 : public BoundingBoxMesh +{ +public: + BoundingBoxMeshTet4() : BoundingBoxMesh(stk::topology::TETRAHEDRON_4) {} +}; + +class BoundingBoxMeshHex8 : public BoundingBoxMesh +{ +public: + BoundingBoxMeshHex8() : BoundingBoxMesh(stk::topology::HEXAHEDRON_8) {} +}; + +} // namespace krino + +#endif // Akri_BoundingBoxMesh_h diff --git a/packages/krino/krino/krino_lib/Akri_CDFEM_Parent_Edge.cpp b/packages/krino/krino/krino_lib/Akri_CDFEM_Parent_Edge.cpp new file mode 100644 index 000000000000..b856b61eed17 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDFEM_Parent_Edge.cpp @@ -0,0 +1,636 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include + +#include + +namespace krino{ + +CDFEM_Parent_Edge::CDFEM_Parent_Edge(const std::vector & edgeNodes, + const std::vector & edgeNodePositions) +: my_edge_nodes(edgeNodes), + my_edge_node_positions(edgeNodePositions) +{ +} + +double CDFEM_Parent_Edge::MinSize() +{ + return SegmentLowerEnvelope::MinSize(); +} + +double CDFEM_Parent_Edge::get_edge_node_position(stk::mesh::Entity edgeNode) const +{ + for (size_t i=0; isecond << " "; + } + krinolog << "}" << "\n"; +} + +void +CDFEM_Parent_Edge::find_crossings(const std::vector > & nodes_isovar) +{ + my_crossings.clear(); + my_crossing_signs.clear(); + + const int num_nodes = get_num_nodes(); + ThrowAssert(static_cast(nodes_isovar.size()) == num_nodes); + const int num_ls = nodes_isovar[0].size(); + if (num_ls > 1 && Phase_Support::has_one_levelset_per_phase()) + { + find_crossings_multiple_levelset(nodes_isovar); + find_crossings_including_fake_ones(nodes_isovar); + return; + } + + // TODO: respect minimum_internal_edge_size + + for ( int ls_index = 0; ls_index < num_ls; ++ls_index ) + { + InterfaceID iface(ls_index, ls_index); + my_crossing_signs[iface] = LevelSet::sign(nodes_isovar[num_nodes-1][ls_index]); + if( !LevelSet::sign_change(nodes_isovar[0][ls_index], nodes_isovar[num_nodes-1][ls_index]) ) continue; + for ( int s = 0; s < num_nodes-1; ++s ) + { + const double ls0 = nodes_isovar[s][ls_index]; + const double ls1 = nodes_isovar[s+1][ls_index]; + if ( LevelSet::sign_change(ls0, ls1) ) + { + const double interval_position = ls0 / ( ls0 - ls1 ); + const double abs_position = (1.-interval_position)*my_edge_node_positions[s] + interval_position*my_edge_node_positions[s+1]; + my_crossings[iface] = abs_position; + my_crossing_signs[iface] = LevelSet::sign(ls1); + } + } + } + + if (krinolog.shouldPrint(LOG_DEBUG)) + { + debug_print_crossings(); + } +} + +std::vector*> get_sorted_internal_crossings(CrossingMap & crossings) +{ + std::vector*> sortedCrossings; + sortedCrossings.reserve(crossings.size()); + for (auto && crossing : crossings) + if (crossing.second > 0. && crossing.second < 1.) + sortedCrossings.push_back(&crossing); + + std::sort(sortedCrossings.begin(), sortedCrossings.end(), + [](const std::pair* crossing0, const std::pair* crossing1) + { return crossing0->second < crossing1->second || (crossing0->second == crossing1->second && crossing0->first < crossing1->first); }); + return sortedCrossings; +} + +void collapse_small_internal_segments_while_perserving_topology(CrossingMap &crossings, const double snapTol) +{ + const std::vector*> sortedCrossings = get_sorted_internal_crossings(crossings); + + bool done = false; + while (!done) + { + size_t minSegment = 0; + double minSegmentSize = snapTol; + for (size_t i=1; isecond - sortedCrossings[i-1]->second); + if (segmentSize > 0 && segmentSize <= minSegmentSize) + { + minSegment = i; + minSegmentSize = segmentSize; + } + } + done = minSegment == 0; + if (!done) + { + double & loc0 = sortedCrossings[minSegment-1]->second; + double & loc1 = sortedCrossings[minSegment]->second; + const double newLoc = 0.5*(loc0+loc1); + loc0 = newLoc; + loc1 = newLoc; + } + } +} + +void CDFEM_Parent_Edge::collapse_small_segments_while_preserving_topology(const double snapTol) +{ + for (auto && crossing : my_crossings) + { + if (crossing.second <= snapTol) + crossing.second = 0.0; + else if (crossing.second >= 1.-snapTol ) + crossing.second = 1.0; + } + collapse_small_internal_segments_while_perserving_topology(my_crossings, snapTol); +} + +std::vector get_effective_sorted_parent_node_domains(const int parentNodeIndex, const CrossingMap & crossings, const std::vector & sortedParentNodeDomains) +{ + std::vector effectiveSortedParentNodeDomains = sortedParentNodeDomains; + double endPoint = (parentNodeIndex == 0) ? 0. : 1.; + for (auto && crossing : crossings) + { + if (std::binary_search(sortedParentNodeDomains.begin(), sortedParentNodeDomains.end(), crossing.first.first_ls()) || + std::binary_search(sortedParentNodeDomains.begin(), sortedParentNodeDomains.end(), crossing.first.second_ls())) + { + endPoint = (parentNodeIndex == 0) ? std::max(endPoint, crossing.second) : std::min(endPoint, crossing.second); + } + } + for (auto && crossing : crossings) + { + const bool inRange = (parentNodeIndex == 0) ? (crossing.second < endPoint) : (crossing.second > endPoint); + if (inRange) + { + effectiveSortedParentNodeDomains.push_back(crossing.first.first_ls()); + effectiveSortedParentNodeDomains.push_back(crossing.first.second_ls()); + } + } + + stk::util::sort_and_unique(effectiveSortedParentNodeDomains); + return effectiveSortedParentNodeDomains; +} + +void adjust_crossing_locations_based_on_node_captured_domains_for_level_set_per_interface(const std::vector & sortedParentNode0Domains, const std::vector & sortedParentNode1Domains, CrossingMap & crossings) +{ + if (sortedParentNode0Domains.empty() && sortedParentNode1Domains.empty()) + return; + + for (auto && crossing : crossings) + { + const InterfaceID & iface = crossing.first; + ThrowAssert(iface.first_ls() == iface.second_ls()); + const bool in0 = std::binary_search(sortedParentNode0Domains.begin(), sortedParentNode0Domains.end(), iface.first_ls()); + const bool in1 = std::binary_search(sortedParentNode1Domains.begin(), sortedParentNode1Domains.end(), iface.first_ls()); + + double & loc = crossing.second; + if (in0 && (!in1 || loc<0.5)) + loc = 0.; + else if (in1 && (!in0 || loc>=0.5)) + loc = 1.; + } +} + +void adjust_crossing_locations_based_on_node_captured_domains_for_level_set_per_phase(const int parentNodeIndex, const std::vector & sortedParentNodeDomains, CrossingMap & allCrossings) +{ + if (sortedParentNodeDomains.empty()) + return; + + ThrowAssert(parentNodeIndex == 0 || parentNodeIndex == 1); + const double nodePos = (parentNodeIndex == 0) ? 0. : 1.; + + double furthestLocationToAdjust = (parentNodeIndex == 0) ? 0. : 1.; + for (auto && crossing : allCrossings) + { + const InterfaceID & iface = crossing.first; + if (std::binary_search(sortedParentNodeDomains.begin(), sortedParentNodeDomains.end(), iface.first_ls()) && + std::binary_search(sortedParentNodeDomains.begin(), sortedParentNodeDomains.end(), iface.second_ls())) + { + furthestLocationToAdjust = (parentNodeIndex == 0) ? std::max(furthestLocationToAdjust, crossing.second) : std::min(furthestLocationToAdjust, crossing.second); + crossing.second = nodePos; + } + } + + for (auto && crossing : allCrossings) + { + if ((parentNodeIndex == 0 && crossing.second < furthestLocationToAdjust) || + (parentNodeIndex == 1 && crossing.second > furthestLocationToAdjust)) + { + crossing.second = nodePos; + } + } +} + +void adjust_crossing_locations_based_on_node_captured_domains_for_level_set_per_phase(const std::vector & sortedParentNode0Domains, const std::vector & sortedParentNode1Domains, CrossingMap & allCrossings) +{ + if (sortedParentNode0Domains.empty() && sortedParentNode1Domains.empty()) + return; + + double highestLocToSendTo0 = 0.; + double lowestLocToSendTo1 = 1.; + for (auto && crossing : allCrossings) + { + const InterfaceID & iface = crossing.first; + const bool in0 = + std::binary_search(sortedParentNode0Domains.begin(), sortedParentNode0Domains.end(), iface.first_ls()) && + std::binary_search(sortedParentNode0Domains.begin(), sortedParentNode0Domains.end(), iface.second_ls()); + const bool in1 = + std::binary_search(sortedParentNode1Domains.begin(), sortedParentNode1Domains.end(), iface.first_ls()) && + std::binary_search(sortedParentNode1Domains.begin(), sortedParentNode1Domains.end(), iface.second_ls()); + + double & loc = crossing.second; + if (in0 && (!in1 || loc<0.5)) + { + highestLocToSendTo0 = std::max(loc, highestLocToSendTo0); + loc = 0.; + } + if (in1 && (!in0 || loc>=0.5)) + { + highestLocToSendTo0 = std::min(loc, lowestLocToSendTo1); + loc = 1.; + } + } + + for (auto && crossing : allCrossings) + { + double & loc = crossing.second; + if (loc < highestLocToSendTo0) + loc = 0.; + if (loc > lowestLocToSendTo1) + loc = 1.; + } +} + +void copy_real_crossing_locations_to_fake_crossing_locations(const CrossingMap & realCrossings, CrossingMap & allCrossings) +{ + for (auto && crossing : allCrossings) + { + auto iter = realCrossings.find(crossing.first); + if (iter != realCrossings.end()) + crossing.second = iter->second; + } +} + +std::pair get_begin_and_end_phases(const CrossingMap & realCrossings, const CrossingSignMap & realCrossingSign) +{ + double begLoc = 1.; + double endLoc = 0.; + int begPhase = -1; + int endPhase = -1; + for (auto && crossing : realCrossings) + { + const InterfaceID iface = crossing.first; + const double loc = crossing.second; + const int sign = realCrossingSign.at(iface); + const int fromPhase = (sign == 1) ? iface.first_ls() : iface.second_ls(); + if (loc < begLoc || (loc == begLoc && fromPhase > begPhase)) + { + begPhase = fromPhase; + begLoc = loc; + } + const int toPhase = (sign == -1) ? iface.first_ls() : iface.second_ls(); + if (loc > endLoc || (loc == endLoc && toPhase > endPhase)) + { + endPhase = toPhase; + endLoc = loc; + } + } + return {begPhase,endPhase}; +} + +static double find_next_crossing_location(const CrossingMap & crossings, + const CrossingSignMap & crossingSigns, + const double currentLocation, + const int currentPhase) +{ + double nextLocation = 1.; + for (auto && crossing : crossings) + { + if (crossing.second >= currentLocation) + { + const InterfaceID iface = crossing.first; + const int fromPhase = (crossingSigns.at(iface) == 1) ? iface.first_ls() : iface.second_ls(); + if (fromPhase == currentPhase) + nextLocation = std::min(nextLocation, crossing.second); + } + } + return nextLocation; +} + +static std::vector find_next_phase_candidates_at_location(const CrossingMap & crossings, + const CrossingSignMap & crossingSigns, + const double nextLocation, + const int currentPhase) +{ + std::vector nextPhaseCandidates; + for (auto && crossing : crossings) + { + if (crossing.second == nextLocation) + { + const InterfaceID iface = crossing.first; + const int sign = crossingSigns.at(iface); + const int fromPhase = (sign == 1) ? iface.first_ls() : iface.second_ls(); + if (fromPhase == currentPhase) + { + const int toPhase = (sign == -1) ? iface.first_ls() : iface.second_ls(); + nextPhaseCandidates.push_back(toPhase); + } + } + } + return nextPhaseCandidates; +} + +std::vector find_next_phase_candidates(const CrossingMap & crossings, + const CrossingSignMap & crossingSigns, + const double currentLocation, + const int currentPhase) +{ + const double nextLocation = find_next_crossing_location(crossings, crossingSigns, currentLocation, currentPhase); + return find_next_phase_candidates_at_location(crossings, crossingSigns, nextLocation, currentPhase); +} + +std::vector> shortest_path_to_end(const std::vector> & pathSoFar, + const CrossingMap & crossings, + const CrossingSignMap & crossingSigns, + const double currentLocation, + const int currentPhase, + const int endPhase) +{ + const std::vector nextPhaseCandidates = find_next_phase_candidates(crossings, crossingSigns, currentLocation, currentPhase); + if (nextPhaseCandidates.empty()) + { + if (currentPhase == endPhase) + return pathSoFar; + else + return {}; + } + + std::vector> shortestPath; + size_t shortestPathSize = std::numeric_limits::max(); + for (int nextPhase : nextPhaseCandidates) + { + std::vector> path = pathSoFar; + const InterfaceID iface(currentPhase, nextPhase); + const double nextLocation = crossings.at(iface); + path.emplace_back(iface, nextLocation); + const auto fullPath = shortest_path_to_end(path, crossings, crossingSigns, nextLocation, nextPhase, endPhase); + if (!fullPath.empty() && fullPath.size() < shortestPathSize) + { + shortestPath = fullPath; + shortestPathSize = fullPath.size(); + } + } + return shortestPath; +} + +std::vector> shortest_path_from_begin_to_end(const CrossingMap & crossings, + const CrossingSignMap & crossingSigns, + const int beginPhase, + const int endPhase) +{ + std::vector> emptyPath; + double beginLocation = -1.; + return shortest_path_to_end(emptyPath, crossings, crossingSigns, beginLocation, beginPhase, endPhase); +} + +bool determine_real_crossings_from_locations(CrossingMap & realCrossings, CrossingSignMap & realCrossingSigns, std::set & edgePhases, const CrossingMap & allCrossings, const CrossingSignMap & allCrossingSigns) +{ + const auto begEndPhases = get_begin_and_end_phases(realCrossings, realCrossingSigns); + + if (begEndPhases.first == begEndPhases.second) + { + return true; + } + + realCrossings.clear(); + realCrossingSigns.clear(); + edgePhases.clear(); + + const auto shortestPath = shortest_path_from_begin_to_end(allCrossings, allCrossingSigns, begEndPhases.first, begEndPhases.second); + if (shortestPath.empty()) + return false; + + for (auto && crossing : shortestPath) + { + realCrossings[crossing.first] = crossing.second; + realCrossingSigns[crossing.first] = allCrossingSigns.at(crossing.first); + edgePhases.insert(crossing.first.first_ls()); + edgePhases.insert(crossing.first.second_ls()); + } + return true; +} + +void CDFEM_Parent_Edge::adjust_crossing_locations_based_on_node_captured_domains(const std::vector & sortedParentNode0Domains, const std::vector & sortedParentNode1Domains) +{ + if (sortedParentNode0Domains.empty() && sortedParentNode1Domains.empty()) + return; + if (Phase_Support::has_one_levelset_per_phase()) + { + if (my_crossings_including_fake.empty()) + return; + + adjust_crossing_locations_based_on_node_captured_domains_for_level_set_per_phase(sortedParentNode0Domains, sortedParentNode1Domains, my_crossings_including_fake); + + const bool success = determine_real_crossings_from_locations(my_crossings, my_crossing_signs, edge_phases, my_crossings_including_fake, my_crossing_signs_including_fake); + if (!success) + krinolog << "Failed to adjust crossings " << *this << stk::diag::dendl; + } + else + { + if (my_crossings.empty()) + return; + adjust_crossing_locations_based_on_node_captured_domains_for_level_set_per_interface(sortedParentNode0Domains, sortedParentNode1Domains, my_crossings); + } +} + +void +CDFEM_Parent_Edge::find_crossings_multiple_levelset(const std::vector > & nodes_isovar) +{ + const Segment_Vector lower_envelope = SegmentLowerEnvelope::find_lower_envelope(my_edge_node_positions, nodes_isovar); + + edge_phases.clear(); + for(auto && it : lower_envelope) + { + edge_phases.insert(it.ls_index()); + } + + // Create crossings between the segments of the lower_envelope + for(Segment_Vector::const_iterator it = lower_envelope.begin(); it != lower_envelope.end()-1; ++it) + { + const LS_Segment & cur = *it; + const LS_Segment & next = *(it+1); + if (cur.ls_index() == next.ls_index()) continue; + + const double cur_right = cur.right_endpoint(); + const double next_left = next.left_endpoint(); + ThrowRequire(cur_right == next_left); + ThrowRequire(cur.ls_index() != next.ls_index()); + InterfaceID iface(cur.ls_index(), next.ls_index()); + ThrowRequireMsg(my_crossings.find(iface) == my_crossings.end(), "Multiple interface crossing error after pruning."); + my_crossings[iface] = cur_right; + my_crossing_signs[iface] = (cur.ls_index() < next.ls_index()) ? 1 : -1; + } + + if (krinolog.shouldPrint(LOG_DEBUG)) + { + debug_print_crossings(); + } +} + +bool +CDFEM_Parent_Edge::have_any_crossings() const +{ + return !my_crossings.empty(); +} + +std::tuple +CDFEM_Parent_Edge::get_crossing_position_and_sign(const InterfaceID key) const +{ + ThrowRequire(Phase_Support::has_one_levelset_per_phase()); + + if(have_crossing(key)) + { + return std::make_tuple(get_crossing_position(key), get_crossing_sign(key), false); + } + + return std::make_tuple(get_fake_crossing_position(key), get_fake_crossing_sign(key), true); +} + +typedef std::array*,2> CrossingInterval; + +static CrossingInterval get_crossing_interval(const CDFEM_Parent_Edge & edge, const std::pair & fakeCrossing) +{ + const double fakeLoc = fakeCrossing.second; + const std::pair * before = nullptr; + const std::pair * after = nullptr; + for (auto && crossing : edge.get_crossings()) + { + const double loc = crossing.second; + if (loc == fakeLoc) + { + return {&crossing, &crossing}; + } + else if (loc < fakeLoc) + { + if (before == nullptr || loc > before->second) + before = &crossing; + } + else + { + if (after == nullptr || loc < after->second) + after = &crossing; + } + } + return {before, after}; +} + +static int get_phase_on_interval(const CDFEM_Parent_Edge & edge, const CrossingInterval & crossingInterval) +{ + const std::pair * before = crossingInterval[0]; + const std::pair * after = crossingInterval[1]; + ThrowRequire(before != nullptr || after != nullptr); + if (before != nullptr) + { + return (edge.get_crossing_sign(before->first) == -1) ? before->first.first_ls() : before->first.second_ls(); + } + return (edge.get_crossing_sign(after->first) == -1) ? after->first.second_ls() : after->first.first_ls(); +} + +static bool fake_crossing_is_actually_real(const CDFEM_Parent_Edge & edge, const CrossingInterval & crossingInterval, const std::pair & fakeCrossing) +{ + if (crossingInterval[0] != crossingInterval[1]) + { + const int phaseOnInterval = get_phase_on_interval(edge, crossingInterval); + const InterfaceID fakeInterface = fakeCrossing.first; + const bool crossingIsActuallyReal = (fakeInterface.first_ls() == phaseOnInterval || fakeInterface.second_ls() == phaseOnInterval); + return crossingIsActuallyReal; + } + return false; +} + +static void fixup_fake_crossing_location_for_consistency(CDFEM_Parent_Edge & edge, std::pair & fakeCrossing) +{ + const CrossingInterval & crossingInterval = get_crossing_interval(edge, fakeCrossing); + if (fake_crossing_is_actually_real(edge, crossingInterval, fakeCrossing)) + { + const std::pair * before = crossingInterval[0]; + const std::pair * after = crossingInterval[1]; + double & loc = fakeCrossing.second; + const bool useBefore = before ? (after ? (loc-before->second < after->second-loc) : true) : false; + loc = useBefore ? before->second : after->second; + } +} + +static bool fake_crossing_is_actually_real(const CDFEM_Parent_Edge & edge, const std::pair & fakeCrossing) +{ + const CrossingInterval & crossingInterval = get_crossing_interval(edge, fakeCrossing); + return fake_crossing_is_actually_real(edge, crossingInterval, fakeCrossing); +} + +bool CDFEM_Parent_Edge::all_fake_crossings_are_really_fake() const +{ + for (auto && crossing : my_crossings_including_fake) + if (!have_crossing(crossing.first) && fake_crossing_is_actually_real(*this, crossing)) + return false; + return true; +} + +void CDFEM_Parent_Edge::fixup_fake_crossing_locations_for_consistency() +{ + for (auto && crossing : my_crossings_including_fake) + if (!have_crossing(crossing.first)) + fixup_fake_crossing_location_for_consistency(*this, crossing); +} + +void +CDFEM_Parent_Edge::find_crossings_including_fake_ones(const std::vector > & nodes_isovar) +{ + my_crossings_including_fake.clear(); + my_crossing_signs_including_fake.clear(); + + ThrowRequire(Phase_Support::has_one_levelset_per_phase()); + const int numLS = nodes_isovar[0].size(); + std::vector lsMins(numLS,std::numeric_limits::max()); + std::vector lsMaxs(numLS,std::numeric_limits::lowest()); + for (auto && nodeIsovar : nodes_isovar) + { + for (int i=0; i result = have_crossing(iface) ? + std::make_pair(get_crossing_position(iface), get_crossing_sign(iface)) : + find_crossing_position_and_sign(iface, nodes_isovar); + if (result.first >= 0.) + { + my_crossings_including_fake[iface] = result.first; + my_crossing_signs_including_fake[iface] = result.second; + } + } + } + } + + fixup_fake_crossing_locations_for_consistency(); +} + +std::pair +CDFEM_Parent_Edge::find_crossing_position_and_sign(const InterfaceID key, const std::vector > & nodes_isovar) const +{ + ThrowRequire(Phase_Support::has_one_levelset_per_phase()); + return krino::find_crossing_position_and_sign(key, my_edge_node_positions, nodes_isovar); +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_CDFEM_Parent_Edge.hpp b/packages/krino/krino/krino_lib/Akri_CDFEM_Parent_Edge.hpp new file mode 100644 index 000000000000..cceb31cf10e1 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDFEM_Parent_Edge.hpp @@ -0,0 +1,137 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_CDFEM_Parent_Edge_h +#define Akri_CDFEM_Parent_Edge_h + +#include +#include +#include + +#include +#include +#include + +namespace stk { namespace mesh { class BulkData; } } + +namespace krino { + +class CDFEM_Parent_Edge { +public: + CDFEM_Parent_Edge(const std::vector & edgeNodes, + const std::vector & edgeNodePositions, + const std::vector > & nodes_isovar) + : my_edge_nodes(edgeNodes), + my_edge_node_positions(edgeNodePositions) + { + ThrowAssert(edgeNodePositions.size() == nodes_isovar.size()); + find_crossings(nodes_isovar); + } + + CDFEM_Parent_Edge(const std::vector & edgeNodePositions, + const std::vector > & nodes_isovar) + : CDFEM_Parent_Edge({}, edgeNodePositions, nodes_isovar) {} + + CDFEM_Parent_Edge(const std::vector & edgeNodes, + const std::vector > & nodes_isovar) + : CDFEM_Parent_Edge(edgeNodes, {0.,1.}, nodes_isovar) {} + + CDFEM_Parent_Edge(const std::vector > & nodes_isovar) + : CDFEM_Parent_Edge({0.,1.}, nodes_isovar) {} + + CDFEM_Parent_Edge(const std::vector & edgeNodes, + const std::vector & edgeNodePositions); + CDFEM_Parent_Edge() {} + + // Must be larger than machine epsilon, but puts limit on smallest effective snap tolerance + static double MinSize(); + + bool valid() const { return !my_edge_node_positions.empty(); } + + void find_crossings(const std::vector > & nodes_isovar); + void collapse_small_segments_while_preserving_topology(const double snapTol); + + void adjust_crossing_locations_based_on_node_captured_domains(const std::vector & sortedParentNode0Domains, const std::vector & sortedParentNode1Domains); + + bool have_crossing(const InterfaceID key) const { return my_crossings.find(key) != my_crossings.end(); } + const CrossingMap & get_crossings() const { return my_crossings; } + const CrossingMap & get_crossings_including_fake() const { return my_crossings_including_fake; } + bool all_fake_crossings_are_really_fake() const; + + double get_crossing_position(const InterfaceID key) const { + CrossingMap::const_iterator it = my_crossings.find(key); + return it != my_crossings.end() ? it->second : -1.0; + } + // Crossing sign is defined as whether the interface is + or - for x > crossing point + // For multiple LS problems we'll say that -1 corresponds to InterfaceID.first being lower for x > x_crossing_point + // For uncrossed edges it will be the sign of both parent nodes + int get_crossing_sign(const InterfaceID key) const { + CrossingSignMap::const_iterator it = my_crossing_signs.find(key); + return it->second; + } + std::tuple get_crossing_position_and_sign(const InterfaceID key) const; + unsigned get_num_nodes() const { return my_edge_node_positions.size(); } + const std::vector & get_nodes() const { return my_edge_nodes; } + bool have_any_crossings() const; + std::pair get_parent_nodes() const { return std::pair(my_edge_nodes.front(), my_edge_nodes.back()); } + int get_uncrossed_phase() const { return (edge_phases.size() == 1) ? (*edge_phases.begin()) : -1; } + const std::set & get_edge_phases() const { return edge_phases; } + double get_edge_node_position(stk::mesh::Entity edgeNode) const; + + friend std::ostream & operator << (std::ostream &os, const CDFEM_Parent_Edge & edge); + void debug_print_crossings() const; + +private: + void find_crossings_multiple_levelset(const std::vector > & nodes_isovar); + std::pair find_crossing_position_and_sign(const InterfaceID key, const std::vector > & nodes_isovar) const; + void find_crossings_including_fake_ones(const std::vector > & nodes_isovar); + double get_fake_crossing_position(const InterfaceID key) const { + CrossingMap::const_iterator it = my_crossings_including_fake.find(key); + return it != my_crossings_including_fake.end() ? it->second : -1.0; + } + int get_fake_crossing_sign(const InterfaceID key) const { + CrossingSignMap::const_iterator it = my_crossing_signs_including_fake.find(key); + return it->second; + } + void fixup_fake_crossing_locations_for_consistency(); + + std::vector my_edge_nodes; + std::vector my_edge_node_positions; + CrossingMap my_crossings; + CrossingSignMap my_crossing_signs; + CrossingMap my_crossings_including_fake; + CrossingSignMap my_crossing_signs_including_fake; + std::set edge_phases; +}; + +inline std::ostream & operator << (std::ostream &os, const CDFEM_Parent_Edge & edge) +{ + const unsigned num_nodes = edge.get_num_nodes(); + os << "CDFEM parent edge has " << num_nodes << " nodes and phases { "; + for (int phase : edge.get_edge_phases()) + os << phase << " "; + os << "}\n crossings: { "; + const auto oldPrecision = os.precision(); + os.precision(16); + for ( auto && crossing : edge.get_crossings() ) + { + os << crossing.first << "@" << crossing.second << ", sign=" << edge.get_crossing_sign(crossing.first) << " "; + } + os << "}" << "\n crossings including fake: { "; + for ( auto && crossing : edge.get_crossings_including_fake() ) + { + os << crossing.first << "@" << crossing.second << ", sign=" << edge.get_fake_crossing_sign(crossing.first) << " "; + } + os << "}" << "\n"; + os.precision(oldPrecision); + return os; +} + +} // namespace krino + +#endif // Akri_CDFEM_Parent_Edge_h diff --git a/packages/krino/krino/krino_lib/Akri_CDFEM_Parent_Edges.cpp b/packages/krino/krino/krino_lib/Akri_CDFEM_Parent_Edges.cpp new file mode 100644 index 000000000000..d3eab40fb058 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDFEM_Parent_Edges.cpp @@ -0,0 +1,492 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino +{ + +std::set get_phases_present_on_edges(const std::vector & parentEdges) +{ + std::set phasesPresent; + for(auto && parentEdge : parentEdges) + if(parentEdge) + for (int phase : parentEdge->get_edge_phases()) + phasesPresent.insert(phase); + return phasesPresent; +} + +static void add_interface_phases(const InterfaceID & interface, std::set & phases) +{ + phases.insert(interface.first_ls()); + phases.insert(interface.second_ls()); +} + +static std::set get_all_interfaces_including_fake(const std::vector & parentEdges) +{ + std::set allInterfacesIncludingFake; + for(auto && parentEdge : parentEdges) + if(parentEdge) + for (auto && crossing : parentEdge->get_crossings_including_fake()) + allInterfacesIncludingFake.insert(crossing.first); + return allInterfacesIncludingFake; +} + +bool phase_has_interfaces_to_all_other_phases(const int phase, const std::set & phasesPresent, const std::set & interfaces) +{ + for (int otherPhase : phasesPresent) + if (interfaces.find(InterfaceID(phase,otherPhase)) == interfaces.end()) + return false; + return true; +} + +void add_phases_possibly_present_on_interior(const std::vector & parentEdges, std::set & phasesPresent) +{ + const std::set allInterfacesIncludingFake = get_all_interfaces_including_fake(parentEdges); + + std::set phasesPossiblyPresent; + for (auto && interface : allInterfacesIncludingFake) + add_interface_phases(interface, phasesPossiblyPresent); + + // This should be unit tested. What about order dependency? + for (int phase : phasesPossiblyPresent) + if (phasesPresent.find(phase) == phasesPresent.end()) + if (phase_has_interfaces_to_all_other_phases(phase, phasesPresent, allInterfacesIncludingFake)) + phasesPresent.insert(phase); +} + +std::set get_phases_present_on_edges_and_interior(const std::vector & elementParentEdges) +{ + std::set phasesPresent = get_phases_present_on_edges(elementParentEdges); + add_phases_possibly_present_on_interior(elementParentEdges, phasesPresent); + return phasesPresent; +} + +static +std::set get_interfaces_with_any_real_edge_crossings(const std::vector & elementParentEdges) +{ + std::set interfacesPresent; + for(auto && parentEdge : elementParentEdges) + if(parentEdge) + for(const auto & crossing : parentEdge->get_crossings()) + interfacesPresent.insert(crossing.first); + return interfacesPresent; +} + +std::set get_interfaces_present(const std::vector & elementParentEdges) +{ + return get_interfaces_with_any_real_edge_crossings(elementParentEdges); +} + +void fill_element_parent_edges(const stk::mesh::BulkData & mesh, + const stk::mesh::Entity elem, + const ParentEdgeMap & parentEdges, + std::vector & elementParentEdges, + std::vector & areParentEdgesAreOrientedSameAsElementEdges) +{ + const stk::topology stk_topology = mesh.bucket(elem).topology(); + const unsigned numEdges = stk_topology.num_edges(); + const stk::mesh::Entity * const nodes = mesh.begin_nodes(elem); + elementParentEdges.assign(numEdges, nullptr); + areParentEdgesAreOrientedSameAsElementEdges.assign(numEdges, true); + for(unsigned i=0; i < numEdges; ++i) + { + const unsigned * edge_node_ordinals = get_edge_node_ordinals(stk_topology, i); + const CDFEM_Parent_Edge * parent_edge = + find_parent_edge(mesh, parentEdges, nodes[edge_node_ordinals[0]], nodes[edge_node_ordinals[1]]); + elementParentEdges[i] = parent_edge; + if(parent_edge && parent_edge->get_parent_nodes().first != nodes[edge_node_ordinals[0]]) + areParentEdgesAreOrientedSameAsElementEdges[i] = false; + } +} + +void fill_face_nodes_and_parent_edges(const stk::topology & elementTopology, + const int iFace, + const std::vector & elementNodes, + const std::vector & elementParentEdges, + const std::vector & areParentEdgesOrientedSameAsElementEdges, + std::vector & faceNodes, + std::vector & faceParentEdges, + std::vector & areParentEdgesOrientedSameAsFaceEdges) +{ + ThrowAssert(elementTopology == stk::topology::TETRAHEDRON_4); + constexpr std::array,4> faceEdges = {{ {{0,4,3}}, {{1,5,4}}, {{3,5,2}}, {{2,1,0}} }}; + constexpr std::array,4> isFaceEdgeOrientedSameAsElementEdge = {{ {{true,true,false}}, {{true,true,false}}, {{true,false,true}}, {{false,false,false}} }}; + + faceNodes.resize(3); + elementTopology.face_nodes(elementNodes, iFace, faceNodes.data()); + + constexpr int numFaceEdges = 3; + faceParentEdges.resize(numFaceEdges); + areParentEdgesOrientedSameAsFaceEdges.resize(3); + for (int i=0; iprimary_entity_rank() != stk::topology::ELEMENT_RANK) continue; + + const stk::mesh::Part * nonconformal_node_iopart = phaseSupport.find_nonconformal_part(*part_ptr); + + if (phaseSupport.level_set_is_used_by_nonconformal_part(cdfemSupport.ls_field(ls_index).ptr, nonconformal_node_iopart)) + { + return true; + } + } + + return false; +} + +static bool +has_io_part_containing_phase(const Phase_Support & phase_support, const stk::mesh::PartVector & parts, const PhaseTag & phase) +{ + for(stk::mesh::PartVector::const_iterator part_iter = parts.begin(); part_iter != parts.end(); ++part_iter) + { + const stk::mesh::Part * const part = *part_iter; + if (part->primary_entity_rank() != stk::topology::ELEMENT_RANK || // limit ourselves to volume parts + !(stk::io::is_part_io_part(*part) || + phase_support.is_nonconformal(part))) + continue; + + const PhaseTag & iopart_phase = phase_support.get_iopart_phase(*part); + if (iopart_phase.contain(phase)) + { + return true; + } + } + + return false; +} + +static bool node_touches_alive_block(const stk::mesh::BulkData & mesh, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + stk::mesh::Entity node, + const int ls_index ) +{ + const CDFEM_Inequality_Spec * death_spec = cdfemSupport.get_death_spec(ls_index); + if (nullptr == death_spec) return false; + + const PhaseTag & alive_phase = death_spec->get_active_phase(); + const stk::mesh::PartVector & node_parts = mesh.bucket(node).supersets(); + return has_io_part_containing_phase(phaseSupport, node_parts, alive_phase); +} + +static bool node_touches_dead_block(const stk::mesh::BulkData & mesh, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + stk::mesh::Entity node, + const int ls_index ) +{ + const CDFEM_Inequality_Spec * death_spec = cdfemSupport.get_death_spec(ls_index); + if (nullptr == death_spec) return false; + + const PhaseTag & dead_phase = death_spec->get_deactivated_phase(); + const stk::mesh::PartVector & node_parts = mesh.bucket(node).supersets(); + return has_io_part_containing_phase(phaseSupport, node_parts, dead_phase); +} + +static bool node_has_real_ls_value(const stk::mesh::BulkData & mesh, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + stk::mesh::Entity node, + const int ls_index ) +{ + if( node_touches_dead_block(mesh, cdfemSupport, phaseSupport, node, ls_index) && + !node_touches_alive_block(mesh, cdfemSupport, phaseSupport, node, ls_index) ) + return false; + + return in_block_decomposed_by_ls(mesh, cdfemSupport, phaseSupport, node, ls_index); +} + +static void debug_print_edge_info(const stk::mesh::BulkData & mesh, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + const std::vector & edge_nodes, + const std::vector > & nodes_isovar) +{ + const int num_nodes = nodes_isovar.size(); + if(krinolog.shouldPrint(LOG_DEBUG)) + { + const auto old_precision = krinolog.getStream().precision(); + krinolog.getStream().precision(16); + const int num_ls = cdfemSupport.num_ls_fields(); + krinolog << stk::diag::dendl << "CDFEM_Parent_Edge::find_crossings():" << "\n"; + for ( int n = 0; n < num_nodes; ++n ) + { + krinolog << " Node: " << mesh.identifier(edge_nodes[n]) << ", in_block_decomposed_by_ls = { "; + for ( int ls_index = 0; ls_index < num_ls; ++ls_index ) krinolog << in_block_decomposed_by_ls(mesh, cdfemSupport, phaseSupport, edge_nodes[n], ls_index) << " "; + krinolog << "}, has_real_ls_value = { "; + for ( int ls_index = 0; ls_index < num_ls; ++ls_index ) krinolog << node_has_real_ls_value(mesh, cdfemSupport, phaseSupport, edge_nodes[n], ls_index) << " "; + krinolog << "}, ls = { "; + for ( int ls_index = 0; ls_index < num_ls; ++ls_index ) krinolog << nodes_isovar[n][ls_index] << " "; + krinolog << "}"; + krinolog << stk::diag::dendl; + } + krinolog.getStream().precision(old_precision); + } +} + +static void edge_ls_node_values(const stk::mesh::BulkData & mesh, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + const std::vector & edge_nodes, + std::vector >& nodes_isovar) +{ + const unsigned num_nodes = edge_nodes.size(); + const int num_ls = cdfemSupport.num_ls_fields(); + + nodes_isovar.assign(num_nodes, std::vector(num_ls, -1.0)); + for (unsigned n = 0; n < num_nodes; ++n) + { + for (int ls_index = 0; ls_index < num_ls; ++ls_index) + { + const CDFEM_Inequality_Spec * death_spec = cdfemSupport.get_death_spec(ls_index); + FieldRef isovar = cdfemSupport.ls_field(ls_index).isovar; + + const bool node_has_ls = node_has_real_ls_value(mesh, cdfemSupport, phaseSupport, edge_nodes[n], ls_index); + nodes_isovar[n][ls_index] = 0.0; + if (node_has_ls) + { + if (nullptr != death_spec && isovar.entity_rank() == stk::topology::ELEMENT_RANK) + { + // currently requires aura to work correctly in parallel + ThrowAssertMsg(mesh.is_automatic_aura_on(), "Capability requires aura."); + bool have_pos_elem = false; + bool have_neg_elem = false; + const unsigned num_node_elems = mesh.num_elements(edge_nodes[n]); + const stk::mesh::Entity* node_elems = mesh.begin_elements(edge_nodes[n]); + for (unsigned node_elem_index=0; node_elem_index(isovar, node_elem); + if (nullptr != isoptr) + { + if (*isoptr - cdfemSupport.ls_field(ls_index).isoval < 0.) + have_neg_elem = true; + else + have_pos_elem = true; + } + } + nodes_isovar[n][ls_index] = (have_pos_elem) ? (have_neg_elem ? 0.0 : 1.0) : -1.0; + } + else + { + const double * isoptr = field_data(isovar, edge_nodes[n]); + ThrowRequireMsg(nullptr != isoptr, "Isovar " << isovar.name() << " missing on node " << debug_entity(mesh, edge_nodes[n])); + nodes_isovar[n][ls_index] = *isoptr - cdfemSupport.ls_field(ls_index).isoval; + } + } + else if (nullptr != death_spec && node_touches_dead_block(mesh, cdfemSupport, phaseSupport, edge_nodes[n], ls_index)) + { + const bool dead_is_positive = death_spec->get_deactivated_phase().contain(cdfemSupport.ls_field(ls_index).identifier,+1); + if (dead_is_positive) nodes_isovar[n][ls_index] = 1.0; + } + + if (nullptr != death_spec && node_touches_dead_block(mesh, cdfemSupport, phaseSupport, edge_nodes[n], ls_index)) + { + const bool dead_is_positive = death_spec->get_deactivated_phase().contain(cdfemSupport.ls_field(ls_index).identifier, +1); + if (dead_is_positive && nodes_isovar[n][ls_index] < 0.0) + { + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "Setting node " << mesh.identifier(edge_nodes[n]) << " to zero to enforce irreversibility, ls = " << nodes_isovar[n][ls_index] << "\n"; + nodes_isovar[n][ls_index] = 0.0; + } + else if (!dead_is_positive && nodes_isovar[n][ls_index] >= 0.0) + { + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "Setting node " << mesh.identifier(edge_nodes[n]) << " to -REAL_MIN to enforce irreversibility, ls = " << nodes_isovar[n][ls_index] << "\n"; + nodes_isovar[n][ls_index] = -std::numeric_limits::min(); + } + } + } + } +} + +static CDFEM_Parent_Edge & +build_parent_edge(const stk::mesh::BulkData & mesh, ParentEdgeMap & parentEdges, const ParentsToChildMapper & parentsToChildMapper, const bool linearizeEdge, stk::mesh::Entity node0, stk::mesh::Entity node1) +{ + const stk::mesh::EntityId id0 = mesh.identifier(node0); + const stk::mesh::EntityId id1 = mesh.identifier(node1); + const ParentEdgeKey edge_key(id0, id1); + CDFEM_Parent_Edge & edge = parentEdges[edge_key]; + + std::vector edgeNodes; + std::vector edgeNodePositions; + + if(!edge.valid()) + { + const std::array nodes = (id0 < id1) ? std::array{node0, node1} : std::array{node1, node0}; + if (linearizeEdge) + fill_linear_edge_nodes_and_positions(nodes[0], nodes[1], edgeNodes, edgeNodePositions); + else + fill_edge_nodes_and_positions(mesh, nodes[0], nodes[1], parentsToChildMapper, edgeNodes, edgeNodePositions); + edge = CDFEM_Parent_Edge(edgeNodes, edgeNodePositions); + } + return edge; +} + +static void find_parent_edge_crossings(const stk::mesh::BulkData & mesh, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + ParentEdgeMap & parentEdges) +{ + std::vector> nodes_isovar; + + for (auto && map_entry : parentEdges) + { + CDFEM_Parent_Edge & edge = map_entry.second; + const std::vector edge_nodes = edge.get_nodes(); + edge_ls_node_values(mesh, cdfemSupport, phaseSupport, edge_nodes, nodes_isovar); + debug_print_edge_info(mesh, cdfemSupport, phaseSupport, edge_nodes, nodes_isovar); + edge.find_crossings(nodes_isovar); + } + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << stk::diag::dendl; +} + +stk::mesh::Selector get_parent_element_selector(const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport) +{ + stk::mesh::Selector parentElementSelector = + (cdfemSupport.get_parent_part() | (activePart & !cdfemSupport.get_child_part())) & + phaseSupport.get_all_decomposed_blocks_selector(); + + return parentElementSelector; +} + +stk::mesh::Selector get_owned_parent_element_selector(const stk::mesh::BulkData & mesh, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport) +{ + stk::mesh::Selector parentElementSelector = + (cdfemSupport.get_parent_part() | (activePart & !cdfemSupport.get_child_part())) & + phaseSupport.get_all_decomposed_blocks_selector() & + mesh.mesh_meta_data().locally_owned_part(); + + return parentElementSelector; +} + +std::vector get_owned_parent_elements(const stk::mesh::BulkData & mesh, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport) +{ + const stk::mesh::Selector parentElementSelector = get_owned_parent_element_selector(mesh, activePart, cdfemSupport, phaseSupport); + std::vector parentElements; + stk::mesh::get_selected_entities( parentElementSelector, mesh.get_buckets(stk::topology::ELEMENT_RANK, parentElementSelector), parentElements, false ); + return parentElements; +} + +ParentEdgeMap +build_parent_edges(const stk::mesh::BulkData & mesh, + const ParentsToChildMapper & parentsToChildMapper, + const std::function & should_build_linear_edge, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport) +{ + std::vector elements; + stk::mesh::get_entities( mesh, stk::topology::ELEMENT_RANK, mesh.mesh_meta_data().locally_owned_part(), elements, false); + + return build_parent_edges_using_elements(mesh, parentsToChildMapper, should_build_linear_edge, elements, activePart, cdfemSupport, phaseSupport); +} + +std::function build_no_linearized_edges_function() +{ + return [](stk::mesh::Entity node0, stk::mesh::Entity node1) + { return false; }; +} + +std::function build_all_linearized_edges_function() +{ + return [](stk::mesh::Entity node0, stk::mesh::Entity node1) + { return true; }; +} + +ParentEdgeMap +build_parent_edges_using_elements(const stk::mesh::BulkData & mesh, + const ParentsToChildMapper & parentsToChildMapper, + const std::function & should_build_linear_edge, + const std::vector & elements, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport) +{ + const stk::mesh::Selector parentElementSelector = get_owned_parent_element_selector(mesh, activePart, cdfemSupport, phaseSupport); + + ParentEdgeMap parentEdges; + + for (auto && elem : elements) + { + if (parentElementSelector(mesh.bucket(elem))) + { + const stk::topology topology = mesh.bucket(elem).topology(); + const stk::mesh::Entity* elem_nodes = mesh.begin_nodes(elem); + const unsigned numEdges = topology.num_edges(); + + for (unsigned iedge = 0; iedge < numEdges; ++iedge) + { + const unsigned * edge_node_ordinals = get_edge_node_ordinals(topology, iedge); + stk::mesh::Entity node0 = elem_nodes[edge_node_ordinals[0]]; + stk::mesh::Entity node1 = elem_nodes[edge_node_ordinals[1]]; + + const bool linearizeEdge = should_build_linear_edge(node0, node1); + build_parent_edge(mesh, parentEdges, parentsToChildMapper, linearizeEdge, node0, node1); + } + } + } + + find_parent_edge_crossings(mesh, cdfemSupport, phaseSupport, parentEdges); + + return parentEdges; +} + +const CDFEM_Parent_Edge * +find_parent_edge(const stk::mesh::BulkData & mesh, const ParentEdgeMap & parentEdges, stk::mesh::Entity node0, stk::mesh::Entity node1) +{ + const stk::mesh::EntityId id0 = mesh.identifier(node0); + const stk::mesh::EntityId id1 = mesh.identifier(node1); + const ParentEdgeKey edge_key(id0, id1); + + ParentEdgeMap::const_iterator it = parentEdges.find(edge_key); + + if (it != parentEdges.end()) + return &(it->second); + + return nullptr; +} + +} // namespace diff --git a/packages/krino/krino/krino_lib/Akri_CDFEM_Parent_Edges.hpp b/packages/krino/krino/krino_lib/Akri_CDFEM_Parent_Edges.hpp new file mode 100644 index 000000000000..50003bf3a910 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDFEM_Parent_Edges.hpp @@ -0,0 +1,86 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_CDFEM_PARENT_EDGES_H_ +#define KRINO_INCLUDE_AKRI_CDFEM_PARENT_EDGES_H_ +#include +#include +#include + +namespace krino +{ +class CDFEM_Support; +class Phase_Support; +class InterfaceID; +class ParentsToChildMapper; + +typedef OrderedIdPair ParentEdgeKey; +typedef std::map ParentEdgeMap; + +std::set get_phases_present_on_edges(const std::vector & parentEdges); +std::set get_phases_present_on_edges_and_interior(const std::vector & elementParentEdges); +std::set get_interfaces_present(const std::vector & elementParentEdges); + +const CDFEM_Parent_Edge * find_parent_edge(const stk::mesh::BulkData & mesh, const ParentEdgeMap & parentEdges, stk::mesh::Entity node0, stk::mesh::Entity node1); + +std::function build_no_linearized_edges_function(); + +std::function build_all_linearized_edges_function(); + +ParentEdgeMap +build_parent_edges_using_elements(const stk::mesh::BulkData & mesh, + const ParentsToChildMapper & parentsToChildMapper, + const std::function & should_build_linear_edge, + const std::vector & elements, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport); + +ParentEdgeMap +build_parent_edges(const stk::mesh::BulkData & mesh, + const ParentsToChildMapper & parentsToChildMapper, + const std::function & should_build_linear_edge, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport); + +void fill_element_parent_edges(const stk::mesh::BulkData & mesh, + const stk::mesh::Entity elem, + const ParentEdgeMap & parentEdges, + std::vector & elementParentEdges, + std::vector & areParentEdgesAreOrientedSameAsElementEdges); + +void fill_face_nodes_and_parent_edges(const stk::topology & elementTopology, + const int iFace, + const std::vector & elementNodes, + const std::vector & elementParentEdges, + const std::vector & areParentEdgesOrientedSameAsElementEdges, + std::vector & faceNodes, + std::vector & faceParentEdges, + std::vector & areParentEdgesOrientedSameAsFaceEdges); + +stk::mesh::Selector get_parent_element_selector(const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport); + +stk::mesh::Selector get_owned_parent_element_selector(const stk::mesh::BulkData & mesh, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport); + +std::vector get_owned_parent_elements(const stk::mesh::BulkData & mesh, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport); + +} + + + + +#endif /* KRINO_INCLUDE_AKRI_CDFEM_PARENT_EDGES_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_CDFEM_Snapper.hpp b/packages/krino/krino/krino_lib/Akri_CDFEM_Snapper.hpp new file mode 100644 index 000000000000..d63c5af7b6b9 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDFEM_Snapper.hpp @@ -0,0 +1,31 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_CDFEM_SNAPPER_H_ +#define KRINO_INCLUDE_AKRI_CDFEM_SNAPPER_H_ +#include + +namespace krino { + +class CDFEM_Snapper { +public: + CDFEM_Snapper() : my_cdfem_edge_tol(default_edge_tol) {} + + void set_edge_tolerance(double edge_tol) { my_cdfem_edge_tol = edge_tol; } + double get_edge_tolerance() const { ThrowRequireMsg(my_cdfem_edge_tol < 0.5, "Should not be requesting tolerance when always snapping."); return my_cdfem_edge_tol; } + bool always_snap() const { return my_cdfem_edge_tol > 0.5; } + bool snap_lo(double crossingVal) const { return always_snap() ? (crossingVal <= 0.5) : (crossingVal < get_edge_tolerance()); } + bool snap_hi(double crossingVal) const { return always_snap() ? (crossingVal > 0.5) : (crossingVal > 1.0-get_edge_tolerance()); } +private: + static constexpr double default_edge_tol = 1.e-3; + double my_cdfem_edge_tol; +}; + +} + +#endif /* KRINO_INCLUDE_AKRI_CDFEM_SNAPPER_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_CDFEM_Support.cpp b/packages/krino/krino/krino_lib/Akri_CDFEM_Support.cpp new file mode 100644 index 000000000000..55ed155e5b0e --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDFEM_Support.cpp @@ -0,0 +1,364 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include + +namespace krino{ + +Edge_Interpolation_Model CDFEM_Support::the_edge_interpolation_model = PIECEWISE_LINEAR; + +CDFEM_Support & +CDFEM_Support::get(stk::mesh::MetaData & meta) +{ + CDFEM_Support * support = const_cast(meta.get_attribute()); + if (NULL == support) + { + support = new CDFEM_Support(meta); + meta.declare_attribute_with_delete(support); + } + return *support; +} + +CDFEM_Support & +CDFEM_Support::get(const stk::mesh::MetaData & meta) +{ + CDFEM_Support * support = const_cast(meta.get_attribute()); + ThrowRequireMsg(nullptr != support, "Could not find CDFEM_Support attribute on MetaData."); + return *support; +} + +CDFEM_Support::CDFEM_Support(stk::mesh::MetaData & meta) + : my_meta(meta), + my_aux_meta(AuxMetaData::get(meta)), + my_simplex_generation_method(CUT_QUADS_BY_LARGEST_ANGLE), + my_fully_coupled_cdfem(false), + my_num_initial_decomposition_cycles(1), + myGlobalIDsAreParallelConsistent(true), + my_interface_minimum_refinement_level(0), + my_interface_maximum_refinement_level(0), + my_post_adapt_uniform_refinement_levels(0), + my_post_cdfem_refinement_levels(0), + my_nonconformal_adapt_target_element_count(0), + my_cdfem_edge_degeneracy_handling(SNAP_TO_NODE), + my_cdfem_snapper(), + my_cdfem_dof_edge_tol(0.0), + my_internal_face_stabilization_multiplier(0.0), + my_flag_use_hierarchical_dofs(false), + my_flag_constrain_CDFEM_to_XFEM_space(false), + my_flag_use_nonconformal_element_size(true), + my_timer_cdfem("CDFEM", sierra::Diag::sierraTimer()), + my_timer_adapt("Nonconformal Adapt", my_timer_cdfem) +{ + my_prolongation_model = ALE_NEAREST_POINT; + + if (3 == my_meta.spatial_dimension()) + my_simplex_generation_method = CUT_QUADS_BY_NEAREST_EDGE_CUT; + + create_parts(); +} + +void CDFEM_Support::create_parts() +{ + my_parent_part = &my_meta.declare_part("CDFEM_PARENT_CONTEXT_BIT"); + my_child_part = &my_meta.declare_part("CDFEM_CHILD_CONTEXT_BIT"); + my_internal_side_part = &my_meta.declare_part("CDFEM_INTERNAL_SIDE"); + + if (my_aux_meta.using_fmwk()) + { + const bool restartOnlyIOPart = true; + my_child_edge_node_part = &my_aux_meta.declare_io_part_with_topology("CDFEM_EDGE_NODE_2_PARENTS", stk::topology::NODE, restartOnlyIOPart); + } + else + { + // Currently no need to output nodeset for krino usage + my_child_edge_node_part = &my_meta.declare_part_with_topology("CDFEM_EDGE_NODE_2_PARENTS", stk::topology::NODE); + } +} + +void CDFEM_Support::register_cdfem_mesh_displacements_field() +{ + ThrowRequireMsg(my_meta.spatial_dimension() > 1, "Spatial dimension must be set and equal to 2 or 3."); + + const FieldType & vec_type = (my_meta.spatial_dimension() == 3) ? FieldType::VECTOR_3D : FieldType::VECTOR_2D; + FieldRef cdfem_disp_field = my_aux_meta.register_field(cdfem_mesh_displacements_field_name(), vec_type, stk::topology::NODE_RANK, 2, 1, get_universal_part()); + set_cdfem_displacement_field(cdfem_disp_field); +} + +void CDFEM_Support::register_parent_node_ids_field() +{ + FieldType id_field_type = (my_aux_meta.get_assert_32bit_flag()) ? FieldType::UNSIGNED_INTEGER : FieldType::UNSIGNED_INTEGER_64; + my_parent_node_ids_field = my_aux_meta.register_field("CDFEM_2_PARENT_NODE_IDS", + id_field_type, stk::topology::NODE_RANK, 1, + 2, *my_child_edge_node_part); +} + +stk::mesh::Selector +CDFEM_Support::get_post_cdfem_refinement_selector() const +{ + stk::mesh::Selector selector = my_aux_meta.active_part(); + if (!my_post_cdfem_refinement_blocks.empty()) + { + stk::mesh::PartVector blocks; + for (auto && block_name : my_post_cdfem_refinement_blocks) + { + if (my_aux_meta.has_part(block_name)) + { + stk::mesh::Part & block = my_aux_meta.get_part(block_name); + blocks.push_back(&block); + } + else + { + stk::RuntimeDoomedAdHoc() << "post_cdfem_refinement_block " << block_name << " not found.\n"; + } + } + selector &= stk::mesh::selectUnion(blocks); + } + return selector; +} + +void +CDFEM_Support::setup_fields() +{ + my_coords_field = LevelSet::get_current_coordinates(my_meta); + + Phase_Support::get(my_meta).check_phase_parts(); + + my_ls_fields.clear(); + const LevelSetManager & region_ls = LevelSetManager::get(my_meta); + for (auto&& ls : region_ls) + { + LS_Field calc_ls_field(ls->name(),ls->get_identifier(),ls->get_isovar_field(),ls->get_isoval(),ls.get()); + my_ls_fields.push_back(calc_ls_field); + } + + const unsigned num_ls = region_ls.numberLevelSets(); + my_death_specs.resize(num_ls, nullptr); + for (unsigned i=0; i(is); + my_ale_prolongation_fields.insert(field.field_state(state)); + } +} + +void +CDFEM_Support::add_interpolation_field(const FieldRef field) +{ + ThrowAssert(field.valid()); + ThrowRequireMsg(!is_ale_prolongation_field(field), "Cannot add " << field.name() << " as interpolation field because it is already an ALE prolongation field."); + for ( unsigned is = 0; is < field.number_of_states(); ++is ) + { + const stk::mesh::FieldState state = static_cast(is); + my_interpolation_fields.insert(field.field_state(state)); + } +} + +void +CDFEM_Support::finalize_fields() +{ + krinolog << "Finalizing field prolongation strategies for CDFEM." << stk::diag::push << stk::diag::dendl; + for ( auto && field_ptr : my_meta.get_fields() ) + { + const FieldRef field(field_ptr); + if( !field.type_is() || field.field_state(stk::mesh::StateNew) == my_cdfem_displacements_field ) continue; + + if( field.entity_rank()==stk::topology::ELEMENT_RANK && + !stk::equal_case(field.name(), "transition_element") && + !stk::equal_case(field.name(), "transition_element_3") && + !stk::equal_case(field.name(), "parent_element") ) + { + my_element_fields.insert(field); + } + + if( field.entity_rank()!=stk::topology::NODE_RANK ) continue; + + const auto& initial_prolong_field_name_entry = my_initial_prolongation_field_name_map.find(field.name()); + if (initial_prolong_field_name_entry != my_initial_prolongation_field_name_map.end()) + { + const std::string & src_field_name = initial_prolong_field_name_entry->second; + ThrowErrorMsgIf(!my_aux_meta.has_field(stk::topology::NODE_RANK, src_field_name), + "Error: Could not find initial prolongation field with name " << src_field_name); + + // If the src field does not have the desired state, use StateNone (which is the same as StateNew). + FieldRef src_field = my_aux_meta.get_field(stk::topology::NODE_RANK, src_field_name); + stk::mesh::FieldState src_state = (field.state() < src_field.number_of_states()) ? field.state() : stk::mesh::StateNone; + src_field = src_field.field_state(src_state); + + my_initial_prolongation_field_map[field] = src_field; + krinolog << "Added " + << src_field.name() << " (" << state_string(src_state) << ") as initial prolongation field for " + << field.name() << " (" << state_string(field.state()) << ")" << stk::diag::dendl; + } + + if (std::find(my_force_ale_prolongation_fields.begin(), + my_force_ale_prolongation_fields.end(), + field.name()) != my_force_ale_prolongation_fields.end()) + { + add_ale_prolongation_field(field); + } + + if(is_interpolation_field(field)) + { + krinolog << field.name() << " will use interpolation." << stk::diag::dendl; + continue; + } + else if(is_ale_prolongation_field(field)) + { + krinolog << field.name() << " will use ALE prolongation." << stk::diag::dendl; + continue; + } + else if (field.name() == "node_registry") + { + krinolog << field.name() << " will not be modified." << stk::diag::dendl; + continue; + } + + krinolog << field.name() << " will be zeroed." << stk::diag::dendl; + my_zeroed_fields.insert(field); + } + krinolog << stk::diag::pop << stk::diag::dendl; +} + +void CDFEM_Support::force_ale_prolongation_for_field(const std::string & field_name) +{ + my_force_ale_prolongation_fields.push_back(field_name); +} + +bool +CDFEM_Support::add_initial_prolongation_field(const std::string & dest_field_name, const std::string & src_field_name) +{ + if (my_initial_prolongation_field_name_map.find(dest_field_name) != my_initial_prolongation_field_name_map.end()) + { + return false; + } + my_initial_prolongation_field_name_map[dest_field_name] = src_field_name; + return true; +} + +FieldRef +CDFEM_Support::get_initial_prolongation_field(const FieldRef field) const +{ + auto iter = my_initial_prolongation_field_map.find(field); + if (iter == my_initial_prolongation_field_map.end()) + return FieldRef(); + return iter->second; +} + +void +CDFEM_Support::set_simplex_generation_method(const Simplex_Generation_Method & method) +{ + ThrowAssert(method < MAX_SIMPLEX_GENERATION_METHOD); + ThrowRequireMsg(3 == my_meta.spatial_dimension() || method != CUT_QUADS_BY_NEAREST_EDGE_CUT, "Simplex generation method CUT_QUADS_BY_NEAREST_EDGE_CUT only supported in 3d."); + my_simplex_generation_method = method; +} + +void +CDFEM_Support::activate_interface_refinement(int minimumLevel, int maximumLevel) +{ + /* %TRACE% */ Traceback trace__("krino::CDFEM_Support::activate_interface_refinement(int minimum_level, int maximum_level)"); /* %TRACE% */ + + ThrowRequireMsg(my_interface_minimum_refinement_level == 0 && my_interface_maximum_refinement_level == 0, + "Interface refinement levels should only be specified once."); + ThrowRequireMsg(maximumLevel >= minimumLevel || maximumLevel == 0, + "Maximum interface refinement level must be greater than or equal to the minimum interface refinement level or left unspecified."); + if (maximumLevel == 0) maximumLevel = minimumLevel; + + my_interface_minimum_refinement_level = minimumLevel; + my_interface_maximum_refinement_level = maximumLevel; + + setup_refinement_marker(); + + if (maximumLevel > 0) + set_global_ids_are_NOT_parallel_consistent(); +} + +void +CDFEM_Support::activate_nonconformal_adaptivity(const int numLevels) +{ + /* %TRACE% */ Traceback trace__("krino::CDFEM_Support::activate_nonconformal_adaptivity(const int num_levels)"); /* %TRACE% */ + + if (numLevels < my_interface_maximum_refinement_level) + { + krinolog << "Ignoring request to activate " << numLevels << " of CDFEM nonconformal adaptivity because a maximum of " << my_interface_maximum_refinement_level << " have already been activated." << stk::diag::dendl; + return; + } + + my_interface_minimum_refinement_level = numLevels; + my_interface_maximum_refinement_level = numLevels; + + setup_refinement_marker(); + + if (numLevels > 0) + set_global_ids_are_NOT_parallel_consistent(); +} + +void +CDFEM_Support::setup_refinement_marker() +{ + /* %TRACE% */ Traceback trace__("krino::CDFEM_Support::activate_nonconformal_adaptivity(const int num_levels)"); /* %TRACE% */ + + my_nonconformal_adapt_marker_name = "CDFEM_NONCONFORMAL_MARKER"; + + my_aux_meta.register_field(my_nonconformal_adapt_marker_name, FieldType::INTEGER, stk::topology::ELEMENT_RANK, 1, 1, get_universal_part()); + my_aux_meta.register_field(my_nonconformal_adapt_marker_name, FieldType::INTEGER, stk::topology::NODE_RANK, 1, 1, get_universal_part()); +} + +int +CDFEM_Support::get_ls_index(const LevelSet * ls) const +{ + for ( unsigned ls_index = 0; ls_index < my_ls_fields.size(); ++ls_index ) + { + if (ls == ls_field(ls_index).ptr) return ls_index; + } + ThrowRuntimeError("Invalid level set field index"); +} + +void +CDFEM_Support::activate_nonconformal_adapt_target_count(const uint64_t target_count) +{ + /* %TRACE% */ Traceback trace__("CDFEM_Support::activate_nonconformal_adapt_target_count(const uint64_t target_count)"); /* %TRACE% */ + + my_nonconformal_adapt_target_element_count = target_count; + my_nonconformal_adapt_indicator_name = "CDFEM_ADAPTIVITY_ERROR_INDICATOR"; + + my_aux_meta.register_field(my_nonconformal_adapt_indicator_name, + FieldType::REAL, + stk::topology::ELEMENT_RANK, + 1, + 1, + get_universal_part()); +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_CDFEM_Support.hpp b/packages/krino/krino/krino_lib/Akri_CDFEM_Support.hpp new file mode 100644 index 000000000000..3d07bd11f63a --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDFEM_Support.hpp @@ -0,0 +1,259 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_CDFEM_Support_h +#define Akri_CDFEM_Support_h +// +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace krino { + +class LevelSet; +class CDFEM_Inequality_Spec; + +struct LS_Field +{ + LS_Field(const std::string & name_, const LevelSet_Identifier & identifier_, const FieldRef isovar_, const double isoval_, const LevelSet * const ptr_) + : name(name_), identifier(identifier_), isovar(isovar_), isoval(isoval_), ptr(ptr_) { + ThrowRequireMsg(isovar_.valid(), "Invalid field " + isovar_.name() + " used in CDFEM initialization"); + } + + // Constructor just for unit tests + LS_Field(const std::string & name_, const LevelSet_Identifier & identifier_) + : name(name_), identifier(identifier_), isoval(0), ptr(NULL) { + } + + std::string name; + LevelSet_Identifier identifier; + FieldRef isovar; + double isoval; + const LevelSet * ptr; +}; + +enum Prolongation_Model +{ + ALE_NEAREST_NODE=0, + ALE_NEAREST_POINT, + INTERPOLATION, + MAX_PROLONGATION_MODEL +}; + +enum Edge_Interpolation_Model +{ + PIECEWISE_LINEAR=0, + CONSTRAINED_LINEAR, + MAX_EDGE_INTERPOLATION_MODEL +}; + +enum Edge_Degeneracy_Handling +{ + SNAP_TO_NODE=0, + SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE, + MAX_EDGE_DEGENERACY_HANDLING_TYPE +}; + +enum Simplex_Generation_Method +{ + CUT_QUADS_BY_GLOBAL_IDENTIFIER=0, + CUT_QUADS_BY_LARGEST_ANGLE, + CUT_QUADS_BY_NEAREST_EDGE_CUT, + MAX_SIMPLEX_GENERATION_METHOD +}; + +class CDFEM_Support { +public: + + CDFEM_Support(stk::mesh::MetaData & meta); + ~CDFEM_Support() {} + CDFEM_Support(CDFEM_Support const&) = delete; + CDFEM_Support& operator=(CDFEM_Support const&) = delete; + + static CDFEM_Support & get(stk::mesh::MetaData & meta); + static CDFEM_Support & get(const stk::mesh::MetaData & meta); + static void use_constrained_edge_interpolation() { the_edge_interpolation_model = CONSTRAINED_LINEAR; } + static Edge_Interpolation_Model get_edge_interpolation_model() { return the_edge_interpolation_model; } + static bool use_nonconformal_adaptivity(stk::mesh::MetaData & meta) { CDFEM_Support & cdfem_support = get(meta); return cdfem_support.get_interface_maximum_refinement_level() > 0; } + static std::string cdfem_mesh_displacements_field_name() { return "CDFEM_MESH_DISPLACEMENTS"; } + + static bool is_active(const stk::mesh::MetaData & meta) + { + return Phase_Support::exists_and_has_phases_defined(meta); + } + + stk::mesh::MetaData & get_mesh_meta() { return my_meta; } + const stk::mesh::MetaData & get_mesh_meta() const { return my_meta; } + int num_ls_fields() const { return my_ls_fields.size(); } + const LS_Field & ls_field(int i) const { return my_ls_fields[i]; } + LS_Field & ls_field(int i) { return my_ls_fields[i]; } + const std::vector & ls_fields() const { return my_ls_fields; } + Prolongation_Model get_prolongation_model() const { return my_prolongation_model; } + void set_prolongation_model(const Prolongation_Model & model) { my_prolongation_model = model; } + Simplex_Generation_Method get_simplex_generation_method() const { return my_simplex_generation_method; } + void set_simplex_generation_method(const Simplex_Generation_Method & method); + bool get_global_ids_are_parallel_consistent() const { return myGlobalIDsAreParallelConsistent; } + void set_global_ids_are_NOT_parallel_consistent() { myGlobalIDsAreParallelConsistent = false; } + const CDFEM_Inequality_Spec * get_death_spec(int ls_index) const { return my_death_specs[ls_index]; } + void activate_interface_refinement(int minimum_level, int maximum_level); + void activate_nonconformal_adaptivity(const int num_levels); + int get_ls_index(const LevelSet * ls) const; + + void create_parts(); + + void register_cdfem_mesh_displacements_field(); + void register_parent_node_ids_field(); + + void setup_fields(); + void finalize_fields(); + void set_cdfem_displacement_field(const FieldRef field) { my_cdfem_displacements_field = field; } + void set_cdfem_snap_displacement_field(const FieldRef field) { myCDFEMSnapDisplacementsField = field; } + void add_ale_prolongation_field(const FieldRef field); + void add_interpolation_field(const FieldRef field); + // Just for unit test setup purposes + void add_ls_field(const LS_Field & to_add, const CDFEM_Inequality_Spec * death_spec = nullptr) { my_ls_fields.push_back(to_add); my_death_specs.push_back(death_spec); } + + void set_coords_field(const FieldRef coords_field) { my_coords_field = coords_field; } + const FieldRef get_coords_field() const { return my_coords_field; } + const FieldRef get_cdfem_displacements_field() { return my_cdfem_displacements_field; } + const FieldRef get_cdfem_snap_displacements_field() const { return myCDFEMSnapDisplacementsField; } + const FieldSet & get_ale_prolongation_fields() const { return my_ale_prolongation_fields; } + const FieldSet & get_interpolation_fields() const { return my_interpolation_fields; } + const FieldSet & get_zeroed_fields() const { return my_zeroed_fields; } + const FieldSet & get_element_fields() const { return my_element_fields; } + + bool add_initial_prolongation_field(const std::string & dest_field_name, const std::string & src_field_name); + FieldRef get_initial_prolongation_field(const FieldRef field) const; + + stk::mesh::Part & get_parent_part() const { return *my_parent_part; } + stk::mesh::Part & get_child_part() const { return *my_child_part; } + stk::mesh::Part & get_internal_side_part() const { return *my_internal_side_part; } + stk::mesh::Part & get_active_part() const { return my_aux_meta.active_part(); } + stk::mesh::Part & get_universal_part() const { return my_meta.universal_part(); } + stk::mesh::Part & get_locally_owned_part() const { return my_meta.locally_owned_part(); } + stk::mesh::Part & get_globally_shared_part() const { return my_meta.globally_shared_part(); } + + stk::mesh::Selector get_post_cdfem_refinement_selector() const; + + stk::mesh::Part & get_child_edge_node_part() const { return *my_child_edge_node_part; } + FieldRef get_parent_node_ids_field() const { return my_parent_node_ids_field; } + + void activate_fully_coupled_cdfem() { my_fully_coupled_cdfem = true; } + bool fully_coupled_cdfem() const { return my_fully_coupled_cdfem; } + + void activate_nonconformal_adapt_target_count(uint64_t val); + uint64_t get_nonconformal_adapt_target_count() const { return my_nonconformal_adapt_target_element_count; } + int get_interface_minimum_refinement_level() const { return my_interface_minimum_refinement_level; } + int get_interface_maximum_refinement_level() const { return my_interface_maximum_refinement_level; } + void set_post_adapt_refinement_levels(int levels) { my_post_adapt_uniform_refinement_levels = levels; } + int get_post_adapt_refinement_levels() const { return my_post_adapt_uniform_refinement_levels; } + void set_post_cdfem_refinement_levels(int levels) { my_post_cdfem_refinement_levels = levels; } + int get_post_cdfem_refinement_levels() const { return my_post_cdfem_refinement_levels; } + void set_post_cdfem_refinement_blocks(const std::vector & post_cdfem_refinement_blocks) { my_post_cdfem_refinement_blocks = post_cdfem_refinement_blocks; } + void set_num_initial_decomposition_cycles(int num_initial_decomposition_cycles) { my_num_initial_decomposition_cycles = num_initial_decomposition_cycles; } + // In case of both nonconformal adaptivity, perform at least 2 rounds of level set initialization and decomposition. + // This is to capture features that might be missed on the unrefined mesh. + int get_num_initial_decomposition_cycles() const { return (my_num_initial_decomposition_cycles > 1) ? my_num_initial_decomposition_cycles : ((my_interface_maximum_refinement_level > 0) ? 2 : 1); } + const std::string & get_nonconformal_adapt_marker_name() const { return my_nonconformal_adapt_marker_name; } + const std::string & get_nonconformal_adapt_indicator_name() const { return my_nonconformal_adapt_indicator_name; } + void set_nonconformal_hadapt(const std::function & hadapt) { my_nonconformal_hadapt = hadapt; } + const std::function & get_nonconformal_hadapt() const { return my_nonconformal_hadapt; } + + bool is_ale_prolongation_field(const FieldRef field) const + { + return (my_ale_prolongation_fields.find(field) != my_ale_prolongation_fields.end()); + } + bool is_interpolation_field(const FieldRef field) const + { + return (my_interpolation_fields.find(field) != my_interpolation_fields.end()); + } + + void use_nonconformal_element_size(bool flag) { my_flag_use_nonconformal_element_size = flag; } + bool use_nonconformal_element_size() const { return my_flag_use_nonconformal_element_size; } + + Edge_Degeneracy_Handling get_cdfem_edge_degeneracy_handling() const { return my_cdfem_edge_degeneracy_handling; } + void set_cdfem_edge_degeneracy_handling( const Edge_Degeneracy_Handling type ) { my_cdfem_edge_degeneracy_handling = type; } + + stk::diag::Timer & get_timer_cdfem() const { return my_timer_cdfem; } + stk::diag::Timer & get_timer_adapt() const { return my_timer_adapt; } + + void set_cdfem_edge_tol( const double tol ) { my_cdfem_snapper.set_edge_tolerance(tol); } + const CDFEM_Snapper & get_snapper() const { return my_cdfem_snapper; } + const double & get_cdfem_dof_edge_tol() const { return my_cdfem_dof_edge_tol; } + void set_cdfem_dof_edge_tol( const double tol ) { my_cdfem_dof_edge_tol = tol; } + bool use_internal_face_stabilization() const { return my_internal_face_stabilization_multiplier > 0.0; } + double get_internal_face_stabilization_multiplier() const { return my_internal_face_stabilization_multiplier; } + void set_internal_face_stabilization_multiplier( const double mult ) { my_internal_face_stabilization_multiplier = mult; } + bool get_use_hierarchical_dofs() const { return my_flag_use_hierarchical_dofs; } + void set_use_hierarchical_dofs(bool flag) { my_flag_use_hierarchical_dofs = flag; } + bool get_constrain_CDFEM_to_XFEM_space() const { return my_flag_constrain_CDFEM_to_XFEM_space; } + void set_constrain_CDFEM_to_XFEM_space(bool flag) { my_flag_constrain_CDFEM_to_XFEM_space = flag; } + + void force_ale_prolongation_for_field(const std::string & field_name); + +private: + void setup_refinement_marker(); + +private: + stk::mesh::MetaData & my_meta; + AuxMetaData & my_aux_meta; + + FieldRef my_coords_field; + std::vector my_ls_fields; + std::vector my_death_specs; + FieldRef my_cdfem_displacements_field; + FieldRef myCDFEMSnapDisplacementsField; + Prolongation_Model my_prolongation_model; + Simplex_Generation_Method my_simplex_generation_method; + std::vector my_force_ale_prolongation_fields; + FieldSet my_ale_prolongation_fields; + FieldSet my_interpolation_fields; + FieldSet my_zeroed_fields; + FieldSet my_element_fields; + std::map my_initial_prolongation_field_name_map; + std::map my_initial_prolongation_field_map; + static Edge_Interpolation_Model the_edge_interpolation_model; + stk::mesh::Part * my_parent_part; + stk::mesh::Part * my_child_part; + stk::mesh::Part * my_internal_side_part; + stk::mesh::Part * my_child_edge_node_part; + FieldRef my_parent_node_ids_field; + bool my_fully_coupled_cdfem; + int my_num_initial_decomposition_cycles; + bool myGlobalIDsAreParallelConsistent; + int my_interface_minimum_refinement_level; + int my_interface_maximum_refinement_level; + int my_post_adapt_uniform_refinement_levels; + int my_post_cdfem_refinement_levels; + std::vector my_post_cdfem_refinement_blocks; + uint64_t my_nonconformal_adapt_target_element_count; + std::string my_nonconformal_adapt_marker_name; + std::string my_nonconformal_adapt_indicator_name; + std::function my_nonconformal_hadapt; + Edge_Degeneracy_Handling my_cdfem_edge_degeneracy_handling; + CDFEM_Snapper my_cdfem_snapper; + double my_cdfem_dof_edge_tol; + double my_internal_face_stabilization_multiplier; + bool my_flag_use_hierarchical_dofs; + bool my_flag_constrain_CDFEM_to_XFEM_space; + bool my_flag_use_nonconformal_element_size; + mutable stk::diag::Timer my_timer_cdfem; + mutable stk::diag::Timer my_timer_adapt; +}; + +} // namespace krino + +#endif // Akri_CDFEM_Support_h diff --git a/packages/krino/krino/krino_lib/Akri_CDMesh.cpp b/packages/krino/krino/krino_lib/Akri_CDMesh.cpp new file mode 100644 index 000000000000..71b66be817d4 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDMesh.cpp @@ -0,0 +1,3689 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Needed for all_reduce_max +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace krino{ + +std::shared_ptr CDMesh::the_new_mesh; + +//-------------------------------------------------------------------------------- + +CDMesh::CDMesh( stk::mesh::BulkData & mesh, const std::shared_ptr & old_mesh ) + : my_meta(mesh.mesh_meta_data()), + my_aux_meta(AuxMetaData::get(my_meta)), + my_entity_id_pool(my_meta), + my_spatial_dim( my_meta.spatial_dimension() ), + my_cdfem_support(CDFEM_Support::get(my_meta)), + my_phase_support(Phase_Support::get(my_meta)), + my_old_mesh(old_mesh), + my_stash_step_count(-1), + my_missing_remote_prolong_facets(false), + my_timer_decompose("Decompose", my_cdfem_support.get_timer_cdfem()), + my_timer_decomposition_has_changed("Need CDFEM", my_cdfem_support.get_timer_cdfem()), + my_timer_snap("Snapping", my_timer_decompose), + my_timer_stash_field_data("Stash Field Data", my_timer_decompose), + my_timer_modify_mesh("Modify Mesh", my_cdfem_support.get_timer_cdfem()), + my_timer_prolongation("Prolongation", my_cdfem_support.get_timer_cdfem()), + my_timer_compute_CFL("Compute CFL", my_cdfem_support.get_timer_cdfem()) +{ /* %TRACE[ON]% */ Trace trace__("CDMesh::CDMesh( stk::mesh::BulkData & mesh )"); /* %TRACE% */ + + stk::mesh::insert(my_attribute_parts, my_aux_meta.active_part()); + stk::mesh::insert(my_attribute_parts, my_aux_meta.exposed_boundary_part()); + stk::mesh::insert(my_attribute_parts, my_aux_meta.block_boundary_part()); + + if(my_old_mesh) + { + my_old_mesh->my_old_mesh.reset(); + } +} + +CDMesh::~CDMesh() +{ + clear(); +} + +SubElementNode * CDMesh::add_managed_node(std::unique_ptr node) +{ + nodes.emplace_back(std::move(node)); + return nodes.back().get(); +} + +//-------------------------------------------------------------------------------- + + +stk::mesh::Part & CDMesh::get_locally_owned_part() const { return my_meta.locally_owned_part(); } +stk::mesh::Part & CDMesh::get_globally_shared_part() const { return my_meta.globally_shared_part(); } +stk::mesh::Part & CDMesh::get_active_part() const { return my_aux_meta.active_part(); } +stk::mesh::Part & CDMesh::get_block_boundary_part() const { return my_aux_meta.block_boundary_part(); } + +//-------------------------------------------------------------------------------- + +void CDMesh::add_periodic_node_pair(stk::mesh::Entity node1, stk::mesh::Entity node2) +{ + my_periodic_node_id_map[stk_bulk().identifier(node1)].push_back(stk_bulk().identifier(node2)); + my_periodic_node_id_map[stk_bulk().identifier(node2)].push_back(stk_bulk().identifier(node1)); +} + +const std::vector & CDMesh::all_interface_ids() const +{ + if(crossing_keys.empty()) + { + const int num_ls = num_ls_fields(); + if(num_ls < 2 || !Phase_Support::has_one_levelset_per_phase()) + { + crossing_keys.resize(num_ls_fields()); + for(int i=0; i < num_ls_fields(); ++i) + { + crossing_keys[i] = InterfaceID(i,i); + } + } + else + { + for(int i=0; i < num_ls_fields(); ++i) + { + for(int j=i+1; j < num_ls_fields(); ++j) + { + crossing_keys.push_back(InterfaceID(i,j)); + } + } + } + } + return crossing_keys; +} + +std::vector CDMesh::active_interface_ids() const +{ + const std::vector all_interfaces = all_interface_ids(); + if (all_interfaces.size() == 1) return all_interfaces; + + std::vector id_is_active_locally(all_interfaces.size(), false); + for (const auto & elem : elements) + { + for (auto && elemInterface : elem->get_sorted_cutting_interfaces()) + { + const auto lower = std::lower_bound(all_interfaces.begin(), all_interfaces.end(), elemInterface); + ThrowAssert(*lower == elemInterface); + id_is_active_locally[std::distance(all_interfaces.begin(), lower)] = true; + } + } + + std::vector id_is_active_globally(all_interfaces.size()); + stk::all_reduce_sum(stk_bulk().parallel(), id_is_active_locally.data(), id_is_active_globally.data(), id_is_active_locally.size()); + + std::vector active_ids; + for (size_t id=0; id & oldMesh = the_new_mesh->my_old_mesh; + const bool noSuccessfulDecompositionSinceLastFailedStep = nullptr == oldMesh; + const bool lastStepFailed = nullptr != oldMesh && oldMesh->my_stash_step_count == step_count; + const bool restoreMesh = lastStepFailed || + noSuccessfulDecompositionSinceLastFailedStep; // Even though mesh has already been restored, another process might have modified it (ie rayTracer) + if (restoreMesh) + { + the_new_mesh = std::make_shared(mesh, std::shared_ptr()); + the_new_mesh->generate_nonconformal_elements(); + the_new_mesh->restore_subelement_edge_nodes(); + the_new_mesh->restore_subelements(); + } + } +} + +void CDMesh::build_and_stash_old_mesh(const int stepCount) +{ + std::shared_ptr & old_mesh = my_old_mesh; + if (!old_mesh) + { + old_mesh = std::make_shared(stk_bulk(), std::shared_ptr()); + old_mesh->generate_nonconformal_elements(); + old_mesh->stash_field_data(-1, *this); + } + else + { + if (my_cdfem_support.get_interface_maximum_refinement_level() > 0) + { + old_mesh = std::make_shared(stk_bulk(), std::shared_ptr()); + old_mesh->rebuild_child_part(); + old_mesh->rebuild_parent_and_active_parts_using_nonconformal_and_child_parts(); + old_mesh->generate_nonconformal_elements(); + old_mesh->restore_subelement_edge_nodes(); + old_mesh->restore_subelements(); + } + + old_mesh->stash_field_data(stepCount, *this); + } +} + +static FieldSet get_snap_fields(const CDFEM_Support & cdfemSupport) +{ + FieldSet snapFields = cdfemSupport.get_interpolation_fields(); + + FieldRef cdfemSnapField = cdfemSupport.get_cdfem_snap_displacements_field(); + if (cdfemSnapField.valid()) + { + for ( unsigned is = 0; is < cdfemSnapField.number_of_states(); ++is ) + { + const stk::mesh::FieldState state = static_cast(is); + snapFields.erase(cdfemSnapField.field_state(state)); + } + + for (auto && field : cdfemSupport.ls_fields()) + { + const FieldRef lsField = field.isovar.field(); + for ( unsigned is = 0; is < lsField.number_of_states(); ++is ) + { + const stk::mesh::FieldState state = static_cast(is); + if (state != stk::mesh::StateNew) + snapFields.erase(lsField.field_state(state)); + } + } + } + return snapFields; +} + +static void interpolate_nodal_field(const FieldRef field, + stk::mesh::Entity node, + const std::vector & interpNodes, + const std::vector & interpWeights) +{ + const unsigned fieldLength = field.length(); + + double * val = field_data(field, node); + if (nullptr == val) return; + + for (unsigned i=0; i(field, interpNodes[iNode]); + ThrowRequire(nullptr != nodeVal); + + for (unsigned i=0; i & parentNodes, + std::vector & parentWeights) +{ + std::map nodeStencil; + node.build_stencil(nodeStencil); + + parentNodes.clear(); + parentWeights.clear(); + for (auto && entry : nodeStencil) + { + parentNodes.push_back(entry.first->entity()); + parentWeights.push_back(entry.second); + } +} + +static bool any_node_was_snapped(const std::vector & nodes, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) +{ + for (auto && node : nodes) + { + const auto iter = nodesToCapturedDomains.find(node); + if (iter != nodesToCapturedDomains.end() && !iter->second.empty()) + return true; + } + return false; +} + +static void apply_snapping_to_children_of_snapped_nodes(const CDFEM_Support & cdfemSupport, + const NodeToCapturedDomainsMap & nodesToCapturedDomains, + const krino::SubElementNode & node, + std::vector & parentNodes, + std::vector & parentWeights) +{ + fill_parent_nodes_and_weights(node, parentNodes, parentWeights); + if (any_node_was_snapped(parentNodes, nodesToCapturedDomains)) + { + for (auto && field : cdfemSupport.get_interpolation_fields()) + interpolate_nodal_field(field, node.entity(), parentNodes, parentWeights); + } +} + +void CDMesh::snap_and_update_fields_and_captured_domains(const InterfaceGeometry & interfaceGeometry, + NodeToCapturedDomainsMap & nodesToCapturedDomains) const +{ + const FieldSet snapFields = get_snap_fields(my_cdfem_support); + + FieldRef cdfemSnapField = my_cdfem_support.get_cdfem_snap_displacements_field(); + if (cdfemSnapField.valid()) + stk::mesh::field_copy(my_cdfem_support.get_coords_field(), cdfemSnapField); + + const stk::mesh::Selector parentElementSelector = get_parent_element_selector(get_active_part(), my_cdfem_support, my_phase_support); + nodesToCapturedDomains = snap_as_much_as_possible_while_maintaining_quality(stk_bulk(), parentElementSelector, snapFields, interfaceGeometry, my_cdfem_support.get_global_ids_are_parallel_consistent()); + + if (cdfemSnapField.valid()) + stk::mesh::field_axpby(+1.0, my_cdfem_support.get_coords_field(), -1.0, cdfemSnapField); + + std::vector parentNodes; + std::vector parentWeights; + + if (cdfemSnapField.valid() && my_old_mesh) + for (auto && node : my_old_mesh->nodes) + if (!node->is_mesh_node()) + apply_snapping_to_children_of_snapped_nodes(my_cdfem_support, nodesToCapturedDomains, *node, parentNodes, parentWeights); +} + +int +CDMesh::decompose_mesh(stk::mesh::BulkData & mesh, + const InterfaceGeometry & interfaceGeometry, + const int step_count, + const std::vector> & periodic_node_pairs) +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::decompose_mesh()"); /* %TRACE% */ + stk::diag::TimeBlock root_timer__(CDFEM_Support::get(mesh.mesh_meta_data()).get_timer_cdfem()); + + CDFEM_Support & cdfemSupport = CDFEM_Support::get(mesh.mesh_meta_data()); + + if (!the_new_mesh) + { + // FIXME: This can cause problems for shells. + attach_sides_to_elements(mesh); + } + + + krinolog << "Decomposing mesh for region into phase conformal elements." << stk::diag::dendl; + NodeToCapturedDomainsMap nodesToCapturedDomains; + + { + the_new_mesh = std::make_shared(mesh, the_new_mesh); + + for(auto && pair : periodic_node_pairs) + { + the_new_mesh->add_periodic_node_pair(pair.first, pair.second); + } + + // Not sure if this is krino's responsibility or the driving application. If we have + // elemental death fields, these need to be parallel consistent on aura elements. + the_new_mesh->parallel_communicate_elemental_death_fields(); + + stk::diag::TimeBlock timer__(the_new_mesh->my_timer_decompose); + + if (cdfemSupport.get_cdfem_edge_degeneracy_handling() == SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE) + the_new_mesh->snap_and_update_fields_and_captured_domains(interfaceGeometry, nodesToCapturedDomains); + + interfaceGeometry.prepare_to_process_elements(the_new_mesh->stk_bulk(), nodesToCapturedDomains); + } + + { + stk::diag::TimeBlock timer__(the_new_mesh->my_timer_decompose); + + the_new_mesh->generate_nonconformal_elements(); + if (cdfemSupport.get_cdfem_edge_degeneracy_handling() == SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE) + the_new_mesh->snap_nearby_intersections_to_nodes(interfaceGeometry, nodesToCapturedDomains); + the_new_mesh->set_phase_of_uncut_elements(interfaceGeometry); + the_new_mesh->triangulate(interfaceGeometry); + the_new_mesh->decompose(); + } + + the_new_mesh->build_and_stash_old_mesh(step_count); + + const bool mesh_modified = the_new_mesh->modify_mesh(); + + the_new_mesh->prolongation(); + + // debugging + if ( krinolog.shouldPrint(LOG_DEBUG) ) + { + the_new_mesh->debug_output(); + } + + { + const ScaledJacobianQualityMetric qualityMetric; + krinolog << "After cutting quality is " << determine_quality(mesh, the_new_mesh->get_active_part(), qualityMetric) << stk::diag::dendl; + } + + + if (!the_new_mesh->aux_meta().using_fmwk()) + { + the_new_mesh->print_conformal_volumes_and_surface_areas(); + } + + const int status = mesh_modified ? (COORDINATES_MAY_BE_MODIFIED | MESH_MODIFIED) : COORDINATES_MAY_BE_MODIFIED; + return status; +} + +bool +CDMesh::modify_mesh() +{/* %TRACE[ON]% */ Trace trace__("krino::Mesh::modify_mesh()"); /* %TRACE% */ + stk::diag::TimeBlock timer__(my_timer_modify_mesh); + + ParallelThrowAssert(stk_bulk().parallel(), check_face_and_edge_ownership(stk_bulk())); + ParallelThrowAssert(stk_bulk().parallel(), check_face_and_edge_relations(stk_bulk())); + + set_entities_for_identical_nodes(); + const bool all_elems_are_set_and_correct = set_entities_for_existing_child_elements(); + + std::vector< stk::mesh::Entity> unused_old_child_elems; + get_unused_old_child_elements(unused_old_child_elems); + + const bool modificationIsNeeded = (my_cdfem_support.get_interface_maximum_refinement_level() > 0) || stk::is_true_on_any_proc(stk_bulk().parallel(), !all_elems_are_set_and_correct || !unused_old_child_elems.empty()); + + if (modificationIsNeeded) + { + stk::mesh::toggle_sideset_updaters(stk_bulk(), false); + + stk_bulk().modification_begin(); + create_node_entities(); + std::vector side_requests; + create_element_and_side_entities(side_requests); + destroy_custom_ghostings(stk_bulk()); + delete_mesh_entities(stk_bulk(), unused_old_child_elems); + stk_bulk().modification_end(); + ParallelThrowAssert(stk_bulk().parallel(), check_shared_entity_nodes(stk_bulk())); + + add_possible_interface_sides(side_requests); + batch_create_sides(stk_bulk(), side_requests); + + stk::mesh::toggle_sideset_updaters(stk_bulk(), true); + stk_bulk().modification_begin(); + update_node_activation(stk_bulk(), aux_meta().active_part()); // we should be able to skip this step if there are no higher order elements + update_element_side_parts(); + stk_bulk().modification_end(); + + ParallelThrowAssert(stk_bulk().parallel(), check_element_side_connectivity(stk_bulk(), aux_meta().exposed_boundary_part(), aux_meta().active_part())); + ParallelThrowAssert(stk_bulk().parallel(), check_element_side_parts()); + + aux_meta().induce_topology_nodesets(aux_meta().active_locally_owned_selector()); + + ParallelThrowAssert(stk_bulk().parallel(), check_induced_parts(stk_bulk())); + } + + return modificationIsNeeded; +} + +void +CDMesh::set_entities_for_identical_nodes() +{ + CDMesh* old_mesh = get_old_mesh(); + if (nullptr == old_mesh) return; + + for (auto && node : nodes) + { + if (!node->entity_is_valid(stk_bulk())) + { + const SubElementNode * old_node = node->find_node_with_common_ancestry(*old_mesh); + if (nullptr != old_node) + { + stk::mesh::Entity old_node_entity = old_node->entity(); + if (stk_bulk().is_valid(old_node_entity)) + { + node->set_entity(stk_bulk(), old_node_entity); + } + } + } + } +} + +void +CDMesh::parallel_communicate_elemental_death_fields() const +{ + std::vector< const stk::mesh::FieldBase *> element_fields; + for (auto && field : ls_fields()) + { + if (field.isovar.entity_rank() == stk::topology::ELEMENT_RANK) + { + element_fields.push_back(&field.isovar.field()); + } + } + stk::mesh::communicate_field_data(stk_bulk(), element_fields); +} + +bool +CDMesh::set_entities_for_existing_child_elements() +{ + CDMesh* old_mesh = get_old_mesh(); + if (nullptr == old_mesh) return false; + + std::vector subelem_node_entities; + std::vector existing_elems; + + bool all_element_entities_are_set_and_correct = true; + for (const auto & elem : elements) + { + if (elem->have_subelements()) + { + std::vector conformal_subelems; + elem->get_subelements(conformal_subelems); + + for (auto && subelem : conformal_subelems) + { + // If all nodes are set, look for existing element using the nodes. + subelem_node_entities.clear(); + for (auto&& subelem_node : subelem->get_nodes()) + { + stk::mesh::Entity node = subelem_node->entity(); + if (stk_bulk().is_valid(node)) subelem_node_entities.push_back(node); + } + existing_elems.clear(); + if (subelem_node_entities.size() == subelem->get_nodes().size()) + { + stk::mesh::get_entities_through_relations(stk_bulk(), subelem_node_entities, stk::topology::ELEMENT_RANK, existing_elems); + ThrowAssert(existing_elems.size() <= 1); + } + + if (existing_elems.empty()) + { + all_element_entities_are_set_and_correct = false; + } + else + { + subelem->set_entity(stk_bulk(), existing_elems[0]); + ThrowAssert(subelem->check_entity_nodes(stk_bulk())); + if (all_element_entities_are_set_and_correct && elem_io_part_changed(*subelem)) all_element_entities_are_set_and_correct = false; + } + } + } + else + { + if (all_element_entities_are_set_and_correct && elem_io_part_changed(*elem)) all_element_entities_are_set_and_correct = false; + } + } + return all_element_entities_are_set_and_correct; +} + +void +CDMesh::get_unused_old_child_elements(std::vector & unused_old_child_elems) +{ + stk::mesh::Selector selector = get_child_part(); + std::vector old_child_elems; + stk::mesh::get_selected_entities( selector, stk_bulk().buckets( stk::topology::ELEMENT_RANK ), old_child_elems ); + + unused_old_child_elems.clear(); + unused_old_child_elems.reserve(old_child_elems.size()); + + for (auto&& old_child_elem : old_child_elems) + { + const SubElement * subelem = find_child_element(old_child_elem); + if (subelem == nullptr) + { + unused_old_child_elems.push_back(old_child_elem); + } + } + child_elements.clear(); // reset child element vector +} + +bool +CDMesh::decomposition_needs_update(const InterfaceGeometry & interfaceGeometry, + const std::vector> & periodic_node_pairs) +{ + return !the_new_mesh || the_new_mesh->decomposition_has_changed(interfaceGeometry); +} + +void +CDMesh::mark_interface_elements_for_adaptivity(stk::mesh::BulkData & mesh, const InterfaceGeometry & interfaceGeometry, const std::string & marker_field_name, const int num_refinements) +{/* %TRACE[SPEC]% */ Tracespec trace__("CDMesh::mark_interface_elements_for_adaptivity(stk::mesh::BulkData & mesh, const std::string & marker_field_name, const int num_refinements)"); /* %TRACE% */ + + CDMesh cdmesh(mesh, std::shared_ptr()); + krino::mark_interface_elements_for_adaptivity(cdmesh.stk_bulk(), interfaceGeometry, cdmesh.active_interface_ids(), cdmesh.get_snapper(), cdmesh.aux_meta(), cdmesh.get_cdfem_support(), cdmesh.get_coords_field(), marker_field_name, num_refinements); +} + +void +CDMesh::nonconformal_adaptivity(stk::mesh::BulkData & mesh, const InterfaceGeometry & interfaceGeometry) +{/* %TRACE[SPEC]% */ Tracespec trace__("CDMesh::nonconformal_adaptivity(stk::mesh::BulkData & mesh)"); /* %TRACE% */ + + const auto & cdfem_support = CDFEM_Support::get(mesh.mesh_meta_data()); + stk::diag::TimeBlock timer__(cdfem_support.get_timer_adapt()); + + const std::string & marker_name = cdfem_support.get_nonconformal_adapt_marker_name(); + auto & h_adapt = cdfem_support.get_nonconformal_hadapt(); + + std::function marker_function = + [&mesh, &interfaceGeometry](const std::string & marker_field_name, int num_refinements) + { + mark_interface_elements_for_adaptivity(mesh, interfaceGeometry, marker_field_name, num_refinements); + }; + + perform_multilevel_adaptivity(mesh, marker_name, marker_function, h_adapt, cdfem_do_not_refine_or_unrefine_selector(cdfem_support)); +} + +void +CDMesh::rebuild_after_rebalance() +{ + clear(); + generate_nonconformal_elements(); + restore_subelement_edge_nodes(); + restore_subelements(); +} + +static bool side_is_adaptivity_or_cdfem_parent(stk::mesh::BulkData & mesh, stk::mesh::Entity side, const stk::mesh::Part & cdfemParentPart) +{ + if (mesh.num_connectivity(side, stk::topology::CONSTRAINT_RANK) > 0) + return true; + for (auto element : StkMeshEntities{mesh.begin_elements(side), mesh.end_elements(side)}) + if (mesh.bucket(element).member(cdfemParentPart)) + return true; + return false; +} + +void delete_extraneous_inactive_sides(stk::mesh::BulkData & mesh, const stk::mesh::Part & cdfemParentPart, const stk::mesh::Part & activePart) +{ + stk::mesh::Selector notActive = !activePart; + + std::vector sides; + stk::mesh::get_selected_entities(notActive, mesh.buckets(mesh.mesh_meta_data().side_rank()), sides, false); + + mesh.modification_begin(); + + for (auto && side : sides) + if (!side_is_adaptivity_or_cdfem_parent(mesh, side, cdfemParentPart)) + ThrowRequireMsg(disconnect_and_destroy_entity(mesh, side), "Could not destroy entity " << mesh.entity_key(side)); + + mesh.modification_end(); +} + +void +CDMesh::rebuild_from_restart_mesh(stk::mesh::BulkData & mesh) +{ + ParallelThrowRequire(mesh.parallel(), !the_new_mesh); + + the_new_mesh = std::make_shared(mesh, the_new_mesh); + the_new_mesh->rebuild_child_part(); + the_new_mesh->rebuild_parent_and_active_parts_using_nonconformal_and_child_parts(); + the_new_mesh->generate_nonconformal_elements(); + the_new_mesh->restore_subelement_edge_nodes(); + the_new_mesh->restore_subelements(); + + // rebuild conformal side parts + the_new_mesh->stk_bulk().modification_begin(); + the_new_mesh->update_element_side_parts(); + the_new_mesh->stk_bulk().modification_end(); + + delete_extraneous_inactive_sides(mesh, the_new_mesh->get_parent_part(), the_new_mesh->get_active_part()); +} + +void +CDMesh::rebuild_child_part() +{ + auto & child_part = get_child_part(); + auto & mesh = stk_bulk(); + + // Need to iterate all locally owned elements to find child elements, + // which are identified by detecting that they use child edge nodes + stk::mesh::EntityVector local_elements; + stk::mesh::Selector sel = get_active_part() & get_locally_owned_part(); + stk::mesh::get_selected_entities(sel, mesh.buckets(stk::topology::ELEMENT_RANK), + local_elements); + + auto conformal_selector = stk::mesh::selectUnion(my_phase_support.get_conformal_parts()); + const auto & child_edge_node_part = get_child_edge_node_part(); + mesh.modification_begin(); + for(auto && elem : local_elements) + { + auto elem_nodes = mesh.begin_nodes(elem); + auto num_nodes = mesh.num_nodes(elem); + bool is_child_elem = false; + for(unsigned i=0; i < num_nodes; ++i) + { + if(mesh.bucket(elem_nodes[i]).member(child_edge_node_part)) + { + is_child_elem = true; + break; + } + } + + if(is_child_elem) + { + mesh.change_entity_parts(elem, stk::mesh::ConstPartVector{&child_part}, stk::mesh::ConstPartVector{}); + } + } + mesh.modification_end(); +} + +void +CDMesh::rebuild_parent_and_active_parts_using_nonconformal_and_child_parts() +{ + auto & parent_part = get_parent_part(); + auto & mesh = stk_bulk(); + + stk::mesh::EntityVector entities; + stk::mesh::Selector sel = get_active_part() & get_locally_owned_part() & + stk::mesh::selectUnion(my_phase_support.get_nonconformal_parts()); + stk::mesh::get_selected_entities(sel, mesh.buckets(stk::topology::ELEMENT_RANK), + entities); + + mesh.modification_begin(); + for(auto && elem : entities) + { + mesh.change_entity_parts(elem, stk::mesh::ConstPartVector{&parent_part}, stk::mesh::ConstPartVector{&get_active_part()}); + } + + // Also remove active part from nonconformal sides + entities.clear(); + auto side_rank = mesh.mesh_meta_data().side_rank(); + stk::mesh::get_selected_entities(sel, mesh.buckets(side_rank), entities); + for(auto && side : entities) + { + mesh.change_entity_parts(side, stk::mesh::ConstPartVector{}, stk::mesh::ConstPartVector{&get_active_part()}); + } + + // Also need to mark parents in undecomposed blocks that have children due to hanging + // nodes + entities.clear(); + stk::mesh::Selector undecomposed_child_sel = get_active_part() & get_locally_owned_part() & + !my_phase_support.get_all_decomposed_blocks_selector() & + get_child_part(); + stk::mesh::get_selected_entities(undecomposed_child_sel, mesh.buckets(stk::topology::ELEMENT_RANK), + entities); + for(auto && elem : entities) + { + auto parent_elem = get_parent_element(elem); + ThrowRequire(mesh.is_valid(parent_elem)); + mesh.change_entity_parts(parent_elem, stk::mesh::ConstPartVector{&parent_part}, stk::mesh::ConstPartVector{&get_active_part()}); + } + mesh.modification_end(); +} + +const SubElementNode * +CDMesh::build_subelement_edge_node(const stk::mesh::Entity node_entity) +{ + const auto & mesh = stk_bulk(); + + // This is super inefficient but lets just get it working for now. + ThrowRequire(mesh.is_valid(node_entity)); + auto id = mesh.identifier(node_entity); + auto find_existing = std::find_if(nodes.begin(), nodes.end(), + [id](const std::unique_ptr & compare) + { return compare->entityId() == id; }); + if(find_existing != nodes.end()) + return find_existing->get(); + + auto parent_ids = get_edge_node_parent_ids(mesh, get_parent_node_ids_field(), node_entity); + + auto search0 = std::find_if(nodes.begin(), nodes.end(), + [parent_ids](const std::unique_ptr & compare) + { return compare->entityId() == parent_ids[0]; }); + const SubElementNode * immediate_parent0 = (search0 == nodes.end()) ? + build_subelement_edge_node(mesh.get_entity(stk::topology::NODE_RANK, parent_ids[0])) : + search0->get(); + + auto search1 = std::find_if(nodes.begin(), nodes.end(), + [parent_ids](const std::unique_ptr & compare) + { return compare->entityId() == parent_ids[1]; }); + const SubElementNode * immediate_parent1 = (search1 == nodes.end()) ? + build_subelement_edge_node(mesh.get_entity(stk::topology::NODE_RANK, parent_ids[1])) : + search1->get(); + + // For LS per phase the immediate parents are not necessarily actual parent element nodes, + // we need to go all the way up the parentage tree to find the original parent nodes so we can + // get the parent Mesh_Element. + std::set parent_elem_nodes_set; + get_parent_nodes_from_child( + mesh, node_entity, get_parent_node_ids_field(), parent_elem_nodes_set); + std::vector parent_elem_nodes( + parent_elem_nodes_set.begin(), parent_elem_nodes_set.end()); + std::vector parent_elems; + stk::mesh::get_entities_through_relations(stk_bulk(), parent_elem_nodes, stk::topology::ELEMENT_RANK, parent_elems); + + stk::mesh::Entity owner_entity; + const stk::mesh::Selector locally_owned_parent_selector = get_locally_owned_part() & get_parent_part(); + for(auto && e : parent_elems) + { + if(locally_owned_parent_selector(mesh.bucket(e))) + { + owner_entity = e; + break; + } + } + if(!mesh.is_valid(owner_entity)) + { + std::ostringstream err_msg; + err_msg << "Failed to find locally owned element connected to nodes" + << immediate_parent0->entityId() << " and " << immediate_parent1->entityId() + << " when restoring CDFEM data structures after restart.\n"; + err_msg << "Possible parent elems:\n"; + for(auto && e : parent_elems) + { + err_msg << " " << debug_entity(mesh, e) << "\n"; + } + throw std::runtime_error(err_msg.str()); + } + + auto owner_id = mesh.identifier(owner_entity); + const Mesh_Element * owner = find_mesh_element(owner_id); + ThrowRequireMsg(owner, "Could not find Mesh_Element for owner element " << owner_id << " " << debug_entity(mesh, owner_entity)); + const double position = compute_child_position( + mesh, node_entity, immediate_parent0->entity(), immediate_parent1->entity()); + std::unique_ptr newNode = std::make_unique(owner, position, immediate_parent0, immediate_parent1); + SubElementNode * edgeNode = add_managed_node(std::move(newNode)); + edgeNode->set_entity(stk_bulk(), node_entity); + return edgeNode; +} + +void +CDMesh::restore_subelement_edge_nodes() +{ + stk::mesh::Selector selector = get_child_edge_node_part() & + (get_locally_owned_part() | get_globally_shared_part()); + const auto & mesh = stk_bulk(); + const auto & buckets = mesh.get_buckets(stk::topology::NODE_RANK, selector); + const auto parent_id_field = get_parent_node_ids_field(); + + std::vector< const stk::mesh::FieldBase *> fields(1, &parent_id_field.field()); + stk::mesh::communicate_field_data(mesh, fields); + + for(const auto & b_ptr : buckets) + { + for(unsigned i=0; i < b_ptr->size(); ++i) + { + build_subelement_edge_node((*b_ptr)[i]); + } + } +} + +void +CDMesh::restore_subelements() +{ + stk::mesh::Selector selector = get_locally_owned_part() & get_child_part(); + const auto & mesh = stk_bulk(); + const auto & buckets = mesh.get_buckets(stk::topology::ELEMENT_RANK, selector); + NodeVec subelem_nodes; + for(const auto & b_ptr : buckets) + { + const stk::topology & topo = b_ptr->topology(); + const unsigned num_nodes = topo.num_nodes(); + subelem_nodes.reserve(num_nodes); + for(const auto & elem : *b_ptr) + { + const stk::mesh::Entity parent = get_parent_element(elem); + ThrowRequire(mesh.is_valid(parent) && parent != elem); + + auto parent_mesh_elem = find_mesh_element(mesh.identifier(parent)); + ThrowRequire(parent_mesh_elem); + + subelem_nodes.clear(); + // TODO: May need to create subelement edge nodes somehow + const auto * elem_nodes = mesh.begin_nodes(elem); + ThrowAssert(mesh.num_nodes(elem) == num_nodes); + for(unsigned i=0; i < num_nodes; ++i) + { + auto node_id = mesh.identifier(elem_nodes[i]); + auto find_node = std::find_if(nodes.begin(), nodes.end(), + [node_id](const std::unique_ptr & compare) + { return compare->entityId() == node_id; }); + ThrowRequire(find_node != nodes.end()); + subelem_nodes.push_back(find_node->get()); + } + + std::unique_ptr subelem; + switch(topo) + { + case stk::topology::TRIANGLE_3_2D: + subelem = std::make_unique(subelem_nodes, std::vector{-1, -1, -1}, parent_mesh_elem); + break; + case stk::topology::TETRAHEDRON_4: + subelem = std::make_unique(subelem_nodes, std::vector{-1, -1, -1, -1}, parent_mesh_elem); + break; + default: + ThrowRuntimeError("At present only Tri3 and Tet4 topologies are supported for restart of CDFEM problems."); + } + ThrowAssert(subelem); + subelem->set_entity(stk_bulk(), elem); + const_cast(parent_mesh_elem)->add_subelement(std::move(subelem)); + } + } +} + +void +CDMesh::delete_cdfem_parent_elements() +{/* %TRACE[SPEC]% */ Tracespec trace__("Mesh::delete_cdfem_parent_elements(stk::mesh::BulkData & mesh)"); /* %TRACE% */ + // Percept messes up the cdfem child/parent parts, the active part, and the percept refined part for post-cdfem refinement. + // This is kind of an extreme work-around, but here we delete all of the cdfem parents prior to the post-cdfem refiment so that + // there is nothing to mess up. + + std::vector< stk::mesh::Entity> cdfem_parent_elements; + + stk::mesh::Selector selector = get_parent_part(); + stk::mesh::get_selected_entities( selector, stk_bulk().buckets( stk::topology::ELEMENT_RANK ), cdfem_parent_elements ); + + stk_bulk().modification_begin(); + delete_mesh_entities(stk_bulk(), cdfem_parent_elements); + stk_bulk().modification_end(); +} + +void +CDMesh::fixup_adapted_element_parts(stk::mesh::BulkData & mesh) +{/* %TRACE[SPEC]% */ Tracespec trace__("Mesh::fixup_adapted_element_parts(CDFEM_Support & cdfem_support)"); /* %TRACE% */ + // Fixup volume parts that currently can be messed up by adaptivity. + // There are two types of fixes: + // 1. CDFEM parent elements that are activated by Encore adaptivity (this is expected, but needs to be fixed). + // 2. Conformal elements that have somehow picked up the non-conformal part (this probably shouldn't happen). + + Phase_Support & phase_support = Phase_Support::get(mesh.mesh_meta_data()); + CDFEM_Support & cdfem_support = CDFEM_Support::get(mesh.mesh_meta_data()); + AuxMetaData & aux_meta = AuxMetaData::get(mesh.mesh_meta_data()); + stk::mesh::Selector cdfem_parent_selector = cdfem_support.get_parent_part(); + + stk::mesh::Selector locally_owned_selector(mesh.mesh_meta_data().locally_owned_part()); + std::vector entities; + + std::vector remove_parts; + stk::mesh::PartVector bucket_remove_parts; + const stk::mesh::BucketVector & buckets = mesh.get_buckets(stk::topology::ELEMENT_RANK, locally_owned_selector); + for (auto&& bucket_ptr : buckets) + { + unsigned num_volume_parts = 0; + stk::mesh::Part * extraneous_nonconformal_part = nullptr; + const stk::mesh::PartVector & bucket_parts = bucket_ptr->supersets(); + bucket_remove_parts.clear(); + for(auto&& part : bucket_parts) + { + if (part->primary_entity_rank() == stk::topology::ELEMENT_RANK && part->subsets().empty() && part->topology() != stk::topology::INVALID_TOPOLOGY) + { + ++num_volume_parts; + if (phase_support.is_nonconformal(part)) + { + extraneous_nonconformal_part = part; + } + } + } + if (num_volume_parts > 1) + { + bucket_remove_parts.push_back(extraneous_nonconformal_part); + } + if (cdfem_parent_selector(*bucket_ptr)) + { + bucket_remove_parts.push_back(&aux_meta.active_part()); + } + if (!bucket_remove_parts.empty()) + { + entities.insert(entities.end(), bucket_ptr->begin(), bucket_ptr->end()); + remove_parts.insert(remove_parts.end(), bucket_ptr->size(), bucket_remove_parts); + } + } + stk::mesh::PartVector empty; + std::vector add_parts(entities.size(), empty); + + // This seems like a bug. For some reason batch_change_entity_parts does not work the same as calling change_entity_parts within a full modification cycle. + //mesh.bulk().batch_change_entity_parts(entities, add_parts, remove_parts); + mesh.modification_begin(); + for(size_t i=0; iget_edge_interpolation_model()) + { + return; + } + + for (auto && node : mesh->nodes) + { + const krino::SubElementEdgeNode* edge_node = dynamic_cast(node.get()); + if (nullptr == edge_node) continue; + + NodeVec parents = edge_node->get_parents(); + const double ratio = edge_node->get_position(); + + stk::mesh::Entity node_obj = edge_node->entity(); + stk::mesh::Entity parent0_obj = parents[0]->entity(); + stk::mesh::Entity parent1_obj = parents[1]->entity(); + + const int num_ls_fields = mesh->num_ls_fields(); + for (int ls_index=0; ls_indexls_field(ls_index); + double & node_val = *field_data( ls_field.isovar , node_obj); + const double & parent0_val = *field_data( ls_field.isovar , parent0_obj); + const double & parent1_val = *field_data( ls_field.isovar , parent1_obj); + + const double interp_val = (1.-ratio) * parent0_val + ratio * parent1_val; + node_val = interp_val; + } + } +} + +void +CDMesh::stash_field_data(const int step_count, const CDMesh & new_mesh) const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::stash_field_data(const int step_count)"); /* %TRACE% */ + stk::diag::TimeBlock timer__(my_timer_stash_field_data); + my_stash_step_count = step_count; + clear_prolongation_data(); + + stash_nodal_field_data(new_mesh); + stash_elemental_field_data(); +} + +//-------------------------------------------------------------------------------- + +static void fill_nodes_of_elements_with_subelements_or_changed_phase(const stk::mesh::BulkData & mesh, + const std::vector> & newMeshElements, + const std::vector> & oldMeshElements, + std::set & nodesOfElements) +{ + nodesOfElements.clear(); + + for (auto && element : newMeshElements) + { + bool haveSubelementsOrChangedPhase = false; + if (element->have_subelements()) + { + haveSubelementsOrChangedPhase = true; + } + else + { + const Mesh_Element * oldElement = CDMesh::find_mesh_element(element->entityId(), oldMeshElements); + if (nullptr == oldElement || element->get_phase() != oldElement->get_phase()) + haveSubelementsOrChangedPhase = true; + } + + if (haveSubelementsOrChangedPhase) + for (auto && node : element->get_nodes()) + nodesOfElements.insert(node->entity()); + } +} + +static +void pack_shared_nodes_for_sharing_procs(const stk::mesh::BulkData & mesh, + const std::set & nodes, + stk::CommSparse &commSparse) +{ + std::vector nodeSharedProcs; + stk::pack_and_communicate(commSparse,[&]() + { + for (auto node : nodes) + { + if (mesh.bucket(node).shared()) + { + mesh.comm_shared_procs(node, nodeSharedProcs); + for (int procId : nodeSharedProcs) + commSparse.send_buffer(procId).pack(mesh.identifier(node)); + } + } + }); +} + +static +void unpack_shared_nodes(const stk::mesh::BulkData & mesh, + std::set & nodes, + stk::CommSparse &commSparse) +{ + stk::unpack_communications(commSparse, [&](int procId) + { + stk::CommBuffer & buffer = commSparse.recv_buffer(procId); + + while ( buffer.remaining() ) + { + stk::mesh::EntityId nodeId; + commSparse.recv_buffer(procId).unpack(nodeId); + stk::mesh::Entity node = mesh.get_entity(stk::topology::NODE_RANK, nodeId); + ThrowRequire(mesh.is_valid(node)); + nodes.insert(node); + } + }); +} + +static std::set get_nodes_of_elements_with_subelements_or_have_changed_phase(const stk::mesh::BulkData & mesh, const std::vector> & newMeshElements, + const std::vector> & oldMeshElements) +{ + std::set nodesOfElements; + fill_nodes_of_elements_with_subelements_or_changed_phase(mesh, newMeshElements, oldMeshElements, nodesOfElements); + + stk::CommSparse commSparse(mesh.parallel()); + pack_shared_nodes_for_sharing_procs(mesh, nodesOfElements, commSparse); + unpack_shared_nodes(mesh, nodesOfElements, commSparse); + + return nodesOfElements; +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::stash_nodal_field_data(const CDMesh & new_mesh) const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::stash_nodal_field_data()"); /* %TRACE% */ + + // stash child nodes + { + stk::mesh::Selector selector = get_locally_owned_part() & get_child_part(); + + stk::mesh::BucketVector const& buckets = stk_bulk().get_buckets( stk::topology::ELEMENT_RANK, selector ); + for ( auto&& bucket_ptr : buckets ) + { + const stk::mesh::Bucket & b = *bucket_ptr; + const size_t length = b.size(); + for (size_t ielem = 0; ielem < length; ++ielem) + { + stk::mesh::Entity elem = b[ielem]; + const unsigned num_elem_nodes = stk_bulk().num_nodes(elem); + const stk::mesh::Entity* elem_nodes = stk_bulk().begin_nodes(elem); + + for (unsigned inode = 0; inode < num_elem_nodes; ++inode) + { + stk::mesh::Entity node = elem_nodes[inode]; + ThrowAssert((stk_bulk().bucket(node).member(get_active_part()))); + ProlongationNodeData *& node_data = my_prolong_node_map[stk_bulk().identifier(node)]; + if (nullptr == node_data) + { + const bool communicate_me_to_all_sharers = stk_bulk().bucket(node).member(get_globally_shared_part()); + node_data = new ProlongationNodeData(*this, node, communicate_me_to_all_sharers); + } + } + } + } + } + + // Stash all nodes of elements that have child elements or have changed phase. + // Due to hanging nodes, etc, this is more than just the cut elements. + for (auto&& node : get_nodes_of_elements_with_subelements_or_have_changed_phase(stk_bulk(), new_mesh.elements, elements)) + { + if (stk_bulk().bucket(node).member(get_active_part())) // Don't stash inactive midside nodes + { + ThrowAssert(stk_bulk().is_valid(node)); + ProlongationNodeData *& node_data = my_prolong_node_map[stk_bulk().identifier(node)]; + if (nullptr == node_data) + { + const bool communicate_me_to_all_sharers = stk_bulk().bucket(node).member(get_globally_shared_part()); + node_data = new ProlongationNodeData(*this, node, communicate_me_to_all_sharers); + } + } + } + + // stash all inter-block nodes + if (need_nodes_for_prolongation()) + { + stk::mesh::Selector active_not_ghost_selector(get_active_part() & (get_locally_owned_part() | get_globally_shared_part())); + const stk::mesh::BucketVector & buckets = stk_bulk().get_buckets(stk::topology::NODE_RANK, active_not_ghost_selector); + for (auto&& bucket_ptr : buckets) + { + unsigned num_conformal_parts = 0; + const stk::mesh::PartVector & node_parts = bucket_ptr->supersets(); + for(auto&& node_part_ptr : node_parts) + { + // This is designed to catch side with block_2 + block_1_air, block_1_air + block_1_solid, etc. + if (node_part_ptr->primary_entity_rank() == stk::topology::ELEMENT_RANK && + !my_phase_support.is_nonconformal(node_part_ptr) && + stk::io::is_part_io_part(*node_part_ptr)) + { + ++num_conformal_parts; + } + } + + if (num_conformal_parts > 1) + { + for (auto&& node : *bucket_ptr) + { + ProlongationNodeData *& node_data = my_prolong_node_map[stk_bulk().identifier(node)]; + if (nullptr == node_data) + { + node_data = new ProlongationNodeData(*this, node, false); + } + } + } + } + } + + // build facets if needed + if (need_facets_for_prolongation()) + { + stk::mesh::Selector active_locally_owned_selector(get_active_part() & get_locally_owned_part()); + + const stk::mesh::BucketVector & buckets = stk_bulk().get_buckets(stk_bulk().mesh_meta_data().side_rank(), active_locally_owned_selector); + for (auto&& bucket_ptr : buckets) + { + unsigned num_conformal_parts = 0; + const stk::mesh::PartVector & side_parts = bucket_ptr->supersets(); + for(auto&& side_part_ptr : side_parts) + { + // This is designed to catch side with block_2 + block_1_air, block_1_air + block_1_solid, etc. + if (side_part_ptr->primary_entity_rank() == stk::topology::ELEMENT_RANK && + !my_phase_support.is_nonconformal(side_part_ptr) && + stk::io::is_part_io_part(*side_part_ptr)) + { + ++num_conformal_parts; + } + } + + if (num_conformal_parts > 1) + { + for (auto&& side : *bucket_ptr) + { + ThrowAssert( stk_bulk().num_elements(side) > 0 ); + + ProlongationFacet * prolong_facet = new ProlongationFacet(*this, side); + my_prolong_facets.push_back(prolong_facet); + } + } + } + } +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::stash_elemental_field_data() const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::stash_elemental_field_data()"); /* %TRACE% */ + const FieldSet & element_fields = get_element_fields(); + if (element_fields.empty()) return; + + for (const auto & mesh_elem : elements) + { + const stk::mesh::EntityId elem_id = mesh_elem->entityId(); + stk::mesh::Entity elem = mesh_elem->entity(); + ThrowAssert(stk_bulk().is_valid(elem)); + + if(mesh_elem->have_subelements()) + { + std::vector conformal_subelems; + mesh_elem->get_subelements(conformal_subelems); + + const unsigned num_child = conformal_subelems.size(); + std::vector child_data(num_child); + std::vector< std::vector > child_intg_wts(num_child); + + for (unsigned j=0; jentityId(); + stk::mesh::Entity subelem_entity = stk_bulk().get_entity(stk::topology::ELEMENT_RANK, subelem_id); //EXPENSIVE! + ProlongationElementData * subelem_data = new ProlongationElementData(stk_bulk(), subelem_entity); + ThrowAssertMsg(0 == my_prolong_element_map.count(subelem_id), "Duplicate subelement entityId " << subelem_id); + my_prolong_element_map[subelem_id] = subelem_data; + subelem->set_prolongation_data(subelem_data); + child_data[j] = subelem_data; + + subelem->integration_weights(child_intg_wts[j]); + } + + const bool single_coincident_subelement = (num_child == 1); + if (!single_coincident_subelement) + { + ProlongationElementData * elem_data = new ProlongationElementData(stk_bulk(), child_data, child_intg_wts); + ThrowAssert(0 == my_prolong_element_map.count(elem_id)); + my_prolong_element_map[elem_id] = elem_data; + mesh_elem->set_prolongation_data(elem_data); + } + } + else + { + ProlongationElementData * elem_data = new ProlongationElementData(stk_bulk(), elem); + ThrowAssert(0 == my_prolong_element_map.count(elem_id)); + my_prolong_element_map[elem_id] = elem_data; + mesh_elem->set_prolongation_data(elem_data); + } + } +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::clear_prolongation_trees() const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::clear_prolongation_trees() const"); /* %TRACE% */ + my_phase_prolong_tree_map.clear(); +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::build_prolongation_trees() const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::build_prolongation_trees() const"); /* %TRACE% */ + + clear_prolongation_trees(); + + if (need_facets_for_prolongation()) + { + std::map,std::vector> phase_prolong_facet_map; + + for ( unsigned n=0; nget_common_fields()].push_back(prolong_facet); + } + + for (auto && entry : phase_prolong_facet_map) + { + const std::vector & fields = entry.first; + std::vector & facets = entry.second; + + my_phase_prolong_tree_map[fields] = std::make_unique>(facets, ProlongationFacet::get_bounding_box); + ThrowAssert(!my_phase_prolong_tree_map[fields]->empty()); + } + } +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::communicate_prolongation_facet_fields() const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::communicate_prolongation_facet_fields() const"); /* %TRACE% */ + + if (!need_facets_for_prolongation()) + return; + + const int num_procs = stk_bulk().parallel_size(); + if ( num_procs == 1 ) return; // Don't talk to yourself, it's embarrassing + const int me = stk_bulk().parallel_rank(); + + // formulate messages + stk::CommSparse comm_sparse(stk_bulk().parallel()); + + const size_t map_size = my_phase_prolong_tree_map.size(); + + for (int pass=0; pass<2; ++pass) + { + for ( int p=0; p & facet_fields = entry.first; + b.pack(facet_fields.size()); + for (unsigned field : facet_fields) + b.pack(field); + } + + ThrowAssert( pass == 0 || 0 == b.remaining() ); + } + + if (pass == 0) + { + comm_sparse.allocate_buffers(); + } + else + { + // send/receive + comm_sparse.communicate(); + } + } + + for ( int p=0; p facet_fields(num_fields); + for (size_t ifield = 0; ifield fieldOrdinals) +{ + const stk::mesh::FieldVector & all_fields = meta.get_fields(); + std::ostringstream os; + os << "Fields { "; + for (unsigned fieldOrdinal : fieldOrdinals) + os << all_fields[fieldOrdinal]->name() << " "; + os << "}"; + return os.str(); +} + +//-------------------------------------------------------------------------------- + +const ProlongationPointData * +CDMesh::find_prolongation_node(const SubElementNode & dst_node) const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::find_prolongation_node(const SubElementNode & dst_node) const"); /* %TRACE% */ + + const Vector3d & dst_node_coords = dst_node.coordinates(); + const ProlongationPointData * src_data = nullptr; + + const std::vector required_fields = dst_node.prolongation_node_fields(*this); + + ThrowRequire(need_facets_for_prolongation()); + + const ProlongationFacet * nearest_prolong_facet = nullptr; + bool matching_empty_tree = false; + FacetDistanceQuery nearest_facet_query; + for (auto && entry : my_phase_prolong_tree_map) + { + const std::vector & tree_fields = entry.first; + SearchTree * facet_tree = entry.second.get(); + + if (std::includes(tree_fields.begin(), tree_fields.end(), required_fields.begin(), required_fields.end())) + { + if (nullptr == facet_tree) + { + matching_empty_tree = true; + continue; + } + std::vector nearest_prolong_facets; + facet_tree->find_closest_entities( dst_node_coords, nearest_prolong_facets ); + ThrowAssert(!nearest_prolong_facets.empty()); + + for (auto && prolong_facet : nearest_prolong_facets) + { + FacetDistanceQuery facet_query(*prolong_facet->get_facet(), dst_node_coords); + if (nearest_facet_query.empty() || facet_query.distance_squared() < nearest_facet_query.distance_squared()) + { + nearest_prolong_facet = prolong_facet; + nearest_facet_query = facet_query; + } + } + } + } + + if(nullptr != nearest_prolong_facet) + { + src_data = nearest_prolong_facet->get_prolongation_point_data(nearest_facet_query); + if (krinolog.shouldPrint(LOG_DEBUG)) + { + const std::vector & facet_nodes = nearest_prolong_facet->get_prolongation_nodes(); + krinolog << "Prolongation facet for " << dst_node.entityId() << " has nodes "; + for (auto&& node : facet_nodes) + { + krinolog << node->entityId() << " "; + } + krinolog << stk::diag::dendl; + } + } + + if( nullptr == src_data ) + { + if (matching_empty_tree) + { + my_missing_remote_prolong_facets = true; + if (krinolog.shouldPrint(LOG_DEBUG)) krinolog << "Found missing remote prolong facet for node for " << dst_node.entityId() << stk::diag::dendl; + return nullptr; + } + // Search for facet failed. Now try nodes. This will handle triple points. Something better that handles an actual edge search might be better in 3d. + const ProlongationNodeData * closest_node = nullptr; + double closest_dist2 = std::numeric_limits::max(); + for (auto && entry : my_prolong_node_map) + { + const ProlongationNodeData * node = entry.second; + const std::vector & tree_fields = node->get_fields(); + if (std::includes(tree_fields.begin(), tree_fields.end(), required_fields.begin(), required_fields.end())) + { + const double dist2 = (node->get_coordinates()-dst_node_coords).length_squared(); + if (dist2 < closest_dist2) + { + closest_node = node; + closest_dist2 = dist2; + } + } + } + if (nullptr != closest_node) + { + src_data = closest_node; + if (krinolog.shouldPrint(LOG_DEBUG)) krinolog << "Prolongation node for " << dst_node.entityId() << " is " << closest_node->entityId() << stk::diag::dendl; + } + } + + if (nullptr == src_data) + { + krinolog << "Failed to find prolongation node for node#" << dst_node.entityId() << stk::diag::dendl; + if (krinolog.shouldPrint(LOG_DEBUG)) + { + krinolog << " with required part fields=" << print_fields(stk_meta(), required_fields) << stk::diag::dendl; + krinolog << " with parts="; + const stk::mesh::PartVector & parts = stk_bulk().bucket(dst_node.entity()).supersets(); + for(stk::mesh::PartVector::const_iterator part_iter = parts.begin(); part_iter != parts.end(); ++part_iter) + { + const stk::mesh::Part * const part = *part_iter; + krinolog << "\"" << part->name() << "\"" << " "; + } + krinolog << stk::diag::dendl; + const unsigned num_dst_node_elements = stk_bulk().num_elements(dst_node.entity()); + const stk::mesh::Entity* dst_node_elements = stk_bulk().begin_elements(dst_node.entity()); + for (unsigned dst_node_elem_index=0; dst_node_elem_indexname() << "\"" << " "; + } + krinolog << stk::diag::dendl; + } + + krinolog << "Candidate prolongation facets:" << stk::diag::dendl; + for (auto && entry : my_phase_prolong_tree_map) + { + const std::vector & tree_fields = entry.first; + krinolog << " matching fields=" << std::includes(tree_fields.begin(), tree_fields.end(), required_fields.begin(), required_fields.end()) + << ", tree fields=" << print_fields(stk_meta(), tree_fields) + << stk::diag::dendl; + } + } + } + else if(krinolog.shouldPrint(LOG_DEBUG)) + { + krinolog << "Prolongation data for node#" << stk_bulk().identifier(dst_node.entity()) << " (" << dst_node.coordinates() << ")" + << " will be point at location (" << src_data->get_coordinates() << ")" << stk::diag::dendl; + + } + + return src_data; +} + +//-------------------------------------------------------------------------------- + +const SubElementNode * +CDMesh::find_node_with_common_ancestry(const SubElementNode * new_node) const +{ + return new_node->find_node_with_common_ancestry(*this); +} + +static bool entity_has_any_node_in_selector(const stk::mesh::BulkData & mesh, stk::mesh::Entity entity, const stk::mesh::Selector & selector) +{ + const unsigned numNodes = mesh.num_nodes(entity); + stk::mesh::Entity const* entityNodes = mesh.begin_nodes(entity); + for (unsigned n=0; n +CDMesh::get_nonconformal_elements() const +{ + std::vector elems; + const auto & all_decomposed_blocks_selector = my_phase_support.get_all_decomposed_blocks_selector(); + stk::mesh::Selector selector = get_locally_owned_part() & (get_parent_part() | (get_active_part() & !get_child_part())); + stk::mesh::BucketVector const& buckets = stk_bulk().get_buckets(stk::topology::ELEMENT_RANK, selector); + + for (auto&& bucket : buckets) + { + const stk::topology topology = bucket->topology(); + if (Mesh_Element::is_supported_topology(topology)) + for (auto&& elem : *bucket) + if (entity_has_any_node_in_selector(stk_bulk(), elem, all_decomposed_blocks_selector)) + elems.push_back(elem); + } + + std::sort(elems.begin(), elems.end(), stk::mesh::EntityLess(stk_bulk())); + + return elems; +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::generate_nonconformal_elements() +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::generate_nonconformal_elements()"); /* %TRACE% */ + ParallelThrowRequire(stk_bulk().parallel(), nodes.empty()); + ParallelThrowRequire(stk_bulk().parallel(), elements.empty()); + + const std::vector nonconformalElems = get_nonconformal_elements(); + elements.reserve(nonconformalElems.size()); + for(auto && elem : nonconformalElems) + { + auto mesh_elem = std::make_unique(*this, elem); + elements.emplace_back(std::move(mesh_elem)); + } +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::clear() +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::clear()"); /* %TRACE% */ + + nodes.clear(); + elements.clear(); + + clear_prolongation_data(); + + mesh_node_map.clear(); + child_elements.clear(); +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::clear_prolongation_data() const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::clear()"); /* %TRACE% */ + for (auto && map_entry : my_prolong_node_map) + { + delete map_entry.second; + } + my_prolong_node_map.clear(); + for (auto && map_entry : my_prolong_element_map) + { + delete map_entry.second; + } + my_prolong_element_map.clear(); + + for (auto && prolong_facet : my_prolong_facets) + { + delete prolong_facet; + } + my_prolong_facets.clear(); + + clear_prolongation_trees(); +} + +bool +CDMesh::is_interface(const PhaseTag & phase, const InterfaceID interface) const +{ + return phase_matches_interface(my_cdfem_support, phase, interface); +} + +//-------------------------------------------------------------------------------- + +PhaseTag +CDMesh::determine_entity_phase(stk::mesh::Entity entity) const +{ + return determine_phase_for_entity(stk_bulk(), entity, my_phase_support); +} + +//-------------------------------------------------------------------------------- + +bool +CDMesh::elem_io_part_changed(const ElementObj & elem) const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::verify_elem_part(const Mesh_Element * elem) const"); /* %TRACE% */ + const stk::mesh::Part & current_elem_io_part = find_element_part(stk_bulk(),elem.entity()); + const stk::mesh::Part * const conformal_elem_io_part = my_phase_support.find_conformal_io_part(current_elem_io_part, elem.get_phase()); + return (¤t_elem_io_part != conformal_elem_io_part || !stk_bulk().bucket(elem.entity()).member(get_active_part())); +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::determine_nonconformal_parts( + stk::mesh::Entity entity, + stk::mesh::PartVector & add_parts, + stk::mesh::PartVector & remove_parts) const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::determine_nonconformal_parts(stk::mesh::Entity entity, stk::mesh::PartVector & add_parts, stk::mesh::PartVector & remove_parts) const"); /* %TRACE% */ + + add_parts.clear(); + remove_parts.clear(); + + const auto & all_decomposed_blocks_selector = my_phase_support.get_all_decomposed_blocks_selector(); + stk::mesh::EntityRank entity_rank = stk_bulk().entity_rank(entity); + const stk::mesh::PartVector & current_parts = stk_bulk().bucket(entity).supersets(); + for(stk::mesh::PartVector::const_iterator part_iter = current_parts.begin(); part_iter != current_parts.end(); ++part_iter) + { + stk::mesh::Part & part = **part_iter; + if( part.primary_entity_rank() == entity_rank && all_decomposed_blocks_selector(part) ) + { + stk::mesh::Part * nonconformal_io_part = const_cast(my_phase_support.find_nonconformal_part(part)); + if (nullptr != nonconformal_io_part && nonconformal_io_part != &part) + { + add_parts.push_back(nonconformal_io_part); + remove_parts.push_back(&part); + + for(stk::mesh::PartVector::const_iterator sup_it = part.supersets().begin(); sup_it != part.supersets().end(); ++sup_it) + { + stk::mesh::Part & superset = **sup_it; + if (!stk::mesh::is_auto_declared_part(superset)) + { + remove_parts.push_back(&superset); + } + } + } + } + } + + // Set to inactive + remove_parts.push_back(&aux_meta().active_part()); + + if (entity_rank == stk::topology::ELEMENT_RANK) + { + add_parts.push_back(&get_parent_part()); + remove_parts.push_back(&get_child_part()); + } +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::determine_conformal_parts( + const stk::mesh::PartVector & current_parts, + const stk::mesh::EntityRank entity_rank, + const PhaseTag & phase, + stk::mesh::PartVector & add_parts, + stk::mesh::PartVector & remove_parts) const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::determine_conformal_parts(stk::mesh::Entity entity, const PhaseTag & phase, stk::mesh::PartVector & add_parts, stk::mesh::PartVector & remove_parts) const"); /* %TRACE% */ + + const auto & all_decomposed_blocks_selector = my_phase_support.get_all_decomposed_blocks_selector(); + for(stk::mesh::PartVector::const_iterator part_iter = current_parts.begin(); part_iter != current_parts.end(); ++part_iter) + { + stk::mesh::Part & part = **part_iter; + if( part.primary_entity_rank() == entity_rank && + (stk::io::is_part_io_part(part) || all_decomposed_blocks_selector(&part)) ) + { + stk::mesh::Part * conformal_elem_io_part = const_cast(my_phase_support.find_conformal_io_part(part, phase)); + if (nullptr != conformal_elem_io_part && conformal_elem_io_part != &part) + { + add_parts.push_back(conformal_elem_io_part); + remove_parts.push_back(&part); + + for(stk::mesh::PartVector::const_iterator sup_it = part.supersets().begin(); sup_it != part.supersets().end(); ++sup_it) + { + stk::mesh::Part & superset = **sup_it; + if (!stk::mesh::is_auto_declared_part(superset)) + { + remove_parts.push_back(&superset); + } + } + } + } + } +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::determine_conformal_parts( + stk::mesh::Entity entity, + const PhaseTag & phase, + stk::mesh::PartVector & add_parts, + stk::mesh::PartVector & remove_parts) const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::determine_conformal_parts(stk::mesh::Entity entity, const PhaseTag & phase, stk::mesh::PartVector & add_parts, stk::mesh::PartVector & remove_parts) const"); /* %TRACE% */ + + add_parts.clear(); + remove_parts.clear(); + + ThrowAssert(stk_bulk().is_valid(entity)); + + stk::mesh::EntityRank entity_rank = stk_bulk().entity_rank(entity); + const stk::mesh::PartVector & current_parts = stk_bulk().bucket(entity).supersets(); + determine_conformal_parts(current_parts, entity_rank, phase, add_parts, remove_parts); +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::determine_child_conformal_parts( + stk::topology topology, + const stk::mesh::PartVector & parent_parts, + const PhaseTag & phase, + stk::mesh::PartVector & child_parts) const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::determine_child_conformal_parts(stk::mesh::Entity entity, const PhaseTag & phase, stk::mesh::PartVector & add_parts, stk::mesh::PartVector & remove_parts) const"); /* %TRACE% */ + + child_parts.clear(); + + const auto & all_decomposed_blocks_selector = my_phase_support.get_all_decomposed_blocks_selector(); + stk::mesh::EntityRank entity_rank = topology.rank(); + for(stk::mesh::PartVector::const_iterator part_iter = parent_parts.begin(); part_iter != parent_parts.end(); ++part_iter) + { + stk::mesh::Part & part = **part_iter; + if( part.primary_entity_rank() == entity_rank && + (stk::io::is_part_io_part(part) || all_decomposed_blocks_selector(&part)) ) + { + stk::mesh::Part * conformal_elem_io_part = const_cast(my_phase_support.find_conformal_io_part(part, phase)); + if (nullptr != conformal_elem_io_part && !my_phase_support.is_interface(&part)) + { + child_parts.push_back(conformal_elem_io_part); + } + } + else if (stk::mesh::contain(my_attribute_parts, part)) + { + child_parts.push_back(&part); + } + } + + child_parts.push_back(&stk_meta().get_topology_root_part(topology)); + + if (entity_rank == stk::topology::ELEMENT_RANK) + { + child_parts.push_back(&get_child_part()); + } + + // Set to active + child_parts.push_back(&aux_meta().active_part()); +} + +//-------------------------------------------------------------------------------- + +bool +CDMesh::triangulate(const InterfaceGeometry & interfaceGeometry) +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::triangulate(InterfaceGeometry & interfaceGeometry)"); /* %TRACE% */ + bool made_changes = false; + for (auto && elem : elements) + { + made_changes |= elem->triangulate(*this, interfaceGeometry); + } + return made_changes; +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::cut_sharp_features() +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::cut_sharp_features(void)"); /* %TRACE% */ + for (auto && elem : elements) + { + elem->cut_interior_intersection_points(*this); + } +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::set_phase_of_uncut_elements(const InterfaceGeometry & interfaceGeometry) +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::snap_nearby_intersections_to_nodes(void)"); /* %TRACE% */ + + const bool oneLSPerPhase = Phase_Support::has_one_levelset_per_phase(); + for (auto && entry : interfaceGeometry.get_phase_for_uncut_elements()) + { + Mesh_Element * elem = find_mesh_element(stk_bulk().identifier(entry.first)); + if (elem) + { + PhaseTag elemPhase; + if (oneLSPerPhase) + { + elemPhase.add(ls_field(entry.second).identifier, -1); + elem->set_phase(elemPhase); + } + else + { + ThrowRequire(1 == num_ls_fields()); + elemPhase.add(ls_field(0).identifier, entry.second); + elem->set_phase(elemPhase); + } + if (false) + krinolog << "Set phase for elem " << stk_bulk().identifier(entry.first) << ".\n" << elem->visualize(*this) << stk::diag::dendl; + } + } +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::snap_nearby_intersections_to_nodes(const InterfaceGeometry & interfaceGeometry, NodeToCapturedDomainsMap & domainsAtNodes) +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::snap_nearby_intersections_to_nodes(void)"); /* %TRACE% */ + stk::diag::TimeBlock timer__(my_timer_snap); + + snap_to_node(stk_bulk(), interfaceGeometry, get_snapper(), domainsAtNodes); + for (auto && entry : domainsAtNodes) + { + const SubElementNode * node = get_mesh_node(stk_bulk().identifier(entry.first)); + if (node) + node->set_node_domains(entry.second); + } + + domainsAtNodes.clear(); // done using this +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::decompose() +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::decompose(void)"); /* %TRACE% */ + + if (my_cdfem_support.get_cdfem_edge_degeneracy_handling() == SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE) + cut_sharp_features(); + + // TODO: N^2 in number of phases + for (auto && interface : active_interface_ids()) + { + determine_node_signs(interface); + decompose_edges(interface); + determine_node_scores(interface); + handle_hanging_children(interface); + } + for (auto && elem : elements) + { + elem->build_quadratic_subelements(*this); + } + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << stk::diag::dendl; + + for (auto && elem : elements ) + { + if(krinolog.shouldPrint(LOG_DEBUG)) + { + krinolog << "Determining subelement phases for Mesh_Element local_id=" << " identifier=" << elem->entityId(); + krinolog << "\n"; + } + elem->determine_decomposed_elem_phase(*this); + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "\n"; + } + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << stk::diag::dendl; +} + +void +CDMesh::build_parallel_hanging_edge_nodes() +{ /* %TRACE[ON]% */ Trace trace__("krino::CDMesh::build_parallel_hanging_edge_nodes(void)"); /* %TRACE% */ + stk::mesh::BulkData & mesh = stk_bulk(); + + if (mesh.parallel_size() < 2) return; + + // Get all cut edges in the mesh that are parallel shared. Processing by edge nodes should be cheaper than processing + // by elements since we don't have to deal with duplicates. + std::vector shared_edge_nodes; + for (auto&& node : nodes) + { + const SubElementEdgeNode * edge_node = dynamic_cast( node.get() ); + if (nullptr != edge_node) + { + if (SubElementChildNodeAncestry::is_shared(mesh, edge_node)) + { + shared_edge_nodes.emplace_back(edge_node); + } + } + } + + std::vector sharing_procs; + std::vector edge_node_keys; + + stk::CommSparse comm_spec(mesh.parallel()); + + for (int phase=0;phase<2;++phase) + { + for (auto&& shared_edge_node : shared_edge_nodes) + { + shared_edge_node.get_parent_node_keys(edge_node_keys); + stk_bulk().shared_procs_intersection(edge_node_keys, sharing_procs); + + for (auto&& other_proc : sharing_procs) + { + if (other_proc != mesh.parallel_rank()) + { + shared_edge_node.pack_into_buffer(comm_spec.send_buffer(other_proc)); + } + } + } + + if ( phase == 0 ) + { + comm_spec.allocate_buffers(); + } + else + { + comm_spec.communicate(); + } + } + + for(int i = 0; i < mesh.parallel_size(); ++i) + { + if(i != mesh.parallel_rank()) + { + while(comm_spec.recv_buffer(i).remaining()) + { + SubElementChildNodeAncestry shared_child_node(comm_spec.recv_buffer(i)); + shared_child_node.build_missing_child_nodes(*this); + } + } + } +} + +void +CDMesh::determine_node_signs(const InterfaceID & interface) +{ + for (auto && node : nodes) + { + node->clear_node_sign(); + } + for (auto && elem : elements) + { + elem->determine_node_signs(*this, interface); + } + sync_node_signs_on_constrained_nodes(); + parallel_sync_node_signs_on_shared_nodes(); +} + +void +CDMesh::decompose_edges(const InterfaceID & interface) +{ + for (auto && elem : elements) + { + if(krinolog.shouldPrint(LOG_DEBUG)) + { + krinolog << "Decomposing Mesh_Element local_id=" << " identifier=" << elem->entityId(); + krinolog << "\n"; + } + elem->decompose(*this, interface); + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "\n"; + } +} + +void +CDMesh::determine_node_scores(const InterfaceID & interface) +{ + for (auto && node : nodes) + { + node->clear_node_score(); + } + for (auto && elem : elements) + { + elem->determine_node_scores(*this, interface); + } + sync_node_scores_on_constrained_nodes(); + parallel_sync_node_scores_on_shared_nodes(); +} + +template +std::vector> determine_owning_procs_of_nodes_in_ancestries(const stk::mesh::BulkData & mesh, const std::vector> & constrainedNodesAndData) +{ + std::vector edgeNodeKeys; + + std::vector> owningProcsOfNodesInAncestries; + owningProcsOfNodesInAncestries.reserve(constrainedNodesAndData.size()); + for (auto&& constrainedNodeAndData : constrainedNodesAndData) + { + owningProcsOfNodesInAncestries.emplace_back(); + std::vector & owningProcs = owningProcsOfNodesInAncestries.back(); + + const auto & nodeAncestry = constrainedNodeAndData.first; + nodeAncestry.get_parent_node_keys(edgeNodeKeys); + for (auto && edgeNodeKey : edgeNodeKeys) + owningProcs.push_back(mesh.parallel_owner_rank(mesh.get_entity(edgeNodeKey))); //Expensive? + + stk::util::sort_and_unique(owningProcs); + } + + return owningProcsOfNodesInAncestries; +} + +template +std::vector> determine_sharing_procs_of_nodes_in_ancestries(const stk::mesh::BulkData & mesh, const std::vector> & sharedNodesAndData) +{ + std::vector edgeNodeKeys; + + std::vector> sharingProcsOfNodesInAncestries; + sharingProcsOfNodesInAncestries.reserve(sharedNodesAndData.size()); + for (auto&& sharedNodeAndData : sharedNodesAndData) + { + sharingProcsOfNodesInAncestries.emplace_back(); + std::vector & sharingProcs = sharingProcsOfNodesInAncestries.back(); + + const auto & nodeAncestry = sharedNodeAndData.first; + nodeAncestry.get_parent_node_keys(edgeNodeKeys); + mesh.shared_procs_intersection(edgeNodeKeys, sharingProcs); + } + + return sharingProcsOfNodesInAncestries; +} + +template +void pack_node_data_for_node_ancestries(const stk::mesh::BulkData & mesh, const std::vector> & nodeAncestriesAndData, const std::vector> & destinationProcs, stk::CommSparse &commSparse) +{ + stk::pack_and_communicate(commSparse,[&]() + { + ThrowAssert(nodeAncestriesAndData.size() == destinationProcs.size()); + std::vector edgeNodeKeys; + + for (size_t i=0; i +void set_node_sign_or_score(const SubElementNode * node, const T & signOrScore) { ThrowRequireMsg(false, "Unsupported type in set_node_sign_or_score."); } + +template <> +void set_node_sign_or_score(const SubElementNode * node, const int & sign) { node->set_node_sign(sign); } + +template <> +void set_node_sign_or_score(const SubElementNode * node, const double & score) { node->set_node_score(score); } + +template +T get_node_sign_or_score(const SubElementNode * node) { ThrowRequireMsg(false, "Unsupported type in get_node_sign_or_score."); } + +template <> +int get_node_sign_or_score(const SubElementNode * node) { return node->get_node_sign(); } + +template <> +double get_node_sign_or_score(const SubElementNode * node) { return node->get_node_score(); } + +template +bool node_sign_or_score_is_set(const SubElementNode * node) { ThrowRequireMsg(false, "Unsupported type in node_sign_or_score_is_set."); return false; } + +template <> +bool node_sign_or_score_is_set(const SubElementNode * node) { return node->node_sign_is_set(); } + +template <> +bool node_sign_or_score_is_set(const SubElementNode * node) { return node->node_score_is_set(); } + +template +std::vector> gather_constrained_node_ancestries_and_sign_or_score(const std::vector> & nodes, + const std::unordered_map > & periodicNodeIDMap) +{ + std::vector> ancestriesAndNodeOrScore; + for (auto && node : nodes) + { + if (node_sign_or_score_is_set(node.get())) + { + SubElementChildNodeAncestry nodeAncestry(node.get()); + const auto & constrainedNodeAncestries = nodeAncestry.get_constrained_node_ancestries(periodicNodeIDMap); + for (auto && constrainedNodeAncestry : constrainedNodeAncestries) + ancestriesAndNodeOrScore.emplace_back(constrainedNodeAncestry,get_node_sign_or_score(node.get())); + } + } + return ancestriesAndNodeOrScore; +} + +template +std::vector> gather_shared_node_ancestries_and_sign_or_score(const stk::mesh::BulkData & mesh, + const std::vector> & nodes) +{ + // Get all cut edges in the mesh that are parallel shared. Processing by edge nodes should be cheaper than processing + // by elements since we don't have to deal with duplicates. + + std::vector> sharedNodeAncestriesAndSignOrScore; + for (auto && node : nodes) + if (node_sign_or_score_is_set(node.get()) && SubElementChildNodeAncestry::is_shared(mesh, node.get())) + sharedNodeAncestriesAndSignOrScore.emplace_back(node.get(),get_node_sign_or_score(node.get())); + + return sharedNodeAncestriesAndSignOrScore; +} + +template +void receive_node_sign_or_score(CDMesh & cdmesh, stk::CommSparse &commSparse) +{ + stk::unpack_communications(commSparse, [&commSparse, &cdmesh](int procId) + { + auto & buffer = commSparse.recv_buffer(procId); + SubElementChildNodeAncestry shared_edge_node(buffer); + T signOrScore = 0; + buffer.unpack(signOrScore); + + const SubElementNode * node = shared_edge_node.find_subelement_node(cdmesh); + if (node) + set_node_sign_or_score(node, signOrScore); + }); +} + +template +void sync_node_sign_or_score_on_local_constrained_nodes(CDMesh & cdmesh, const std::vector> & constrainedNodesAndSignOrScore, const std::vector> & owningProcsOfNodesInAncestries) +{ + ThrowAssert(constrainedNodesAndSignOrScore.size() == owningProcsOfNodesInAncestries.size()); + + for (size_t i=0; i +void sync_node_sign_or_score_on_constrained_nodes(CDMesh & cdmesh, + const stk::mesh::BulkData & mesh, + const std::vector> & nodes, + const std::unordered_map > & periodicNodeIDMap) +{ + if (stk::is_true_on_all_procs(cdmesh.stk_bulk().parallel(), periodicNodeIDMap.empty())) + return; + + const std::vector> constrainedNodeAncestriesAndSignOrScore = gather_constrained_node_ancestries_and_sign_or_score(nodes, periodicNodeIDMap); + const std::vector> owningProcsOfNodesInAncestries = determine_owning_procs_of_nodes_in_ancestries(mesh, constrainedNodeAncestriesAndSignOrScore); + + sync_node_sign_or_score_on_local_constrained_nodes(cdmesh, constrainedNodeAncestriesAndSignOrScore, owningProcsOfNodesInAncestries); + + if (mesh.parallel_size() < 2) return; + + stk::CommSparse commSparse(mesh.parallel()); + pack_node_data_for_node_ancestries(mesh, constrainedNodeAncestriesAndSignOrScore, owningProcsOfNodesInAncestries, commSparse); + receive_node_sign_or_score(cdmesh, commSparse); +} + +template +void sync_node_sign_or_score_on_shared_nodes(CDMesh & cdmesh, + const stk::mesh::BulkData & mesh, + const std::vector> & nodes) +{ + if (mesh.parallel_size() < 2) return; + + const std::vector> sharedNodeAncestriesAndSignOrScore = gather_shared_node_ancestries_and_sign_or_score(mesh, nodes); + const std::vector> sharingProcsOfNodesInAncestries = determine_sharing_procs_of_nodes_in_ancestries(mesh, sharedNodeAncestriesAndSignOrScore); + + stk::CommSparse commSparse(mesh.parallel()); + pack_node_data_for_node_ancestries(mesh, sharedNodeAncestriesAndSignOrScore, sharingProcsOfNodesInAncestries, commSparse); + receive_node_sign_or_score(cdmesh, commSparse); +} + +void +CDMesh::sync_node_signs_on_constrained_nodes() +{ + sync_node_sign_or_score_on_constrained_nodes(*this, stk_bulk(), nodes, my_periodic_node_id_map); +} + +void +CDMesh::sync_node_scores_on_constrained_nodes() +{ + sync_node_sign_or_score_on_constrained_nodes(*this, stk_bulk(), nodes, my_periodic_node_id_map); +} + +void +CDMesh::parallel_sync_node_signs_on_shared_nodes() +{ + sync_node_sign_or_score_on_shared_nodes(*this, stk_bulk(), nodes); +} + +void +CDMesh::parallel_sync_node_scores_on_shared_nodes() +{ + sync_node_sign_or_score_on_shared_nodes(*this, stk_bulk(), nodes); +} + +void +CDMesh::handle_hanging_children(const InterfaceID & interface) +{ /* %TRACE[ON]% */ Trace trace__("krino::CDMesh::handle_hanging_children(const InterfaceID & interface)"); /* %TRACE% */ + + build_parallel_hanging_edge_nodes(); + + for (auto && elem : elements) + { + if(krinolog.shouldPrint(LOG_DEBUG)) + { + krinolog << "Handling hanging children Mesh_Element identifier=" << elem->entityId(); + krinolog << "\n"; + } + elem->handle_hanging_children(*this, interface); + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "\n"; + } +} + +//-------------------------------------------------------------------------------- + +const SubElementMeshNode * +CDMesh::get_mesh_node( stk::mesh::EntityId node_id ) const +{ + NodeMap::const_iterator it = mesh_node_map.find( node_id ); + return ( it != mesh_node_map.end() ) ? it->second : nullptr; +} + +const SubElementMeshNode * +CDMesh::get_mesh_node(const SubElementNode * new_node) const +{ + const SubElementMeshNode * mesh_node = dynamic_cast( new_node ); + if (nullptr != mesh_node) + { + return get_mesh_node(new_node->entityId()); + } + return nullptr; +} + +SubElementMeshNode * +CDMesh::add_managed_mesh_node( std::unique_ptr node ) +{ + SubElementMeshNode * ptr = node.get(); + mesh_node_map.insert( NodeMap::value_type( node->entityId(), ptr ) ); + add_managed_node(std::move(node)); + return ptr; +} + +//-------------------------------------------------------------------------------- +void +CDMesh::check_isovariable_field_existence_on_decomposed_blocks(stk::mesh::BulkData & mesh, + const bool conformal_parts_require_field) +{ + CDMesh cdmesh(mesh, std::shared_ptr()); + cdmesh.check_isovariable_field_existence_on_decomposed_blocks(conformal_parts_require_field); +} + +namespace { +bool field_exists_on_part(const FieldRef field, const stk::mesh::Part & part) +{ + stk::mesh::Selector field_selector = AuxMetaData::get(part.mesh_meta_data()).selectField(field, stk::topology::ELEMENT_RANK); + return field_selector(part); +} +std::ostream & dump_fields_on_part(std::ostream & os, const stk::mesh::Part & part) +{ + const auto & meta = part.mesh_meta_data(); + for(auto && field : meta.get_fields(stk::topology::NODE_RANK)) + { + if(field_exists_on_part(*field, part)) + os << " * " << field->name() << "\n"; + } + return os; +} +} +void +CDMesh::check_isovariable_field_existence_on_decomposed_blocks(const bool conformal_parts_require_field) const +{ + const int num_ls = num_ls_fields(); + for(auto && part : stk_meta().get_mesh_parts()) + { + if(part->primary_entity_rank() != stk::topology::ELEMENT_RANK) continue; + + const auto nonconformal_part = my_phase_support.find_nonconformal_part(*part); + if(!nonconformal_part) continue; + + /* 4 cases we care about here: + * 1) Nonconformal part - Skip + * 2) Initial IO part (i.e. block_1) - Must have field + * 3) Active conformal part (either LS or active part for irreversible) - Must have field + * 4) Deactivated conformal part (irreversible only) - Skip + */ + if(my_phase_support.is_nonconformal(part)) continue; + + // If part is the initial IO part the phase will be empty + const auto & phase = my_phase_support.get_iopart_phase(*part); + + // Steady state problems that only do a single decomposition can + // get away with only defining the isovariable on the initial IO part + // so skip checking for the field in that case. + if(!conformal_parts_require_field && !phase.empty()) continue; + + for(int ls=0; ls < num_ls; ++ls) + { + const auto * LS = ls_field(ls).ptr; + const auto * death_spec = get_death_spec(ls); + const bool is_dead_block = death_spec && phase.contain(death_spec->get_deactivated_phase()); + const bool LS_uses_part = + my_phase_support.level_set_is_used_by_nonconformal_part(LS, nonconformal_part); + if(LS_uses_part && !is_dead_block) + { + const auto field = ls_field(ls).isovar; + if(!field_exists_on_part(field, *part)) + { + std::ostringstream err_msg; + err_msg << "CDFEM isovariable field " << field.name() << " is not defined on " + << part->name() << " that is supposed to be decomposed.\n" + << "Available fields on " << part->name() << " are:\n"; + dump_fields_on_part(err_msg, *part); + throw std::runtime_error(err_msg.str()); + } + } + } + } +} + +//-------------------------------------------------------------------------------- + +const SubElementNode * +CDMesh::create_mesh_node( + const Mesh_Element * owner, + const int lnn, + stk::mesh::Entity nodeEntity ) +{ + const stk::mesh::EntityId nodeId = stk_bulk().identifier(nodeEntity); + const SubElementMeshNode * subnode = get_mesh_node(nodeId); + + if ( nullptr == subnode ) + { + const Vector3d owner_coords = owner->get_node_parametric_coords( lnn ); + const double * global_coords_ptr = field_data(get_coords_field(), nodeEntity); + + Vector3d global_coords(global_coords_ptr, my_spatial_dim); + + std::unique_ptr meshNode = std::make_unique( owner, nodeEntity, nodeId, owner_coords, global_coords ); + subnode = add_managed_mesh_node(std::move(meshNode)); + } + + return subnode; +} + +//-------------------------------------------------------------------------------- + +const SubElementNode * +CDMesh::create_edge_node( const Mesh_Element * owner, + const SubElementNode * parent1, + const SubElementNode * parent2, + const double position) +{ + const SubElementNode * subnode = SubElementNode::common_child({parent1, parent2}); + + if (subnode == nullptr) + { + std::unique_ptr newNode = std::make_unique(owner, position, parent1, parent2); + subnode = add_managed_node(std::move(newNode)); + + parent1->add_child(subnode); + parent2->add_child(subnode); + } + + return subnode; +} + +//-------------------------------------------------------------------------------- + +const SubElementNode * +CDMesh::create_midside_node( const Mesh_Element * owner, + const SubElementNode * parent1, + const SubElementNode * parent2, + stk::mesh::Entity entity) +{ + std::pair parents = + parent1second; + } + else + { + std::unique_ptr newNode = (stk_bulk().is_valid(entity)) ? + std::make_unique(owner, parent1, parent2, entity, stk_bulk().identifier(entity)) : + std::make_unique(owner, parent1, parent2); + subnode = add_managed_node(std::move(newNode)); + my_midside_node_map[parents] = subnode; + } + + return subnode; +} + +//-------------------------------------------------------------------------------- + +const SubElementNode * +CDMesh::create_steiner_node( const Mesh_Element * owner, + const NodeVec & parents, + const std::vector & weights ) +{ + std::unique_ptr newNode = std::make_unique( owner, parents, weights ); + return add_managed_node(std::move(newNode)); +} + +//-------------------------------------------------------------------------------- + +const SubElementNode * +CDMesh::create_child_internal_or_face_node( const Mesh_Element * owner, + const NodeVec & parents, + const std::vector & weights ) +{ + const SubElementNode * subnode = SubElementNode::common_child(parents); + + if (subnode == nullptr) + { + std::unique_ptr newNode = std::make_unique( owner, parents, weights ); + subnode = add_managed_node(std::move(newNode)); + + for (auto && parent : parents) + parent->add_child(subnode); + } + + return subnode; +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::create_subelement_mesh_entities( + const Mesh_Element & elem, + const std::vector conformal_subelems) +{ + stk::mesh::Entity parent_elem = elem.entity(); + for (auto && subelem : conformal_subelems) + { + const stk::mesh::PartVector & parent_parts = stk_bulk().bucket(parent_elem).supersets(); + const stk::topology parent_topology = stk_bulk().bucket(parent_elem).topology(); + stk::mesh::PartVector subelem_parts; + determine_child_conformal_parts(parent_topology, parent_parts, subelem->get_phase(), subelem_parts); + + if (0 == subelem->entityId()) + { + const stk::mesh::EntityId new_id = my_entity_id_pool.get_EntityId(stk::topology::ELEMENT_RANK); + ThrowAssert(!stk_bulk().is_valid(stk_bulk().get_entity(stk::topology::ELEMENT_RANK, new_id))); + stk::mesh::Entity subelem_entity = stk_bulk().declare_element(new_id, subelem_parts); + subelem->set_entity( stk_bulk(), subelem_entity ); + ThrowAssert(stk_bulk().bucket(subelem_entity).topology() != stk::topology::INVALID_TOPOLOGY); + + const NodeVec & elem_nodes = subelem->get_nodes(); + for (unsigned n=0; nentity(); + stk_bulk().declare_relation( subelem_entity, node , n ); + } + } + else + { + stk::mesh::Entity subelem_entity = subelem->entity(); + stk_bulk().change_entity_parts(subelem_entity, subelem_parts, get_removable_parts(stk_bulk(), subelem_entity)); + } + } +} + +void +CDMesh::attach_existing_and_identify_missing_subelement_sides( + const Mesh_Element & elem, + const std::vector conformal_subelems, + std::vector & side_requests) +{ + stk::mesh::BulkData & stk_mesh = stk_bulk(); + const bool build_internal_sides = my_cdfem_support.use_internal_face_stabilization(); + + for (auto && subelem : conformal_subelems) + { + const stk::topology topology = subelem->topology(); + const stk::mesh::Entity * elem_nodes = stk_bulk().begin_nodes(subelem->entity()); + + for (unsigned s=0; s side_nodes(side_topology.num_nodes()); + topology.side_nodes(elem_nodes, s, side_nodes.data()); + + std::vector sides; + stk::mesh::get_entities_through_relations(stk_mesh, side_nodes, stk_meta().side_rank(), sides); + + if (sides.empty()) + { + stk::mesh::Entity parent_side = find_entity_by_ordinal(stk_bulk(), elem.entity(), stk_meta().side_rank(), subelem->parent_side_id(s)); + const bool have_parent_side = stk_bulk().is_valid(parent_side); + const bool is_internal_side = subelem->parent_side_id(s) == -1; + + if (have_parent_side || (is_internal_side && build_internal_sides)) + { + static stk::mesh::PartVector empty_parts; + const stk::mesh::PartVector & parent_parts = have_parent_side ? stk_bulk().bucket(parent_side).supersets() : empty_parts; + + // We have to make sure that pre-existing sideset parts are added to the side so that we + // can figure out the correct conformal side parts during the second modification pass. + stk::mesh::PartVector side_parts; + determine_child_conformal_parts(side_topology, parent_parts, subelem->get_phase(), side_parts); + if (is_internal_side) + { + side_parts.push_back(&get_internal_side_part()); + } + + side_requests.emplace_back(subelem->entity(), s, side_parts); + } + } + else + { + ThrowRequire(sides.size() == 1); + stk::mesh::Entity elem_side_entity = sides[0]; + attach_entity_to_elements(stk_bulk(), elem_side_entity); + } + } + } +} + +bool +CDMesh::check_element_side_parts() const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::check_element_side_parts()"); /* %TRACE% */ + // This method requires aura to work correctly. + if (!stk_bulk().is_automatic_aura_on()) + { + // Skip check if we don't have aura + return true; + } + + bool success = true; + stk::mesh::Selector active_locally_owned = aux_meta().active_locally_owned_selector(); + stk::mesh::BucketVector const& buckets = stk_bulk().get_buckets(stk::topology::ELEMENT_RANK, active_locally_owned); + + std::vector side_nodes; + + for (auto&& bucket : buckets) + { + const stk::topology topology = bucket->topology(); + const unsigned num_sides = topology.num_sides(); + for (auto&& elem : *bucket) + { + auto elem_nodes = stk_bulk().begin(elem, stk::topology::NODE_RANK); + for (unsigned s=0; s elems; + stk::mesh::get_entities_through_relations(stk_bulk(), side_nodes, stk::topology::ELEMENT_RANK, elems); + for(auto && touching_elem : elems) krinolog << debug_entity(stk_bulk(), touching_elem) << stk::diag::dendl; + + success = false; + } + } + } + } + + return success; +} + +std::vector get_conformal_volume_part_ordinals(const stk::mesh::BulkData & mesh, const Phase_Support & phaseSupport, stk::mesh::Entity entity) +{ + std::vector conformalVolumeParts; + + for(auto && part : mesh.bucket(entity).supersets()) + { + if (part->primary_entity_rank() == stk::topology::ELEMENT_RANK) + { + if (phaseSupport.is_conformal(part)) + { + conformalVolumeParts.push_back(part->mesh_meta_data_ordinal()); + } + } + } + + return conformalVolumeParts; +} + +bool have_multiple_conformal_volume_parts_in_common(const stk::mesh::BulkData & mesh, const Phase_Support & phaseSupport, const std::vector & sideNodes) +{ + const int numSideNodes = sideNodes.size(); + ThrowRequire(numSideNodes > 0); + + std::vector commonConformalVolumeParts = get_conformal_volume_part_ordinals(mesh, phaseSupport, sideNodes[0]); + + for (int n=1; n nodeConformalVolumeParts = get_conformal_volume_part_ordinals(mesh, phaseSupport, sideNodes[n]); + + std::vector workingSet; + workingSet.swap(commonConformalVolumeParts); + std::set_intersection(workingSet.begin(),workingSet.end(),nodeConformalVolumeParts.begin(),nodeConformalVolumeParts.end(),std::back_inserter(commonConformalVolumeParts)); + + if (commonConformalVolumeParts.empty()) + return false; + } + return true; +} + +void +CDMesh::add_possible_interface_sides(std::vector & sideRequests) const +{ + // This will add sides that *might be* interface sides. + // Because this probes the nodes, it will add "keyhole" sides that aren't actually on an interface + // This should be harmless, however, and avoids extra communication or requiring aura. + + stk::mesh::Selector active_locally_owned = aux_meta().active_locally_owned_selector(); + stk::mesh::BucketVector const& buckets = stk_bulk().get_buckets(stk::topology::ELEMENT_RANK, active_locally_owned); + + std::vector sideNodes; + + for (auto&& bucket : buckets) + { + const stk::topology topology = bucket->topology(); + const unsigned num_sides = topology.num_sides(); + for (auto&& elem : *bucket) + { + auto elem_nodes = stk_bulk().begin(elem, stk::topology::NODE_RANK); + for (unsigned s=0; s & side_nodes) const +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::check_element_side_parts(const std::vector & side_nodes)"); /* %TRACE% */ + + // This method requires aura. + ThrowRequire(stk_bulk().is_automatic_aura_on()); + + std::vector elems; + stk::mesh::get_entities_through_relations(stk_bulk(), side_nodes, stk::topology::ELEMENT_RANK, elems); + + std::vector conformal_volume_parts; + for (auto&& elem : elems) + { + if (!stk_bulk().bucket(elem).member(get_active_part())) + { + continue; + } + auto& elem_parts = stk_bulk().bucket(elem).supersets(); + for(auto&& part : elem_parts) + { + if (part->primary_entity_rank() == stk::topology::ELEMENT_RANK && my_phase_support.is_conformal(part)) + { + if (std::find(conformal_volume_parts.begin(), conformal_volume_parts.end(), part) == conformal_volume_parts.end()) + { + conformal_volume_parts.push_back(part); + } + } + } + } + + if (conformal_volume_parts.empty()) + { + return true; + } + + if (conformal_volume_parts.size() > 2) + { + krinolog << "Expected to find 1 or 2 conformal side parts when examining side nodes: "; + for (auto&& side_node : side_nodes) krinolog << stk_bulk().identifier(side_node) << " "; + krinolog << " but instead found the parts: "; + for (auto&& part : conformal_volume_parts) krinolog << part->name() << " "; + krinolog << stk::diag::dendl; + return false; + } + + std::vector side_phases(conformal_volume_parts.size()); + for (unsigned iphase = 0; iphase sides; + stk::mesh::get_entities_through_relations(stk_bulk(), side_nodes, stk_meta().side_rank(), sides); + + if (conformal_volume_parts.size() == 2 && side_phases[0] != side_phases[1]) + { + stk::mesh::PartVector conformal_side_parts; + const stk::mesh::Part * conformal_side_part = my_phase_support.find_interface_part(*conformal_volume_parts[0], *conformal_volume_parts[1]); + if (nullptr != conformal_side_part) conformal_side_parts.push_back(const_cast(conformal_side_part)); + conformal_side_part = my_phase_support.find_interface_part(*conformal_volume_parts[1], *conformal_volume_parts[0]); + if (nullptr != conformal_side_part) conformal_side_parts.push_back(const_cast(conformal_side_part)); + + if (!conformal_side_parts.empty()) + { + // Check that side exists and has conformal side parts + if (sides.size() != 1) + { + krinolog << "Expected to find 1 conformal side, but instead found " << sides.size() << " when examining side nodes: "; + for (auto&& side_node : side_nodes) krinolog << stk_bulk().identifier(side_node) << " "; + krinolog << " with conformal volume parts: "; + for (auto&& part : conformal_volume_parts) krinolog << part->name() << " "; + krinolog << stk::diag::dendl; + return false; + } + else + { + auto& side_bucket = stk_bulk().bucket(sides[0]); + if (!side_bucket.member_all(conformal_side_parts)) + { + krinolog << "Side " << stk_bulk().identifier(sides[0]) << " is missing at least one of the conformal side parts: "; + for (auto&& part : conformal_side_parts) krinolog << part->name() << " "; + krinolog << ", actual parts: "; + auto& side_parts = side_bucket.supersets(); + for (auto&& part : side_parts) krinolog << part->name() << " "; + krinolog << stk::diag::dendl; + return false; + } + } + } + } + else + { + // Check that if the side exists, then it does not have any interface sides + if (sides.size() > 1) + { + krinolog << "Expected to find 0 or 1 side, but instead found " << sides.size() << " when examining side nodes: "; + for (auto&& side_node : side_nodes) krinolog << stk_bulk().identifier(side_node) << " "; + krinolog << " with conformal volume parts: "; + for (auto&& part : conformal_volume_parts) krinolog << part->name() << " "; + krinolog << stk::diag::dendl; + return false; + } + + const stk::mesh::PartVector & existing_side_parts = stk_bulk().bucket(sides[0]).supersets(); + for(auto && sidePart : existing_side_parts) + { + if(sidePart->primary_entity_rank() == stk_meta().side_rank() && my_phase_support.is_interface(sidePart)) + { + krinolog << "Side " << stk_bulk().identifier(sides[0]) << " has an erroneous interface part " << sidePart->name() << "." << stk::diag::dendl; + return false; + } + } + } + + return true; +} + +void +CDMesh::update_element_side_parts() +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::update_element_side_parts()"); /* %TRACE% */ + + // This method makes sure the correct conformal side parts are on the element sides + stk::mesh::Selector locally_owned = get_locally_owned_part(); + + std::vector< stk::mesh::Entity> sides; + stk::mesh::get_selected_entities( locally_owned, stk_bulk().buckets( stk_bulk().mesh_meta_data().side_rank() ), sides ); + + stk::mesh::PartVector add_parts; + stk::mesh::PartVector remove_parts; + + for (auto && side : sides) + { + determine_element_side_parts(side, add_parts, remove_parts); + stk_bulk().change_entity_parts(side, add_parts, remove_parts); + + if (krinolog.shouldPrint(LOG_DEBUG)) + { + krinolog << "After changes: " << debug_entity(stk_bulk(), side); + } + } +} + +void +CDMesh::determine_element_side_parts(const stk::mesh::Entity side, stk::mesh::PartVector & add_parts, stk::mesh::PartVector & remove_parts) const +{ + if (krinolog.shouldPrint(LOG_DEBUG)) + { + krinolog << "Analyzing side " << stk_bulk().identifier(side) << " with nodes "; + const unsigned num_side_nodes = stk_bulk().num_nodes(side); + stk::mesh::Entity const* side_nodes = stk_bulk().begin_nodes(side); + for (unsigned n=0; n volume_parts; + std::vector conformal_volume_parts; + std::vector nonconformal_volume_parts; + const stk::mesh::PartVector & existing_side_parts = stk_bulk().bucket(side).supersets(); + for(stk::mesh::PartVector::const_iterator part_iter = existing_side_parts.begin(); part_iter != existing_side_parts.end(); ++part_iter) + { + const stk::mesh::Part * const side_part = *part_iter; + if (side_part->primary_entity_rank() == stk::topology::ELEMENT_RANK) + { + if (my_phase_support.is_conformal(side_part)) + { + conformal_volume_parts.push_back(side_part); + } + if (my_phase_support.is_nonconformal(side_part)) + { + nonconformal_volume_parts.push_back(side_part); + } + else if (stk::io::is_part_io_part(*side_part)) + { + volume_parts.push_back(side_part); + } + } + } + + ThrowRequire(volume_parts.size() <= 2); // Can be zero for inactive elements supporting a face + + if (conformal_volume_parts.empty()) + { + /* There are two possible cases where no conformal volume parts are found: + * 1) This side is part of a surface that does not touch any blocks that are being decomposed. + * Only the active parts for these sides should be updated. + * 2) This side is a parent side that should be deactivated and moved to the nonconformal part. + * These sides will have at least 1 nonconformal volume part from the parent volume element. + */ + if(nonconformal_volume_parts.empty()) + { + if(element_side_should_be_active(side)) + { + add_parts.push_back(&aux_meta().active_part()); + } + else + { + remove_parts.push_back(&aux_meta().active_part()); + } + } + else + { + determine_nonconformal_parts(side, add_parts, remove_parts); + } + } + + if (volume_parts.size() == 2) + { + add_parts.push_back(&get_block_boundary_part()); + } + else + { + remove_parts.push_back(&get_block_boundary_part()); + } + + if (conformal_volume_parts.empty()) + { + return; + } + + ThrowRequire(conformal_volume_parts.size() == 1 || conformal_volume_parts.size() == 2); + + std::vector side_phases(conformal_volume_parts.size()); + for (unsigned iphase = 0; iphase(my_phase_support.find_interface_part(*conformal_volume_parts[0], *conformal_volume_parts[1])); + if (nullptr != conformal_side0) add_parts.push_back(conformal_side0); + stk::mesh::Part * conformal_side1 = const_cast(my_phase_support.find_interface_part(*conformal_volume_parts[1], *conformal_volume_parts[0])); + if (nullptr != conformal_side1) add_parts.push_back(conformal_side1); + } + + for (auto && side_phase : side_phases) + { + determine_conformal_parts(existing_side_parts, stk_meta().side_rank(), side_phase, add_parts, remove_parts); + } + + if(element_side_should_be_active(side)) + { + add_parts.push_back(&aux_meta().active_part()); + } + else + { + remove_parts.push_back(&aux_meta().active_part()); + } +} + +bool +CDMesh::element_side_should_be_active(const stk::mesh::Entity side) const +{ + auto num_elems = stk_bulk().num_connectivity(side, stk::topology::ELEMENT_RANK); + const auto * touching_elems = stk_bulk().begin(side, stk::topology::ELEMENT_RANK); + auto & active_part = aux_meta().active_part(); + bool active = false; + for(unsigned i=0; i < num_elems; ++i) + { + if(stk_bulk().bucket(touching_elems[i]).member(active_part)) + { + active = true; + break; + } + } + return active; +} +void +CDMesh::handle_single_coincident_subelement(const Mesh_Element & elem, const SubElement * subelem, std::vector & side_requests) +{ + stk::mesh::Entity elem_entity = elem.entity(); + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "single coincident subelement for elem #" << stk_bulk().identifier(elem_entity) << " with phase " << subelem->get_phase() << stk::diag::dendl; + subelem->set_entity( stk_bulk(), elem_entity ); + stk::mesh::PartVector add_parts; + stk::mesh::PartVector remove_parts; + determine_conformal_parts(elem_entity, subelem->get_phase(), add_parts, remove_parts); + + add_parts.push_back(&get_active_part()); + remove_parts.push_back(&get_parent_part()); + + stk_bulk().change_entity_parts(elem_entity, add_parts, remove_parts); + + std::vector subelem_vec(1, subelem); + attach_existing_and_identify_missing_subelement_sides(elem, subelem_vec, side_requests); +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::generate_sorted_child_elements() +{ + child_elements.clear(); + + for (const auto & elem : elements) + { + if (elem->have_subelements()) + { + std::vector conformal_subelems; + elem->get_subelements(conformal_subelems); + + for (auto && subelem : conformal_subelems) + { + child_elements.push_back(subelem); + } + } + } + + std::sort(child_elements.begin(),child_elements.end(), ElementObj::is_less); +} + +//-------------------------------------------------------------------------------- + +const SubElement * +CDMesh::find_child_element(stk::mesh::Entity elem_mesh_obj) const +{ + // Ugh + CDMesh *const mesh = const_cast(this); + + if (child_elements.empty()) + { + mesh->generate_sorted_child_elements(); + } + + const stk::mesh::EntityId elem_id = stk_bulk().identifier(elem_mesh_obj); + auto lb_cmp = [](const ElementObj * elem, stk::mesh::EntityId target_id) { return elem->entityId() < target_id; }; + auto first = std::lower_bound(child_elements.begin(),child_elements.end(), elem_id, lb_cmp); + + if (first != child_elements.end() && (*first)->entityId() == elem_id) + { + return dynamic_cast(*first); + } + return nullptr; +} + +//-------------------------------------------------------------------------------- + +stk::mesh::Entity +CDMesh::get_parent_element(stk::mesh::Entity elem_entity) const +{ + std::set parent_elem_node_set; + + const stk::mesh::Entity * elem_nodes = stk_bulk().begin_nodes(elem_entity); + const unsigned num_base_elem_nodes = stk_bulk().bucket(elem_entity).topology().base().num_nodes(); + + for (unsigned inode=0; inode parent_elem_nodes(parent_elem_node_set.begin(), parent_elem_node_set.end()); + std::vector parent_elems; + stk::mesh::get_entities_through_relations(stk_bulk(), parent_elem_nodes, stk::topology::ELEMENT_RANK, parent_elems); + + ThrowAssert(parent_elems.size() <= 1); + + if (parent_elems.empty()) + { + krinolog << "Did not find parent element for element \n" << debug_entity(stk_bulk(), elem_entity) << stk::diag::dendl; + return stk::mesh::Entity(); + } + else + { + return parent_elems.front(); + } +} + +//-------------------------------------------------------------------------------- + +bool +CDMesh::get_parent_child_coord_transformation(stk::mesh::Entity elem_mesh_obj, double * dParentdChild) const +{ + ThrowAssert(my_cdfem_support.use_nonconformal_element_size()); + + const SubElement * subelem = find_child_element(elem_mesh_obj); + + if (nullptr == subelem) + { + krinolog << "did not find element " << stk_bulk().identifier(elem_mesh_obj) << stk::diag::dendl; + return false; + } + + subelem->get_owner_coord_transform(dParentdChild); + return true; +} + +void +CDMesh::get_parent_nodes_and_weights(stk::mesh::Entity child, stk::mesh::Entity & parent0, stk::mesh::Entity & parent1, double & position) const +{ + // Really slow! + auto id = stk_bulk().identifier(child); + auto find_existing = std::find_if(nodes.begin(), nodes.end(), + [id](const std::unique_ptr & compare) + { return compare->entityId() == id; }); + ThrowAssert(find_existing != nodes.end()); + ThrowAssert(dynamic_cast(find_existing->get()) != nullptr); + const krino::SubElementEdgeNode& edge_node = dynamic_cast(*find_existing->get()); + krino::NodeVec edge_node_parents = edge_node.get_parents(); + position = edge_node.get_position(); + parent0 = edge_node_parents[0]->entity(); + parent1 = edge_node_parents[1]->entity(); +} + +//-------------------------------------------------------------------------------- + +std::function build_get_element_volume_function(const CDMesh & cdmesh) +{ + auto get_element_size = + [&cdmesh](stk::mesh::Entity elem) + { + stk::mesh::Entity volumeElement = cdmesh.get_cdfem_support().use_nonconformal_element_size() ? cdmesh.get_parent_element(elem) : elem; + ThrowRequire(cdmesh.stk_bulk().is_valid(volumeElement)); + const double elemVolume = ElementObj::volume( cdmesh.stk_bulk(), volumeElement, cdmesh.get_coords_field() ); + return elemVolume; + }; + return get_element_size; +} + +Vector3d get_side_average_cdfem_displacement(const stk::mesh::BulkData& mesh, + const FieldRef cdfemDisplacementsField, + stk::mesh::Entity side) +{ + const int spatialDim = mesh.mesh_meta_data().spatial_dimension(); + + Vector3d avg{Vector3d::ZERO}; + int numNodes = 0; + for (auto node : StkMeshEntities{mesh.begin_nodes(side), mesh.end_nodes(side)}) + { + double * cdfemDispPtr = field_data(cdfemDisplacementsField, node); + if(nullptr != cdfemDispPtr) + { + const Vector3d cdfemDisp(cdfemDispPtr, spatialDim); + avg += cdfemDisp; + ++numNodes; + } + } + if (numNodes > 0) + avg /= numNodes; + + return avg; +} + +Vector3d get_side_normal(const stk::mesh::BulkData& mesh, + const FieldRef coordsField, + stk::mesh::Entity side) +{ + const auto * sideNodes = mesh.begin_nodes(side); + const stk::topology sideTopology = mesh.bucket(side).topology(); + if (sideTopology == stk::topology::TRIANGLE_3 || sideTopology == stk::topology::TRIANGLE_6) + { + const Vector3d v0(field_data(coordsField, sideNodes[0])); + const Vector3d v1(field_data(coordsField, sideNodes[1])); + const Vector3d v2(field_data(coordsField, sideNodes[2])); + return Cross(v1-v0,v2-v0).unit_vector(); + } + else if (sideTopology == stk::topology::LINE_2 || sideTopology == stk::topology::LINE_3) + { + const Vector3d v0(field_data(coordsField, sideNodes[0]), 2); + const Vector3d v1(field_data(coordsField, sideNodes[1]), 2); + return crossZ(v1-v0).unit_vector(); + } + ThrowRequireMsg(false, "Unsupported topology " << sideTopology); + + return Vector3d::ZERO; +} + +double get_side_cdfem_cfl(const stk::mesh::BulkData& mesh, + const FieldRef cdfemDisplacementsField, + const FieldRef coordsField, + const stk::mesh::Selector & elementSelector, + const std::function & get_element_volume, + stk::mesh::Entity side) +{ + const Vector3d sideCDFEMDisplacement = get_side_average_cdfem_displacement(mesh, cdfemDisplacementsField, side); + const Vector3d sideNormal = get_side_normal(mesh, coordsField, side); + const double sideNormalDisplacement = Dot(sideCDFEMDisplacement, sideNormal); + + double minElemVolume = 0.; + for (auto elem : StkMeshEntities{mesh.begin_elements(side), mesh.end_elements(side)}) + { + if (elementSelector(mesh.bucket(elem))) + { + const double elemVol = get_element_volume(elem); + if (minElemVolume == 0. || elemVol < minElemVolume) + minElemVolume = elemVol; + } + } + double sideCFL = 0.; + if (minElemVolume > 0.) + { + const double invDim = 1.0 / mesh.mesh_meta_data().spatial_dimension(); + const double lengthScale = std::pow(minElemVolume, invDim); + sideCFL = sideNormalDisplacement / lengthScale; + } + return sideCFL; +} + +double CDMesh::compute_cdfem_cfl() const +{ + stk::diag::TimeBlock timer__(my_timer_compute_CFL); + + const stk::mesh::Selector interfaceSideSelector = my_phase_support.get_all_conformal_surfaces_selector(); + const stk::mesh::Selector elementSelector = selectUnion(my_phase_support.get_conformal_parts()) & get_active_part() & get_locally_owned_part(); + + auto get_element_volume = build_get_element_volume_function(*this); + + double cfl = 0.; + for( auto&& bucket : stk_bulk().get_buckets(stk_bulk().mesh_meta_data().side_rank(), interfaceSideSelector) ) + { + for (auto && side : *bucket) + { + const double sideCFL = get_side_cdfem_cfl(stk_bulk(), get_cdfem_displacements_field(), get_coords_field(), elementSelector, get_element_volume, side); + if (sideCFL > 0.) + cfl = std::max(cfl, sideCFL); + } + } + + const double localCFL = cfl; + stk::all_reduce_max(stk_bulk().parallel(), &localCFL, &cfl, 1); + + return cfl; +} + + +void +CDMesh::update_adaptivity_parent_entities() +{ + if (my_cdfem_support.get_interface_maximum_refinement_level() <= 0) + { + return; + } + + stk::mesh::BulkData& stk_mesh = stk_bulk(); + + stk::mesh::Part & refine_inactive_part = get_refinement_inactive_part(stk_meta(), stk::topology::ELEMENT_RANK); + stk::mesh::Selector adaptive_parent_locally_owned_selector = get_locally_owned_part() & refine_inactive_part; + + stk::mesh::PartVector add_parts; + stk::mesh::PartVector remove_parts; + + std::vector parents; + stk::mesh::get_selected_entities( adaptive_parent_locally_owned_selector, stk_mesh.buckets( stk::topology::ELEMENT_RANK ), parents ); + + for( auto&& parent : parents ) + { + std::vector leaf_children; + get_refinement_leaf_children(stk_mesh, parent, leaf_children); + std::set child_element_parts; + for (auto&& child : leaf_children) + { + const stk::mesh::Part & child_element_part = find_element_part(stk_mesh, child); + child_element_parts.insert(&child_element_part); + } + ThrowRequire(!child_element_parts.empty()); + + if (child_element_parts.size() > 1 || my_phase_support.is_nonconformal(*child_element_parts.begin())) + { + determine_nonconformal_parts(parent, add_parts, remove_parts); + const auto& add_it = std::find(add_parts.begin(), add_parts.end(), &get_parent_part()); + if (add_it != add_parts.end()) + { + add_parts.erase(add_it); + } + } + else if (child_element_parts.size() == 1 && !my_phase_support.is_nonconformal(*child_element_parts.begin())) + { + const PhaseTag & parent_phase = my_phase_support.get_iopart_phase(**child_element_parts.begin()); + determine_conformal_parts(parent, parent_phase, add_parts, remove_parts); + remove_parts.push_back(&get_parent_part()); + remove_parts.push_back(&get_child_part()); + } + stk_mesh.change_entity_parts(parent, add_parts, remove_parts); + } +} + +void +CDMesh::update_uncut_element(const Mesh_Element & elem) +{ + const stk::mesh::Entity elem_entity = elem.entity(); + stk::mesh::BulkData & stk_mesh = stk_bulk(); + if (stk_mesh.bucket(elem_entity).member(get_parent_part()) || elem_io_part_changed(elem)) + { + stk::mesh::PartVector add_parts; + stk::mesh::PartVector remove_parts; + determine_conformal_parts(elem_entity, elem.get_phase(), add_parts, remove_parts); + add_parts.push_back(&get_active_part()); + remove_parts.push_back(&get_parent_part()); + remove_parts.push_back(&get_child_part()); + + stk_mesh.change_entity_parts(elem_entity, add_parts, remove_parts); + } +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::create_node_entities() +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::create_node_entities(void)"); /* %TRACE% */ + stk::mesh::BulkData& stk_mesh = stk_bulk(); + + std::vector node_parents; + std::vector node_requests; + std::vector higher_order_node_requests; + for (auto && node : nodes) + { + stk::mesh::Entity & node_entity = node->entity(); + if (!stk_bulk().is_valid(node_entity)) + { + node->get_parent_entities(node_parents); + if (nullptr == dynamic_cast(node.get())) + node_requests.push_back(ChildNodeRequest(node_parents, &node_entity)); + else + higher_order_node_requests.push_back(ChildNodeRequest(node_parents, &node_entity)); + } + } + + stk::mesh::PartVector node_parts = {&aux_meta().active_part(), + &get_child_edge_node_part(), + &stk_meta().get_topology_root_part(stk::topology::NODE) + }; + batch_create_child_nodes(stk_mesh, node_requests, node_parts, my_aux_meta.get_assert_32bit_flag(), my_aux_meta.get_force_64bit_flag()); + + stk::mesh::PartVector higher_order_node_parts = {&aux_meta().active_part(), + &stk_meta().get_topology_root_part(stk::topology::NODE) + }; + batch_create_child_nodes(stk_mesh, higher_order_node_requests, higher_order_node_parts, my_aux_meta.get_assert_32bit_flag(), my_aux_meta.get_force_64bit_flag()); + + for (auto && node_request : node_requests) + { + stk::mesh::Entity new_node = *node_request.child; + if (stk_mesh.bucket(new_node).member(stk_meta().locally_owned_part())) + { + if (get_parent_node_ids_field().valid()) + { + if (node_request.parents.size() != 2) + krinolog << "Created Steiner node that cannot be restored on restart: " << stk_mesh.identifier(new_node) << " " << node_request.parents.size() << stk::diag::dendl; + store_edge_node_parent_ids(stk_bulk(), get_parent_node_ids_field(), new_node, stk_bulk().identifier(*node_request.parents.front()), stk_bulk().identifier(*node_request.parents.back())); + } + } + } + + // Since batch_create_child_nodes took pointers to the entities, the entityIds were not updated, Ugh. + for (auto&& node : nodes) + { + node->set_entityId_from_entity(stk_mesh); + if(krinolog.shouldPrint(LOG_DEBUG)) + { + if (!node->is_mesh_node()) + krinolog << "NODE ID : " << node->entityId() << ": ancestry: [" << node->get_ancestry() << "]" << stk::diag::dendl; + } + } +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::create_element_and_side_entities(std::vector & side_requests) +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::create_element_and_side_entities(void)"); /* %TRACE% */ + + // Count how many we need to set pool size + unsigned num_local_subelems = 0; + for (auto && elem : elements) + { + if (elem->have_subelements()) + { + std::vector conformal_subelems; + elem->get_subelements(conformal_subelems); + num_local_subelems += conformal_subelems.size(); + } + } + + my_entity_id_pool.reserve(stk::topology::ELEMENT_RANK, num_local_subelems, my_aux_meta.get_assert_32bit_flag(), my_aux_meta.get_force_64bit_flag()); + + stk::mesh::PartVector add_parts; + stk::mesh::PartVector remove_parts; + + for (const auto & elem : elements) + { + if (elem->have_subelements()) + { + std::vector conformal_subelems; + elem->get_subelements(conformal_subelems); + + // check for corner case of a single subelement that is coincident with parent + if (elem->is_single_coincident()) + { + handle_single_coincident_subelement(*elem, conformal_subelems[0], side_requests); + } + else + { + create_subelement_mesh_entities(*elem, conformal_subelems); + attach_existing_and_identify_missing_subelement_sides(*elem, conformal_subelems, side_requests); + + determine_nonconformal_parts(elem->entity(), add_parts, remove_parts); + stk_bulk().change_entity_parts(elem->entity(), add_parts, remove_parts); + } + } + else + { + update_uncut_element(*elem); + } + } + + update_adaptivity_parent_entities(); +} + +//-------------------------------------------------------------------------------- + +void +CDMesh::prolongation() +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::prolongation(void)"); /* %TRACE% */ + stk::diag::TimeBlock timer__(my_timer_prolongation); + + BoundingBox proc_target_bbox; + for (auto && node : nodes) + { + proc_target_bbox.accommodate(node->coordinates()); + } + if(nodes.empty()) + { + proc_target_bbox.accommodate(Vector3d::ZERO); + } + + const bool guess_and_check_proc_padding = + my_old_mesh->stash_step_count() >= 0 && + stk_bulk().parallel_size() > 1; + double proc_padding = 0.0; + if (guess_and_check_proc_padding) + { + const double max_elem_size = compute_maximum_element_size(stk_bulk()); + proc_padding = 3.0*max_elem_size; + proc_target_bbox.pad(proc_padding); + } + else + { + proc_target_bbox.pad_epsilon(); + } + + bool done = false; + while (!done) + { + done = true; + + std::vector proc_target_bboxes; + BoundingBox::gather_bboxes( proc_target_bbox, proc_target_bboxes ); + + const size_t facet_precomm_size = my_old_mesh->my_prolong_facets.size(); + ProlongationFacet::communicate(*my_old_mesh, my_old_mesh->my_prolong_facets, my_old_mesh->my_prolong_node_map, proc_target_bboxes); + + my_old_mesh->build_prolongation_trees(); + + my_old_mesh->communicate_prolongation_facet_fields(); + + const stk::mesh::Part & active_part = get_active_part(); + + // update nodal fields + my_missing_remote_prolong_facets = false; + for (auto && node : nodes) + { + if(!(node->is_prolonged()) && stk_bulk().bucket(node->entity()).member(active_part)) + { + node->prolongate_fields(*this); + } + } + + const double max_cdfem_displacement = get_maximum_cdfem_displacement(); + if (guess_and_check_proc_padding && (my_missing_remote_prolong_facets || max_cdfem_displacement > proc_padding)) + { + const double growth_multiplier = 1.5; + krinolog << "Must redo ghosting for prolongation. New size = " << growth_multiplier*max_cdfem_displacement <<"\n"; + proc_target_bbox.pad(growth_multiplier*max_cdfem_displacement-proc_padding); + proc_padding = growth_multiplier*max_cdfem_displacement; + done = false; + + for (size_t i = facet_precomm_size; i < my_old_mesh->my_prolong_facets.size(); i++ ) + delete my_old_mesh->my_prolong_facets[i]; + my_old_mesh->my_prolong_facets.resize(facet_precomm_size); + + for (auto && node : nodes) + { + node->set_prolonged_flag(false); + } + } + } + + rebase_cdfem_displacements(); + + // prolongate element fields + for (const auto & elem : elements) + { + if (elem->have_subelements()) + { + std::vector conformal_subelems; + elem->get_subelements(conformal_subelems); + + for (auto && subelem : conformal_subelems) + { + subelem->prolongate_fields(*this); + } + } + else + { + elem->prolongate_fields(*this); + } + } + + // We might want to check what causes any parallel discrepencies, but sync everything here + const stk::mesh::FieldVector & all_fields = stk_bulk().mesh_meta_data().get_fields(); + const std::vector const_fields(all_fields.begin(), all_fields.end()); + for (auto && f : all_fields) + { + f->sync_to_host(); + f->modify_on_host(); + } + stk::mesh::communicate_field_data(stk_bulk(), const_fields); +} + +void +CDMesh::rebase_cdfem_displacements() +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::rebase_cdfem_displacements(void)"); /* %TRACE% */ + // rebase cdfem mesh displacements such that STATE_OLD is zero + const FieldRef cdfem_displacements_field = get_cdfem_displacements_field(); + if (cdfem_displacements_field.valid()) + { + const unsigned field_length = cdfem_displacements_field.length(); + std::vector stk_fields(cdfem_displacements_field.number_of_states()); + for ( unsigned is = 0 ; is < cdfem_displacements_field.number_of_states(); ++is ) + { + const stk::mesh::FieldState state = static_cast(is); + stk_fields[is] = cdfem_displacements_field.field_state(state); + } + + std::vector< stk::mesh::Entity> objs; + stk::mesh::get_selected_entities( stk::mesh::selectField(cdfem_displacements_field), stk_bulk().buckets( stk::topology::NODE_RANK ), objs ); + + std::vector old_displacement(field_length); + + const unsigned len = objs.size(); + for ( unsigned iObj(0); iObj < len; ++iObj ) + { + stk::mesh::Entity node = objs[iObj]; + + const double * old_data = field_data( stk_fields[stk::mesh::StateOld], node); + ThrowRequire(nullptr != old_data); + for (unsigned d=0; d( stk_fields[is], node); + ThrowRequire(nullptr != displacement); + for (unsigned d=0; d(cdfem_displacements_field, *b); + ThrowAssert(nullptr != cdfem_displacements); + + const unsigned field_length = cdfem_displacements_field.length(*b); + + const int num_nodes = b->size(); + for(int n=0; nprimary_entity_rank() == stk::topology::ELEMENT_RANK) + { + volume_conformal_parts.push_back(conformal_part); + } + else if (my_phase_support.is_interface(conformal_part)) + { + interfacial_conformal_parts.push_back(conformal_part); + } + } + + print_volume_or_surface_area(stk_bulk(), stk::topology::ELEMENT_RANK, get_active_part(), volume_conformal_parts); + print_volume_or_surface_area(stk_bulk(), stk_meta().side_rank(), get_active_part(), interfacial_conformal_parts); +} + +void +CDMesh::debug_output() const +{ + for (const auto & elem : elements) + debug_elem_parts_and_relations(stk_bulk(), *elem); + krinolog << stk::diag::dendl; + + for (auto && node : nodes) + debug_nodal_parts_and_fields(stk_bulk(), node.get()); + krinolog << stk::diag::dendl; + + debug_sides(stk_bulk(), get_active_part()); +} + +const Mesh_Element * CDMesh::find_mesh_element(stk::mesh::EntityId elemId, const std::vector> & searchElements) +{ + auto lb_cmp = [](const std::unique_ptr & elem, stk::mesh::EntityId target_id) { return elem->entityId() < target_id; }; + auto first = std::lower_bound(searchElements.begin(),searchElements.end(), elemId, lb_cmp); + + if (first != searchElements.end() && (*first)->entityId() == elemId) + { + return (*first).get(); + } + + return nullptr; +} + +} // namespace krino + + diff --git a/packages/krino/krino/krino_lib/Akri_CDMesh.hpp b/packages/krino/krino/krino_lib/Akri_CDMesh.hpp new file mode 100644 index 000000000000..39155fa144c0 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDMesh.hpp @@ -0,0 +1,333 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_CDMesh_h +#define Akri_CDMesh_h + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino { + +template +class CompleteDecompositionFixture; +class SubElement; +class ElementObj; +class Mesh_Element; +class SubElementNode; +class SubElementMeshNode; +class ProlongationNodeData; +class ProlongationPointData; +class ProlongationElementData; +class ProlongationFacet; +class InterpolationEdge; +class LevelSet; +class Phase_Support; +class AuxMetaData; +struct SideRequest; +class InterfaceGeometry; + +typedef std::vector< const SubElementNode * > NodeVec; +typedef std::set< const SubElementNode * > NodeSet; +typedef std::vector< const ProlongationFacet * > ProlongFacetVec; +typedef std::unordered_map EntityProlongationNodeMap; +typedef std::unordered_map EntityProlongationElementMap; + +enum CDMeshStatus +{ + MESH_MODIFIED = 1, + COORDINATES_MAY_BE_MODIFIED = 2 +}; + +class CDMesh { +public: + + CDMesh( stk::mesh::BulkData & mesh, const std::shared_ptr & old_mesh); + + virtual ~CDMesh(); + + static void check_isovariable_field_existence_on_decomposed_blocks(stk::mesh::BulkData & mesh, + const bool conformal_parts_require_field); + static bool decomposition_needs_update(const InterfaceGeometry & interfaceGeometry, + const std::vector> & periodic_node_pairs); + static void handle_possible_failed_time_step( stk::mesh::BulkData & mesh, const int step_count ); + static int decompose_mesh( stk::mesh::BulkData & mesh, + const InterfaceGeometry & interfaceGeometry, + const int step_count, + const std::vector> & periodic_node_pairs ); + static void nonconformal_adaptivity(stk::mesh::BulkData & mesh, const InterfaceGeometry & interfaceGeometry); + static void mark_interface_elements_for_adaptivity(stk::mesh::BulkData & mesh, const InterfaceGeometry & interfaceGeometry, const std::string & marker_field_name, const int num_refinements); + void delete_cdfem_parent_elements(); + static void fixup_adapted_element_parts(stk::mesh::BulkData & mesh); + static void rebuild_from_restart_mesh(stk::mesh::BulkData & mesh); + void rebuild_after_rebalance(); + + CDMesh* get_old_mesh() { return my_old_mesh.get(); } + const CDMesh* get_old_mesh() const { return my_old_mesh.get(); } + static CDMesh* get_new_mesh() { return the_new_mesh.get(); } + + static void update_cdfem_constrained_nodes(); + void snap_and_update_fields_and_captured_domains(const InterfaceGeometry & interfaceGeometry, + NodeToCapturedDomainsMap & nodesToCapturedDomains) const; + + void communicate_prolongation_facet_fields() const; + const ProlongationPointData * find_prolongation_node(const SubElementNode & node) const; + const SubElementNode * find_node_with_common_ancestry(const SubElementNode * new_node) const; + + bool is_interface(const PhaseTag & phase, const InterfaceID interface) const; + PhaseTag determine_entity_phase(stk::mesh::Entity obj) const; + + void check_isovariable_field_existence_on_decomposed_blocks(const bool conformal_parts_require_field) const; + + std::vector get_nonconformal_elements() const; + void generate_nonconformal_elements(); + + bool triangulate(const InterfaceGeometry & interfaceGeometry); //return value indicates if any changes were made + void decompose(); + void cut_sharp_features(); + void snap_nearby_intersections_to_nodes(const InterfaceGeometry & interfaceGeometry, NodeToCapturedDomainsMap & domainsAtNodes); + void set_phase_of_uncut_elements(const InterfaceGeometry & interfaceGeometry); + + int spatial_dim() const { return my_spatial_dim; } + + const Phase_Support & get_phase_support() const { return my_phase_support; } + const CDFEM_Support & get_cdfem_support() const { return my_cdfem_support; } + CDFEM_Support & get_cdfem_support() { return my_cdfem_support; } + bool need_nodes_for_prolongation() const { return INTERPOLATION != get_prolongation_model() && my_stash_step_count >= 0; } + bool need_facets_for_prolongation() const { return ALE_NEAREST_POINT == get_prolongation_model() && my_stash_step_count >= 0; } + Prolongation_Model get_prolongation_model() const { return my_cdfem_support.get_prolongation_model(); } + Edge_Interpolation_Model get_edge_interpolation_model() const { return my_cdfem_support.get_edge_interpolation_model(); } + int num_ls_fields() const { return my_cdfem_support.num_ls_fields(); } + int get_ls_index(const LevelSet * ls) const { return my_cdfem_support.get_ls_index(ls); } + const LS_Field & ls_field(int i) const { return my_cdfem_support.ls_field(i); } + const std::vector & ls_fields() const { return my_cdfem_support.ls_fields(); } + const std::vector & all_interface_ids() const; + std::vector active_interface_ids() const; + // This should really only be used for unit test purposes. + void add_interface_id(const InterfaceID id) { crossing_keys.push_back(id); } + + const FieldRef get_coords_field() const { return my_cdfem_support.get_coords_field(); } + const FieldRef get_cdfem_displacements_field() const { return my_cdfem_support.get_cdfem_displacements_field(); } + const FieldSet & get_ale_prolongation_fields() const { return my_cdfem_support.get_ale_prolongation_fields(); } + const FieldSet & get_interpolation_fields() const { return my_cdfem_support.get_interpolation_fields(); } + const FieldSet & get_zeroed_fields() const { return my_cdfem_support.get_zeroed_fields(); } + const FieldSet & get_element_fields() const { return my_cdfem_support.get_element_fields(); } + const CDFEM_Snapper & get_snapper() const { return my_cdfem_support.get_snapper(); } + + const CDFEM_Inequality_Spec * get_death_spec(int ls_index) const { return my_cdfem_support.get_death_spec(ls_index); } + + stk::mesh::Part & get_parent_part() const { return my_cdfem_support.get_parent_part(); } + stk::mesh::Part & get_child_part() const { return my_cdfem_support.get_child_part(); } + stk::mesh::Part & get_internal_side_part() const { return my_cdfem_support.get_internal_side_part(); } + stk::mesh::Part & get_active_part() const; + stk::mesh::Part & get_locally_owned_part() const; + stk::mesh::Part & get_globally_shared_part() const; + stk::mesh::Part & get_block_boundary_part() const; + + // methods for finding or creating subelement nodes + const SubElementNode * create_mesh_node( const Mesh_Element * owner, + const int lnn, + stk::mesh::Entity nodeEntity ); + const SubElementNode * create_steiner_node( const Mesh_Element * owner, + const NodeVec & parents, + const std::vector & weights ); + const SubElementNode * create_child_internal_or_face_node( const Mesh_Element * owner, + const NodeVec & parents, + const std::vector & weights ); + const SubElementNode * create_edge_node( const Mesh_Element * owner, + const SubElementNode * parent1, const SubElementNode * parent2, + const double position); + const SubElementNode * create_midside_node( const Mesh_Element * owner, + const SubElementNode * parent1, const SubElementNode * parent2, stk::mesh::Entity entity = stk::mesh::Entity()); + void create_node_obj( const SubElementNode * node, NodeVec & parents ); + + void determine_node_signs(const InterfaceID & interface); + void decompose_edges(const InterfaceID & interface); + void determine_node_scores(const InterfaceID & interface); + + const AuxMetaData& aux_meta() const { return my_aux_meta; } + AuxMetaData& aux_meta() { return my_aux_meta; } + const stk::mesh::MetaData& stk_meta() const { return my_meta; } + stk::mesh::MetaData& stk_meta() { return my_meta; } + const stk::mesh::BulkData& stk_bulk() const { return my_meta.mesh_bulk_data(); } + stk::mesh::BulkData& stk_bulk() { return my_meta.mesh_bulk_data(); } + + void add_periodic_node_pair(stk::mesh::Entity node1, stk::mesh::Entity node2); + + // just search for existing mesh node and return NULL if not found + const SubElementMeshNode * get_mesh_node( stk::mesh::EntityId node_id ) const; + // just search for existing mesh node and return NULL if not found, starting with subelement node (possibly from another mesh) + const SubElementMeshNode * get_mesh_node(const SubElementNode * new_node) const; + + bool check_element_side_parts() const; + + const SubElement * find_child_element(stk::mesh::Entity elem_mesh_obj) const; + static const Mesh_Element * find_mesh_element(stk::mesh::EntityId elemId, const std::vector > & searchElements); + const Mesh_Element * find_mesh_element(stk::mesh::EntityId elemId) const { return find_mesh_element(elemId, elements); } + Mesh_Element * find_mesh_element(stk::mesh::EntityId elemId) { return const_cast(find_mesh_element(elemId, elements)); } // Scott Meyers says this is the right way + bool get_parent_child_coord_transformation(stk::mesh::Entity elem_mesh_obj, double * dParentdChild) const; + void get_parent_nodes_and_weights(stk::mesh::Entity child, stk::mesh::Entity & parent0, stk::mesh::Entity & parent1, double & position) const; + stk::mesh::Entity get_parent_element(stk::mesh::Entity elem_mesh_obj) const; + + double compute_cdfem_cfl() const; + + int stash_step_count() const { return my_stash_step_count; } + const ProlongationNodeData * fetch_prolong_node(stk::mesh::EntityId node_id) const { EntityProlongationNodeMap::const_iterator it = my_prolong_node_map.find(node_id); return ( (it == my_prolong_node_map.end()) ? NULL : (it->second) ); } + const ProlongationElementData * fetch_prolong_element(stk::mesh::EntityId elem_id) const { EntityProlongationElementMap::const_iterator it = my_prolong_element_map.find(elem_id); return ( (it == my_prolong_element_map.end()) ? NULL : (it->second) ); } + + void debug_output() const; + void print_conformal_volumes_and_surface_areas() const; + std::vector> & get_elements() { return elements; } + const std::vector> & get_elements() const { return elements; } + + // FIXME: Make these private. + SubElementNode * add_managed_node(std::unique_ptr node); + // expose vector of nodes + std::vector > nodes; + // expose vector of elements + std::vector > elements; + // expose vector of sorted child elements + std::vector child_elements; + +private: + //: Default constructor not allowed + CDMesh(); + + template + friend class CompleteDecompositionFixture; + + template + friend class AnalyticDecompositionFixture; + + void build_parallel_hanging_edge_nodes(); + void handle_hanging_children(const InterfaceID & interface); + void parallel_sync_nodes_on_interface(); + + void sync_node_signs_on_constrained_nodes(); + void parallel_sync_node_signs_on_shared_nodes(); + void sync_node_scores_on_constrained_nodes(); + void parallel_sync_node_scores_on_shared_nodes(); + + bool decomposition_has_changed(const InterfaceGeometry & interfaceGeometry); + bool elem_io_part_changed(const ElementObj & elem) const; + void determine_nonconformal_parts(stk::mesh::Entity entity, stk::mesh::PartVector & add_parts, stk::mesh::PartVector & remove_parts) const; + void determine_conformal_parts(stk::mesh::Entity entity, const PhaseTag & phase, stk::mesh::PartVector & add_parts, stk::mesh::PartVector & remove_parts) const; + void determine_conformal_parts(const stk::mesh::PartVector & current_parts, const stk::mesh::EntityRank entity_rank, const PhaseTag & phase, stk::mesh::PartVector & add_parts, stk::mesh::PartVector & remove_parts) const; + void determine_child_conformal_parts(stk::topology topology, const stk::mesh::PartVector & parent_parts, const PhaseTag & phase, stk::mesh::PartVector & child_parts) const; + void determine_element_side_parts(const stk::mesh::Entity side, stk::mesh::PartVector & add_parts, stk::mesh::PartVector & remove_parts) const; + bool element_side_should_be_active(const stk::mesh::Entity side) const; + void build_and_stash_old_mesh(const int stepCount); + void stash_field_data(const int step_count, const CDMesh & new_mesh) const; + void stash_nodal_field_data(const CDMesh & new_mesh) const; + void stash_elemental_field_data() const; + + void clear_prolongation_trees() const; + void build_prolongation_trees() const; + + void clear(); + void clear_prolongation_data() const; + + Mesh_Element * create_mesh_element(stk::mesh::Entity mesh_obj); + SubElementMeshNode * add_managed_mesh_node( std::unique_ptr node ); + + bool modify_mesh(); + void update_adaptivity_parent_entities(); + void handle_single_coincident_subelement(const Mesh_Element & elem, const SubElement * subelem, std::vector & side_requests); + void create_subelement_mesh_entities(const Mesh_Element & elem, + const std::vector conformal_subelems); + void attach_existing_and_identify_missing_subelement_sides(const Mesh_Element & elem, + const std::vector conformal_subelems, + std::vector & side_requests); + void update_uncut_element(const Mesh_Element & elem); + + void get_unused_old_child_elements(std::vector & unused_old_child_elems); + void set_entities_for_identical_nodes(); + bool set_entities_for_existing_child_elements(); + void create_new_mesh_entities(); + void create_node_entities(); + void create_element_and_side_entities(std::vector & side_requests); + + void prolongation(); + void rebase_cdfem_displacements(); + double get_maximum_cdfem_displacement() const; + + void add_possible_interface_sides(std::vector & sideRequests) const; + bool check_element_side_parts(const std::vector & side_nodes) const; + void update_element_side_parts(); + + void parallel_communicate_elemental_death_fields() const; + + void generate_sorted_child_elements(); + void cache_node_ids(); + + stk::mesh::Part & get_child_edge_node_part() const { return my_cdfem_support.get_child_edge_node_part(); } + FieldRef get_parent_node_ids_field() const { return my_cdfem_support.get_parent_node_ids_field(); } + + void rebuild_child_part(); + void rebuild_parent_and_active_parts_using_nonconformal_and_child_parts(); + void restore_subelement_edge_nodes(); + void restore_subelements(); + const SubElementNode * build_subelement_edge_node(const stk::mesh::Entity node_entity); + + stk::mesh::MetaData& my_meta; + AuxMetaData& my_aux_meta; + EntityIdPool my_entity_id_pool; + + const int my_spatial_dim; + CDFEM_Support & my_cdfem_support; + Phase_Support & my_phase_support; + typedef std::unordered_map NodeMap; + NodeMap mesh_node_map; + + std::shared_ptr my_old_mesh; + static std::shared_ptr the_new_mesh; + + std::unordered_map > my_periodic_node_id_map; + + mutable int my_stash_step_count; + mutable std::map,std::unique_ptr>> my_phase_prolong_tree_map; + + mutable bool my_missing_remote_prolong_facets; + mutable EntityProlongationNodeMap my_prolong_node_map; + mutable EntityProlongationElementMap my_prolong_element_map; + mutable ProlongFacetVec my_prolong_facets; + + mutable std::vector crossing_keys; + + mutable stk::diag::Timer my_timer_decompose; + mutable stk::diag::Timer my_timer_decomposition_has_changed; + mutable stk::diag::Timer my_timer_snap; + mutable stk::diag::Timer my_timer_stash_field_data; + mutable stk::diag::Timer my_timer_modify_mesh; + mutable stk::diag::Timer my_timer_prolongation; + mutable stk::diag::Timer my_timer_compute_CFL; + stk::mesh::PartVector my_attribute_parts; + + mutable std::map,const SubElementNode*> my_midside_node_map; +}; + +} // namespace krino + +#endif // Akri_CDMesh_h diff --git a/packages/krino/krino/krino_lib/Akri_CDMesh_Debug.cpp b/packages/krino/krino/krino_lib/Akri_CDMesh_Debug.cpp new file mode 100644 index 000000000000..eccd3249c341 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDMesh_Debug.cpp @@ -0,0 +1,229 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include + +namespace krino { + +void +debug_elem_parts_and_relations(const stk::mesh::BulkData & mesh, const Mesh_Element & elem) +{ + const stk::mesh::MetaData & meta = mesh.mesh_meta_data(); + stk::mesh::Entity elem_obj = elem.entity(); + + krinolog << "Elem: id=" << mesh.identifier(elem_obj) << "\n"; + krinolog << " Mesh parts="; + for(auto && elem_part : mesh.bucket(elem_obj).supersets()) + { + krinolog << "\"" << elem_part->name() << "\"" << " "; + } + krinolog << "\n"; + + krinolog << " Nodes: "; + for (auto && elem_node : elem.get_nodes()) + { + krinolog << elem_node->entityId() << " "; + } + krinolog << "\n"; + + const stk::topology topology = elem.coord_topology(); + std::vector side_nodes; + for (unsigned s = 0; s < topology.num_sides(); ++s) + { + stk::mesh::Entity side_obj = find_entity_by_ordinal(mesh, elem_obj, meta.side_rank(), s); + if (!mesh.is_valid(side_obj)) continue; + krinolog << " Elem side: side=" << s << ", id=" << mesh.identifier(side_obj); + krinolog << ", side node ids: "; + const stk::topology side_topology = topology.side_topology(s); + const stk::mesh::Entity * elem_nodes_ptr = mesh.begin_nodes(elem_obj); + side_nodes.resize(side_topology.num_nodes()); + topology.side_nodes(elem_nodes_ptr, s, side_nodes.begin()); + for (unsigned n=0; nname() << "\"" << " "; + } + krinolog << "\n"; + } + + if (elem.have_subelements()) + { + std::vector conformal_subelems; + elem.get_subelements(conformal_subelems); + + for (auto && subelem : conformal_subelems) + { + stk::mesh::Entity subelem_obj = subelem->entity(); + if (!mesh.is_valid(subelem_obj)) + { + // single conformal subelement + if (1 == conformal_subelems.size()) + { + subelem_obj = elem.entity(); + } + if (!mesh.is_valid(subelem_obj)) + { + // For debugging intermediate stage before subelement mesh objects are determined. + krinolog << " Subelem: id=-1" << "\n"; + continue; + } + } + + krinolog << " Subelem: id=" << mesh.identifier(subelem_obj) << "\n"; + krinolog << " Mesh parts="; + for(auto && subelem_part : mesh.bucket(subelem_obj).supersets()) + { + krinolog << "\"" << subelem_part->name() << "\"" << " "; + } + krinolog << "\n"; + + krinolog << " SubElemNodes: "; + for (auto && sub_node : subelem->get_nodes()) + { + krinolog << sub_node->entityId() << " "; + } + krinolog << "\n"; + + for (unsigned s = 0; s < topology.num_sides(); ++s) + { + stk::mesh::Entity side_obj = find_entity_by_ordinal(mesh, subelem_obj, meta.side_rank(), s); + if (!mesh.is_valid(side_obj)) continue; + krinolog << " Subelem side: side=" << s << ", id=" << mesh.identifier(side_obj); + krinolog << ", side node ids: "; + const stk::topology side_topology = topology.side_topology(s); + const stk::mesh::Entity * elem_nodes_ptr = mesh.begin_nodes(subelem_obj); + side_nodes.resize(side_topology.num_nodes()); + topology.side_nodes(elem_nodes_ptr, s, side_nodes.begin()); + for (unsigned n=0; nname() << "\"" << " "; + } + krinolog << "\n"; + } + } + } +} + +void +debug_nodal_parts_and_fields(const stk::mesh::BulkData & mesh, const SubElementNode * node) +{ + stk::mesh::Entity node_obj = node->entity(); + if (!mesh.is_valid(node_obj)) + { + krinolog << "Node: identifier=nullptr\n"; + return; + } + + std::string type = "UNKNOWN"; + if (nullptr != dynamic_cast(node)) type = "internal"; + if (nullptr != dynamic_cast(node)) type = "edge"; + if (nullptr != dynamic_cast(node)) type = "mesh"; + + krinolog << "Node: identifier=" << mesh.identifier(node_obj) << ", type=" << type << "\n"; + if (mesh.mesh_meta_data().spatial_dimension() == 2) + { + krinolog << " coords =" << "(" << node->coordinates()[0] << ", " << node->coordinates()[1] << ")" << "\n"; + } + else if (mesh.mesh_meta_data().spatial_dimension() == 3) + { + krinolog << " coords =" << "(" << node->coordinates()[0] << ", " << node->coordinates()[1] << ", " << node->coordinates()[2] << ")" << "\n"; + } + krinolog << " Mesh parts="; + for(auto && node_part : mesh.bucket(node_obj).supersets()) + { + krinolog << "\"" << node_part->name() << "\"" << " "; + } + krinolog << "\n"; + + for ( auto && stk_field : mesh.mesh_meta_data().get_fields() ) + { + const FieldRef field(stk_field); + + if( field.entity_rank()!=stk::topology::NODE_RANK || !field.type_is() ) continue; + + const unsigned field_length = field.length(); + + double * data = field_data(field, node_obj); + if (nullptr == data) + { + krinolog << " Field: field_name=" << field.name() << ", field_state=" << field.state() << " -> NOT DEFINED." << "\n"; + } + else + { + if (1 == field_length) + { + krinolog << " Field: field_name=" << field.name() << ", field_state=" << field.state() << ", value=" << *data << "\n"; + } + else + { + for (unsigned i=0; i objs; + stk::mesh::get_entities( mesh, mesh.mesh_meta_data().side_rank(), objs ); + + const unsigned len = objs.size(); + for ( unsigned iObj(0); iObj < len; ++iObj ) { + + stk::mesh::Entity side = objs[iObj]; + + krinolog << "side, id=" << mesh.identifier(side) << ", active = " << mesh.bucket(side).member(active_part) << ", num elem = " << mesh.num_elements(side) << ", ioparts="; + const stk::mesh::PartVector & side_parts = mesh.bucket(side).supersets(); + for(stk::mesh::PartVector::const_iterator part_iter = side_parts.begin(); part_iter != side_parts.end(); ++part_iter) + { + const stk::mesh::Part * const part = *part_iter; + if (stk::io::is_part_io_part(*part)) krinolog << part->name() << " "; + } + krinolog << "\n"; + const unsigned num_side_elems = mesh.num_elements(side); + const stk::mesh::Entity* side_elems = mesh.begin_elements(side); + for (unsigned side_elem_index=0; side_elem_index +#include + +namespace krino { + void debug_elem_parts_and_relations(const stk::mesh::BulkData & mesh, const Mesh_Element & elem); + void debug_nodal_parts_and_fields(const stk::mesh::BulkData & mesh, const SubElementNode * node); + void debug_sides(const stk::mesh::BulkData & mesh, stk::mesh::Part & active_part); +} + +#endif /* KRINO_INCLUDE_AKRI_CDMESH_DEBUG_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_CDMesh_Refinement.cpp b/packages/krino/krino/krino_lib/Akri_CDMesh_Refinement.cpp new file mode 100644 index 000000000000..840750152089 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDMesh_Refinement.cpp @@ -0,0 +1,367 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../interface_geometry_interface/Akri_InterfaceGeometry.hpp" +namespace krino { + +namespace { + void set_refine_if_not_reached_max_refine(stk::mesh::Entity elem, + const int interface_max_refine_level, + FieldRef elem_marker, + FieldRef refine_level_field, + FieldRef transition_element_field) + { + int & marker = *field_data(elem_marker, elem); + const int refine_level = *field_data(refine_level_field, elem); + const int transition_element = *field_data(transition_element_field, elem); + + if (refine_level >= transition_element+interface_max_refine_level ) + { + marker = Refinement_Marker::NOTHING; + } + else + { + marker = Refinement_Marker::REFINE; + } + } +} + +static bool node_is_snapped_to_interface(stk::mesh::Entity node, + const InterfaceID & interface, + const std::unordered_map> & nodesSnappedInterfaces) +{ + auto nodeSnappedInterfaces = nodesSnappedInterfaces.find(node); + const bool nodeIsSnapped = nodeSnappedInterfaces != nodesSnappedInterfaces.end() && + nodeSnappedInterfaces->second.find(interface) != nodeSnappedInterfaces->second.end(); + return nodeIsSnapped; +} + +void +refine_edges_with_multiple_unsnapped_crossings(const stk::mesh::BulkData& mesh, + const std::vector & edgeIntersections, + const int interface_max_refine_level, + FieldRef elem_marker_field, + FieldRef refine_level_field, + FieldRef transition_element_field, + const std::unordered_map> & nodesSnappedInterfaces) +{ + // Refine any edge with multiple crossings that are not snapped to the nodes + std::set> edgesWithUnsnappedCrossings; + for (auto && edgeIntersection : edgeIntersections) + { + const EdgeIntersection edge(edgeIntersection); + + const InterfaceID & interface = edge.interface; + const bool node1IsSnapped = node_is_snapped_to_interface(edge.nodes[0], interface, nodesSnappedInterfaces); + const bool node2IsSnapped = node_is_snapped_to_interface(edge.nodes[1], interface, nodesSnappedInterfaces); + if (!node1IsSnapped && !node2IsSnapped) + { + const auto insertion = edgesWithUnsnappedCrossings.insert(edge.nodes); + const bool alreadyInSet = !insertion.second; + if (alreadyInSet) + { + std::vector edge_elems; + stk::mesh::get_entities_through_relations(mesh, {edge.nodes[0], edge.nodes[1]}, + stk::topology::ELEMENT_RANK, edge_elems); + for (auto && elem : edge_elems) + { + set_refine_if_not_reached_max_refine(elem, interface_max_refine_level, elem_marker_field, refine_level_field, transition_element_field); + } + } + } + } +} + +void +refine_edges_with_nodes_with_multiple_snapped_interfaces(const stk::mesh::BulkData& mesh, + const std::vector & edgeIntersections, + const int interface_max_refine_level, + FieldRef elem_marker_field, + FieldRef refine_level_field, + FieldRef transition_element_field, + const std::unordered_map> & node_snapped_interfaces) +{ + for (auto && edgeIntersection : edgeIntersections) + { + const EdgeIntersection edge(edgeIntersection); + + auto node1_snapped_interfaces = node_snapped_interfaces.find(edge.nodes[0]); + auto node2_snapped_interfaces = node_snapped_interfaces.find(edge.nodes[1]); + const unsigned num_node1_interfaces = (node1_snapped_interfaces != node_snapped_interfaces.end()) ? + node1_snapped_interfaces->second.size() : 0; + const unsigned num_node2_interfaces = (node2_snapped_interfaces != node_snapped_interfaces.end()) ? + node2_snapped_interfaces->second.size() : 0; + if (num_node1_interfaces > 1 || num_node2_interfaces > 1) + { + std::vector edge_elems; + stk::mesh::get_entities_through_relations(mesh, {edge.nodes[0], edge.nodes[1]}, + stk::topology::ELEMENT_RANK, edge_elems); + for (auto && elem : edge_elems) + { + set_refine_if_not_reached_max_refine(elem, interface_max_refine_level, elem_marker_field, refine_level_field, transition_element_field); + } + } + } +} + +void +determine_which_interfaces_snap_to_each_node_and_unsnappable_nodes(const stk::mesh::BulkData& mesh, + const std::vector & edgeIntersections, + const std::vector & active_interface_ids, + const CDFEM_Snapper & snapper, + FieldRef node_marker_field, + std::unordered_map> & node_snapped_interfaces, + std::set & unsnappable_nodes) +{ + for (auto && interface : active_interface_ids) + { + stk::mesh::field_fill(0, node_marker_field); + for (auto && edgeIntersection : edgeIntersections) + { + const EdgeIntersection edge(edgeIntersection); + + int & node1_marker = *field_data(node_marker_field, edge.nodes[0]); + int & node2_marker = *field_data(node_marker_field, edge.nodes[1]); + if (snapper.snap_lo(edge.crossingLocation)) + { + if (parts_are_compatible_for_snapping(mesh, edge.nodes[0], edge.nodes[1])) + node1_marker = 2; + else + node1_marker = std::max(node1_marker, 1); + } + if (snapper.snap_hi(edge.crossingLocation)) + { + if (parts_are_compatible_for_snapping(mesh, edge.nodes[1], edge.nodes[0])) + node2_marker = 2; + else + node2_marker = std::max(node2_marker, 1); + } + } + stk::mesh::parallel_max(mesh, {&node_marker_field.field()}); + + for(const auto & b_ptr : mesh.buckets( stk::topology::NODE_RANK )) + { + stk::mesh::Bucket & b = *b_ptr; + int * node_marker = field_data(node_marker_field, b); + for(unsigned i=0; i < b.size(); ++i) + { + if (node_marker[i] == 1) + { + unsnappable_nodes.insert(b[i]); + } + else if (node_marker[i] == 2) + { + node_snapped_interfaces[b[i]].insert(interface); + } + } + } + } +} + +void +resolve_fine_features(const stk::mesh::BulkData& mesh, + const std::vector & edgeIntersections, + const std::vector & active_interface_ids, + const CDFEM_Snapper & snapper, + const int interface_max_refine_level, + FieldRef elem_marker_field, + FieldRef node_marker_field, + FieldRef refine_level_field, + FieldRef transition_element_field) +{ + std::unordered_map> node_snapped_interfaces; + std::set unsnappable_nodes; + + determine_which_interfaces_snap_to_each_node_and_unsnappable_nodes(mesh, edgeIntersections, active_interface_ids, snapper, node_marker_field, node_snapped_interfaces, unsnappable_nodes); + + // Attempt at minimally aggressive. Works pretty well and has has lower element counts. + refine_edges_with_multiple_unsnapped_crossings(mesh, edgeIntersections, interface_max_refine_level, elem_marker_field, refine_level_field, transition_element_field, node_snapped_interfaces); + //refine_edges_with_unsnappable_nodes(interface_max_refine_level, elem_marker_field, refine_level_field, transition_element_field, unsnappable_nodes); + refine_edges_with_nodes_with_multiple_snapped_interfaces(mesh, edgeIntersections, interface_max_refine_level, elem_marker_field, refine_level_field, transition_element_field, node_snapped_interfaces); +} + +void +mark_nearest_node_on_cut_edges(const stk::mesh::BulkData& mesh, + const std::vector & edgeIntersections, + FieldRef node_marker_field) +{ + const double overlap = 0.25; + + stk::mesh::field_fill(0, node_marker_field); + + for (auto && edgeIntersection : edgeIntersections) + { + const EdgeIntersection edge(edgeIntersection); + + if (edge.crossingLocation < 0.5+overlap) + *field_data(node_marker_field, edge.nodes[0]) = 1; + if (edge.crossingLocation > 0.5-overlap) + *field_data(node_marker_field, edge.nodes[1]) = 1; + } + stk::mesh::parallel_max(mesh, {&node_marker_field.field()}); +} + +void +mark_interface_elements_for_adaptivity(const stk::mesh::BulkData& mesh, + const InterfaceGeometry & interfaceGeometry, + const std::vector & active_interface_ids, + const CDFEM_Snapper & snapper, + const AuxMetaData& aux_meta, + const CDFEM_Support & cdfem_support, + const FieldRef coords_field, + const std::string & marker_field_name, + const int num_refinements) +{ +/* %TRACE[SPEC]% */ Tracespec trace__("CDMesh::mark_interface_elements_for_adaptivity(const std::string & marker_field_name, const int num_refinements)"); /* %TRACE% */ + + // This refinement strategy cuts elements by the user-specified number of adapt levels + // before the conformal decomposition. + + const FieldRef elem_marker = aux_meta.get_field(stk::topology::ELEMENT_RANK, marker_field_name, stk::mesh::StateNew); + const FieldRef refine_level_field = aux_meta.get_field(stk::topology::ELEMENT_RANK, "refine_level"); + const std::string transition_field_name = (mesh.mesh_meta_data().spatial_dimension() == 2) ? + "transition_element" : "transition_element_3"; + const FieldRef transition_element_field = aux_meta.get_field(stk::topology::ELEMENT_RANK, transition_field_name); + + const stk::mesh::Selector active_selector(cdfem_support.get_active_part()); + const stk::mesh::Selector locally_owned_selector(cdfem_support.get_locally_owned_part()); + const stk::mesh::Selector parent_or_child_selector = cdfem_support.get_child_part() | cdfem_support.get_parent_part(); + const int interface_min_refine_level = cdfem_support.get_interface_minimum_refinement_level(); + std::vector entities; + std::vector children; + + const NodeToCapturedDomainsMap nodesToCapturedDomains; + const std::vector edgeIntersections = interfaceGeometry.get_edge_intersection_points(mesh, nodesToCapturedDomains); + + FieldRef node_marker_field = aux_meta.get_field(stk::topology::NODE_RANK, marker_field_name, stk::mesh::StateNew); + mark_nearest_node_on_cut_edges(mesh, edgeIntersections, node_marker_field); + + std::vector min_edge_lengths(cdfem_support.get_interface_maximum_refinement_level()+1, std::numeric_limits::max()); + std::vector max_edge_lengths(cdfem_support.get_interface_maximum_refinement_level()+1, 0.0); + + const std::vector debugElementIds{}; + + stk::mesh::get_selected_entities( locally_owned_selector, mesh.buckets( stk::topology::ELEMENT_RANK ), entities ); + for( auto&& elem : entities ) + { + int & marker = *field_data(elem_marker, elem); + + bool has_crossing = false; + const stk::topology stk_topology = mesh.bucket(elem).topology(); + const unsigned num_nodes = stk_topology.num_nodes(); + const stk::mesh::Entity * const elem_nodes = mesh.begin(elem, stk::topology::NODE_RANK); + for(unsigned i=0; i < num_nodes; ++i) + { + const int node_marker = *field_data(node_marker_field, elem_nodes[i]); + if(node_marker) + { + has_crossing = true; + break; + } + } + + const int target_refine_level = has_crossing ? interface_min_refine_level : 0; + const int refine_level = *field_data(refine_level_field, elem); + const int transition_element = *field_data(transition_element_field, elem); + + if (!transition_element) + { + std::vector edge_nodes; + for (unsigned i = 0; i < stk_topology.num_edges(); ++i) + { + edge_nodes.resize(stk_topology.edge_topology(i).num_nodes()); + stk_topology.edge_nodes(elem_nodes, i, edge_nodes.data()); + + const Vector3d edge_node1_coords(field_data(coords_field, edge_nodes[0]), mesh.mesh_meta_data().spatial_dimension()); + const Vector3d edge_node2_coords(field_data(coords_field, edge_nodes[1]), mesh.mesh_meta_data().spatial_dimension()); + const double length = (edge_node2_coords-edge_node1_coords).length(); + + min_edge_lengths[refine_level] = std::min(min_edge_lengths[refine_level], length); + max_edge_lengths[refine_level] = std::max(max_edge_lengths[refine_level], length); + } + } + + if (!debugElementIds.empty() && std::find(debugElementIds.begin(), debugElementIds.end(), mesh.identifier(elem)) != debugElementIds.end()) + krinolog << "Considering refinement of element " << mesh.identifier(elem) << " " << has_crossing << " " << target_refine_level << " " << refine_level << " " << transition_element << stk::diag::dendl; + + if (refine_level < target_refine_level+transition_element) + { + marker = Refinement_Marker::REFINE; + } + else if (num_refinements < interface_min_refine_level && + refine_level > target_refine_level+transition_element) + { + // Stop coarsening after num_levels of refinement to avoid infinite looping + // from the interface position moving between elements because of snapping changes + // with refinement + marker = Refinement_Marker::COARSEN; + } + else + { + marker = Refinement_Marker::NOTHING; + } + } + + { + for (int i=0; i interface_min_refine_level) + { + resolve_fine_features(mesh, edgeIntersections, active_interface_ids, snapper, interface_max_refine_level, elem_marker, node_marker_field, refine_level_field, transition_element_field); + } +} + +void +refine_edges_with_unsnappable_nodes(const stk::mesh::BulkData& mesh, + const std::vector & edgeIntersections, + const CDFEM_Snapper & snapper, + const int interface_max_refine_level, + FieldRef elem_marker_field, + FieldRef refine_level_field, + FieldRef transition_element_field, + std::set unsnappable_nodes) +{ + for (auto && edgeIntersection : edgeIntersections) + { + const EdgeIntersection edge(edgeIntersection); + + if ((snapper.snap_lo(edge.crossingLocation) && unsnappable_nodes.find(edge.nodes[0]) != unsnappable_nodes.end()) || + (snapper.snap_hi(edge.crossingLocation) && unsnappable_nodes.find(edge.nodes[1]) != unsnappable_nodes.end())) + { + krinolog << "Refining unsnappable edge " << mesh.identifier(edge.nodes[0]) << " " << mesh.identifier(edge.nodes[1]) << " " << debug_output(mesh, edgeIntersection) << stk::diag::dendl; + std::vector edge_elems; + stk::mesh::get_entities_through_relations(mesh, {edge.nodes[0], edge.nodes[1]}, + stk::topology::ELEMENT_RANK, edge_elems); + for (auto && elem : edge_elems) + { + set_refine_if_not_reached_max_refine(elem, interface_max_refine_level, elem_marker_field, refine_level_field, transition_element_field); + } + } + } +} + +} diff --git a/packages/krino/krino/krino_lib/Akri_CDMesh_Refinement.hpp b/packages/krino/krino/krino_lib/Akri_CDMesh_Refinement.hpp new file mode 100644 index 000000000000..0f7725594ad9 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDMesh_Refinement.hpp @@ -0,0 +1,31 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_CDMESH_REFINEMENT_H_ +#define KRINO_INCLUDE_AKRI_CDMESH_REFINEMENT_H_ +#include +#include +#include +#include + +namespace krino { + +void +mark_interface_elements_for_adaptivity(const stk::mesh::BulkData& mesh, + const InterfaceGeometry & interfaceGeometry, + const std::vector & active_interface_ids, + const CDFEM_Snapper & snapper, + const AuxMetaData& aux_meta, + const CDFEM_Support & cdfem_support, + const FieldRef coords_field, + const std::string & marker_field_name, + const int num_refinements); + +} + +#endif /* KRINO_INCLUDE_AKRI_CDMESH_REFINEMENT_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_CDMesh_Utils.cpp b/packages/krino/krino/krino_lib/Akri_CDMesh_Utils.cpp new file mode 100644 index 000000000000..717cf7a0f255 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDMesh_Utils.cpp @@ -0,0 +1,143 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include + +namespace krino { + +bool +parts_are_compatible_for_snapping(const stk::mesh::BulkData & mesh, stk::mesh::Entity possible_snap_node, stk::mesh::Entity fixed_node) +{ + const stk::mesh::PartVector & possible_snap_node_parts = mesh.bucket(possible_snap_node).supersets(); + const stk::mesh::PartVector & fixed_node_parts = mesh.bucket(fixed_node).supersets(); + for (auto && possible_snap_node_part : possible_snap_node_parts) + { + if ((possible_snap_node_part->primary_entity_rank() == stk::topology::ELEMENT_RANK || + possible_snap_node_part->primary_entity_rank() == mesh.mesh_meta_data().side_rank()) && + !stk::mesh::is_auto_declared_part(*possible_snap_node_part) && + possible_snap_node_part->name().compare(0,7,"refine_") != 0 && + !stk::mesh::contain(fixed_node_parts, *possible_snap_node_part)) + { + return false; + } + } + return true; +} + +static stk::mesh::Part * get_nonconformal_part(const Phase_Support & phaseSupport, stk::mesh::Part * part) +{ + return const_cast(phaseSupport.find_nonconformal_part(*part)); +} + +static bool is_part_to_check(const Phase_Support & phaseSupport, const AuxMetaData & auxMeta, const stk::mesh::Part & part) +{ + const stk::mesh::Part & exposedBoundaryPart = auxMeta.exposed_boundary_part(); + return part.primary_entity_rank() != stk::topology::INVALID_RANK && + (&part == &exposedBoundaryPart || stk::io::is_part_io_part(part)) && + part.name().compare(0,7,"refine_") != 0 && + !phaseSupport.is_interface(&part); +} + +static stk::mesh::PartVector get_nonconformal_parts_to_check(const AuxMetaData & auxMeta, const Phase_Support & phaseSupport, const stk::mesh::PartVector & inputParts) +{ + stk::mesh::PartVector partsToCheck; + partsToCheck.reserve(inputParts.size()); + for (auto && part : inputParts) + if (is_part_to_check(phaseSupport, auxMeta, *part)) + partsToCheck.push_back(get_nonconformal_part(phaseSupport, part)); + stk::util::sort_and_unique(partsToCheck, stk::mesh::PartLess()); + return partsToCheck; +} + +bool +parts_are_compatible_for_snapping_when_ignoring_phase(const stk::mesh::BulkData & mesh, const AuxMetaData & auxMeta, const Phase_Support & phaseSupport, stk::mesh::Entity possibleSnapNode, stk::mesh::Entity fixedNode) +{ + const stk::mesh::PartVector & possibleSnapNodeParts = mesh.bucket(possibleSnapNode).supersets(); + const stk::mesh::PartVector nonconformalPartsToCheck = get_nonconformal_parts_to_check(auxMeta, phaseSupport, mesh.bucket(fixedNode).supersets()); + for (auto && possibleSnapNodePart : possibleSnapNodeParts) + { + if (is_part_to_check(phaseSupport, auxMeta, *possibleSnapNodePart)) + { + stk::mesh::Part * nonconformalPart = get_nonconformal_part(phaseSupport, possibleSnapNodePart); + if (!stk::mesh::contain(nonconformalPartsToCheck, *nonconformalPart)) + return false; + } + } + return true; +} + +bool phase_matches_interface(const CDFEM_Support & cdfemSupport, const PhaseTag & phase, const InterfaceID interface) +{ + if(cdfemSupport.num_ls_fields() > 1 && Phase_Support::has_one_levelset_per_phase()) + { + return (phase.contain(cdfemSupport.ls_field(interface.first_ls()).identifier, -1) && + phase.contain(cdfemSupport.ls_field(interface.second_ls()).identifier, -1)); + } + return (phase.contain(cdfemSupport.ls_field(interface.first_ls()).identifier, -1) && + phase.contain(cdfemSupport.ls_field(interface.first_ls()).identifier, +1)); +} + +bool determine_phase_from_parts(PhaseTag & phase, const stk::mesh::PartVector & parts, const Phase_Support & phaseSupport) +{ + ThrowAssert(phase.empty()); + bool has_conformal_ioparts = false; + + for (auto && part : parts) + { + if (part->primary_entity_rank() != stk::topology::ELEMENT_RANK || // limit ourselves to phase-specific volumes + !(stk::io::is_part_io_part(*part) || + phaseSupport.is_nonconformal(part))) + continue; + + const PhaseTag & iopart_phase = phaseSupport.get_iopart_phase(*part); + + if (!iopart_phase.empty()) + has_conformal_ioparts = true; + + phase.add(iopart_phase); + } + + return (has_conformal_ioparts); +} + +PhaseTag determine_phase_for_entity(const stk::mesh::BulkData & mesh, stk::mesh::Entity entity, const Phase_Support & phaseSupport) +{ + PhaseTag phase; + const stk::mesh::PartVector & parts = mesh.bucket(entity).supersets(); + determine_phase_from_parts(phase, parts, phaseSupport); + return phase; +} + +bool node_is_on_interface(const stk::mesh::BulkData & mesh, const CDFEM_Support & cdfemSupport, const Phase_Support & phaseSupport, stk::mesh::Entity node, const InterfaceID & interface) +{ + const PhaseTag nodePhase = determine_phase_for_entity(mesh, node, phaseSupport); + return phase_matches_interface(cdfemSupport, nodePhase, interface); +} + +bool nodes_are_on_any_interface(const stk::mesh::BulkData & mesh, const Phase_Support & phaseSupport, const stk::mesh::Bucket & nodeBucket) +{ + auto side_rank = mesh.mesh_meta_data().side_rank(); + for(auto && part : nodeBucket.supersets()) + if(part->primary_entity_rank() == side_rank && stk::io::is_part_io_part(*part) && phaseSupport.is_interface(part)) + return true; + return false; +} + +bool node_is_on_any_interface(const stk::mesh::BulkData & mesh, const Phase_Support & phaseSupport, const stk::mesh::Entity node) +{ + return nodes_are_on_any_interface(mesh, phaseSupport, mesh.bucket(node)); +} + +} + + diff --git a/packages/krino/krino/krino_lib/Akri_CDMesh_Utils.hpp b/packages/krino/krino/krino_lib/Akri_CDMesh_Utils.hpp new file mode 100644 index 000000000000..a3007222587b --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_CDMesh_Utils.hpp @@ -0,0 +1,34 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_CDMESH_UTILS_H_ +#define KRINO_INCLUDE_AKRI_CDMESH_UTILS_H_ +#include + +namespace krino { + +class CDFEM_Support; +class PhaseTag; +class InterfaceID; +class AuxMetaData; +class Phase_Support; + +bool parts_are_compatible_for_snapping(const stk::mesh::BulkData & mesh, stk::mesh::Entity possible_snap_node, stk::mesh::Entity fixed_node); +bool parts_are_compatible_for_snapping_when_ignoring_phase(const stk::mesh::BulkData & mesh, const AuxMetaData & auxMeta, const Phase_Support & phaseSupport, stk::mesh::Entity possible_snap_node, stk::mesh::Entity fixed_node); +bool phase_matches_interface(const CDFEM_Support & cdfemSupport, const PhaseTag & phase, const InterfaceID interface); +bool determine_phase_from_parts(PhaseTag & phase, const stk::mesh::PartVector & parts, const Phase_Support & phaseSupport); +PhaseTag determine_phase_for_entity(const stk::mesh::BulkData & mesh, stk::mesh::Entity entity, const Phase_Support & phaseSupport); +bool nodes_are_on_any_interface(const stk::mesh::BulkData & mesh, const Phase_Support & phaseSupport, const stk::mesh::Bucket & nodeBucket); +bool node_is_on_any_interface(const stk::mesh::BulkData & mesh, const Phase_Support & phaseSupport, const stk::mesh::Entity node); +bool node_is_on_interface(const stk::mesh::BulkData & mesh, const CDFEM_Support & cdfemSupport, const Phase_Support & phaseSupport, stk::mesh::Entity node, const InterfaceID & interface); + +} + + + +#endif /* KRINO_INCLUDE_AKRI_CDMESH_UTILS_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_Composite_Surface.cpp b/packages/krino/krino/krino_lib/Akri_Composite_Surface.cpp new file mode 100644 index 000000000000..941580cc6e98 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Composite_Surface.cpp @@ -0,0 +1,108 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include + +namespace krino{ + +Composite_Surface::Composite_Surface(const std::string & sn) + : SurfaceThatTakesAdvantageOfNarrowBandAndThereforeMightHaveWrongSign(), + my_name(sn), + my_composition_method(MINIMUM_SIGNED_DISTANCE) {} + +Composite_Surface::~Composite_Surface() {} + +void +Composite_Surface::prepare_to_compute(const double time, const BoundingBox & point_bbox, const double truncation_length) +{ /* %TRACE[ON]% */ Trace trace__("krino::Composite_Surface::prepare_to_compute(const double time, const BoundingBox & point_bbox, const double truncation_length)"); /* %TRACE% */ + + for ( auto&& surface : my_subsurfaces ) + { + surface->prepare_to_compute(time, point_bbox, truncation_length); + } + + if (truncation_length > 0.) + { + BoundingBox padded_point_bbox = point_bbox; + padded_point_bbox.pad(truncation_length); + + SurfaceAutoVec nearby_surfaces; + for ( auto&& surface : my_subsurfaces ) + { + // keep nearby or moving surfaces + if (FACETED_SURFACE == surface->type() || + nullptr != surface->get_transformation() || + point_bbox.intersects(surface->get_bounding_box())) + { + nearby_surfaces.emplace_back(std::move(surface)); + } + } + nearby_surfaces.swap(my_subsurfaces); + } + + int subsurfaces_might_produce_wrong_sign = false; + for ( auto&& surface : my_subsurfaces ) + { + if (surface->truncated_distance_may_have_wrong_sign()) + { + subsurfaces_might_produce_wrong_sign = true; + break; + } + } + int local_subsurfaces_might_produce_wrong_sign = subsurfaces_might_produce_wrong_sign; + stk::all_reduce_max(stk::EnvData::parallel_comm(), &local_subsurfaces_might_produce_wrong_sign, &subsurfaces_might_produce_wrong_sign, 1); + my_subsurfaces_might_produce_wrong_sign = subsurfaces_might_produce_wrong_sign; +} + +double +Composite_Surface::truncated_point_signed_distance(const Vector3d &x, const double narrow_band, const double far_field_value) const +{ /* %TRACE% */ /* %TRACE% */ + + if (my_composition_method == MINIMUM_SIGNED_DISTANCE) + { + ThrowRequireMsg(far_field_value >= narrow_band, "Composite surfaces have a specific requirement for far_field_value due to min/max operations."); + double dist = (narrow_band == 0.) ? std::numeric_limits::max() : far_field_value; + for ( auto&& surface : my_subsurfaces ) + { + dist = std::min(dist, surface->truncated_point_signed_distance(x, narrow_band, far_field_value)); + } + return dist; + } + else + { + ThrowRequire(my_composition_method == MAXIMUM_SIGNED_DISTANCE); + ThrowRequireMsg(far_field_value <= -narrow_band, "Composite surfaces have a specific requirement for far_field_value due to min/max operations."); + double dist = (narrow_band == 0.) ? -std::numeric_limits::max() : far_field_value; + for ( auto&& surface : my_subsurfaces ) + { + dist = std::max(dist, surface->truncated_point_signed_distance(x, narrow_band, far_field_value)); + } + return dist; + } +} + +BoundingBox +Composite_Surface::get_bounding_box() +{ + BoundingBox bbox; + for (auto && subsurf : my_subsurfaces) + { + bbox.accommodate(subsurf->get_bounding_box()); + } + return bbox; +} + + +} // namespace krino + + + diff --git a/packages/krino/krino/krino_lib/Akri_Composite_Surface.hpp b/packages/krino/krino/krino_lib/Akri_Composite_Surface.hpp new file mode 100644 index 000000000000..92dfe0ba31c0 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Composite_Surface.hpp @@ -0,0 +1,64 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Composite_Surface_h +#define Akri_Composite_Surface_h + +#include + +namespace krino { + +class SurfaceTree; +class Transformation; + +class Composite_Surface : public SurfaceThatTakesAdvantageOfNarrowBandAndThereforeMightHaveWrongSign { + +public: + Composite_Surface(const std::string & sn); + virtual ~Composite_Surface(); + + enum CompositionMethod{ MINIMUM_SIGNED_DISTANCE, MAXIMUM_SIGNED_DISTANCE }; + + virtual Surface_Type type() const override { return COMPOSITE_SURFACE; } + virtual void prepare_to_compute(const double time, const BoundingBox & point_bbox, const double truncation_length) override; + virtual size_t storage_size() const override { size_t tot_size = 0; for (auto && surf : my_subsurfaces) tot_size += surf->storage_size(); return tot_size; } + + // query/modify subsurfaces + void add( Surface * surf ) { my_subsurfaces.emplace_back(surf); } + void reserve(unsigned size_) { my_subsurfaces.reserve(size_); } + unsigned size() const { return my_subsurfaces.size(); } + Surface * operator()( const unsigned index ) const { return my_subsurfaces[index].get(); } + void clear() { my_subsurfaces.clear(); } + void swap(Composite_Surface & other) { my_subsurfaces.swap(other.my_subsurfaces); } + SurfaceAutoVec & get_surfaces() { return my_subsurfaces; } + const SurfaceAutoVec & get_surfaces() const { return my_subsurfaces; } + + virtual void set_transformation(Transformation * trans) override { Surface::set_transformation(trans); for (auto && subsurf : my_subsurfaces) subsurf->set_transformation(trans);} + void set_composition_method(const CompositionMethod composition_method) { my_composition_method = composition_method; } + virtual BoundingBox get_bounding_box() override; + virtual double truncated_point_signed_distance(const Vector3d &x, const double truncation_length, const double far_field_value) const override; + double point_signed_distance_with_narrow_band(const Vector3d &x, const double narrow_band) const + { + // Special to this class, this version of point_signed_distance automatically determines the far_field_value from the composition_method + return truncated_point_signed_distance(x, narrow_band, get_signed_narrow_band_size(narrow_band)); + } + + virtual bool truncated_distance_may_have_wrong_sign() const override { return my_subsurfaces_might_produce_wrong_sign; } + double get_signed_narrow_band_size(const double pos_narrow_band) const { return (my_composition_method == MINIMUM_SIGNED_DISTANCE) ? pos_narrow_band : -pos_narrow_band; } + +protected: + std::string my_name; + CompositionMethod my_composition_method; + SurfaceAutoVec my_subsurfaces; + bool my_subsurfaces_might_produce_wrong_sign = true; +}; + +} // namespace krino + + +#endif // Akri_Composite_Surface_h diff --git a/packages/krino/krino/krino_lib/Akri_Compute_Surface_Distance.cpp b/packages/krino/krino/krino_lib/Akri_Compute_Surface_Distance.cpp new file mode 100644 index 000000000000..0c5b01db83b9 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Compute_Surface_Distance.cpp @@ -0,0 +1,122 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino{ + +void +Compute_Surface_Distance::calculate( + const stk::mesh::BulkData & mesh, + const stk::diag::Timer &parent_timer, + const stk::mesh::Field& coordinates, + const stk::mesh::Field& distance, + const stk::mesh::Selector & surface_selector, + const double narrowBandSize, + const double farFieldValue) +{ + calculate(mesh, parent_timer, coordinates, distance, mesh.mesh_meta_data().universal_part(), surface_selector, narrowBandSize, farFieldValue); +} + +static BoundingBox compute_nodal_bounding_box(const stk::mesh::BulkData & mesh, + const stk::mesh::Field& coordinates, + const stk::mesh::Field& distance, + const stk::mesh::Selector & volume_selector) +{ + const int spatial_dimension = mesh.mesh_meta_data().spatial_dimension(); + + // The bounding box for that contains all the nodes on that proc + BoundingBox nodeBbox; + for ( auto && bucket : mesh.get_buckets(stk::topology::NODE_RANK, volume_selector & stk::mesh::selectField(distance)) ) + { + const size_t length = bucket->size(); + double *coord = stk::mesh::field_data(coordinates, *bucket); + + for (size_t n = 0; n < length; ++n) + nodeBbox.accommodate( Vector3d(coord+n*spatial_dimension, spatial_dimension) ); + } + + return nodeBbox; +} + +static void compute_distance_to_facets(const stk::mesh::BulkData & mesh, + const MeshSurface & facet_list, + const stk::mesh::Field& coordinates, + const stk::mesh::Field& distance, + const stk::mesh::Selector & volume_selector, + const double narrowBandSize, + const double userFarFieldValue) +{ + const int spatial_dimension = mesh.mesh_meta_data().spatial_dimension(); + const double farFieldValue = (userFarFieldValue > 0.0) ? userFarFieldValue : narrowBandSize; // Use farFieldValue, if specified, otherwise, use narrowBandSize + for ( auto && bucket : mesh.get_buckets(stk::topology::NODE_RANK, volume_selector & stk::mesh::selectField(distance)) ) + { + const size_t length = bucket->size(); + double *dist = stk::mesh::field_data(distance, *bucket); + double * coord = stk::mesh::field_data(coordinates, *bucket); + + for (size_t n = 0; n < length; ++n) + { + ThrowAssert(&(dist[n]) != NULL); + + const Vector3d xvec(coord+n*spatial_dimension, spatial_dimension); + dist[n] = facet_list.point_unsigned_distance(xvec, narrowBandSize, farFieldValue); + } + } +} + +void print_facet_info(const MeshSurface & facet_list, stk::ParallelMachine parallel) +{ + constexpr int vec_size = 3; + std::array local_sizes, global_min, global_max; + local_sizes[0] = facet_list.storage_size(); + local_sizes[1] = facet_list.size(); + local_sizes[2] = facet_list.size()+facet_list.nonlocal_size(); + + stk::all_reduce_min( parallel, local_sizes.data(), global_min.data(), vec_size ); + stk::all_reduce_max( parallel, local_sizes.data(), global_max.data(), vec_size ); + + krinolog << "Compute Surface Distance: "<< stk::diag::dendl; + krinolog << " Local facet count: min=" << global_min[1] << ", max=" << global_max[1] << stk::diag::dendl; + krinolog << " Total facet count: min=" << global_min[2] << ", max=" << global_max[2] << stk::diag::dendl; + krinolog << " Memory usage (mb): min=" << global_min[0]/(1024.0*1024.0) << ", max=" << global_max[0]/(1024.0*1024.0) << stk::diag::dendl; +} + + +void +Compute_Surface_Distance::calculate( + const stk::mesh::BulkData & mesh, + const stk::diag::Timer &parent_timer, + const stk::mesh::Field& coordinates, + const stk::mesh::Field& distance, + const stk::mesh::Selector & volume_selector, + const stk::mesh::Selector & surface_selector, + const double narrowBandSize, + const double farFieldValue) +{ /* %TRACE[ON]% */ Trace trace__("krino::Compute_Surface_Distance::compute_surface_distance(void)"); /* %TRACE% */ + + stk::diag::Timer timer( "Compute Surface Distance", parent_timer ); + stk::diag::TimeBlock timer_(timer); + + MeshSurface facet_list(mesh.mesh_meta_data(), coordinates, surface_selector, +1); + const BoundingBox nodeBbox = compute_nodal_bounding_box(mesh, coordinates, distance, volume_selector); + facet_list.prepare_to_compute(0.0, nodeBbox, narrowBandSize); // Setup including communication of facets that are within this processors narrow band + + print_facet_info(facet_list, mesh.parallel()); + + compute_distance_to_facets(mesh, facet_list, coordinates, distance, volume_selector, narrowBandSize, farFieldValue); +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_Compute_Surface_Distance.hpp b/packages/krino/krino/krino_lib/Akri_Compute_Surface_Distance.hpp new file mode 100644 index 000000000000..6795cd4002f5 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Compute_Surface_Distance.hpp @@ -0,0 +1,42 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Compute_Surface_Distance_h +#define Akri_Compute_Surface_Distance_h + +#include +#include +#include + +namespace krino { + +class Compute_Surface_Distance { +public: + static void calculate( + const stk::mesh::BulkData & mesh, + const stk::diag::Timer &parent_timer, + const stk::mesh::Field& coordinates, + const stk::mesh::Field& distance, + const stk::mesh::Selector & surface_selector, + const double narrowBandSize = 0.0, + const double farFieldValue = 0.0); + + static void calculate( + const stk::mesh::BulkData & mesh, + const stk::diag::Timer &parent_timer, + const stk::mesh::Field& coordinates, + const stk::mesh::Field& distance, + const stk::mesh::Selector & volume_selector, + const stk::mesh::Selector & surface_selector, + const double narrowBandSize = 0.0, + const double farFieldValue = 0.0); + }; + +} // namespace krino + +#endif // Akri_Compute_Surface_Distance_h diff --git a/packages/krino/krino/krino_lib/Akri_ContourElement.cpp b/packages/krino/krino/krino_lib/Akri_ContourElement.cpp new file mode 100644 index 000000000000..9ec313d5897e --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_ContourElement.cpp @@ -0,0 +1,556 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino{ + +ContourElement::ContourElement( const stk::mesh::BulkData & mesh, + stk::mesh::Entity in_mesh_obj, + const FieldRef coords_field, + const FieldRef dist_field, + const double iso_dist ) + : my_mesh( mesh ), + my_entity( in_mesh_obj ), + my_spatial_dim( mesh.mesh_meta_data().spatial_dimension() ), + my_coords_master_elem(MasterElementDeterminer::getMasterElement(mesh.bucket(in_mesh_obj), coords_field)), + my_dist_master_elem(MasterElementDeterminer::getMasterElement(mesh.bucket(in_mesh_obj), dist_field)), + my_coords_field( coords_field ), + my_dist_field( dist_field ), + my_length_scale(-1.0), + my_edge_linear_tolerance(-1.0), + my_edge_nonlinear_tolerance(-1.0) +{ /* %TRACE% */ /* %TRACE% */ + + // + // Gather obj's coordinates, distance, and velocity into ArrayContainer's. + // + + const int dim = my_spatial_dim; + + const int npe_coords = my_coords_master_elem.get_topology().num_nodes(); + const int npe_dist = my_dist_master_elem.get_topology().num_nodes(); + + my_coords.resize( dim, npe_coords ); + my_dist.resize( npe_dist ); + + const stk::mesh::Entity* elem_nodes = mesh.begin_nodes(my_entity); + + for ( int i = 0; i < npe_coords; ++i ) + { + double * var = field_data( my_coords_field, elem_nodes[i]); + for ( int d = 0; d < dim; d++ ) + { + my_coords(d,i) = var[d]; + } + } + + for ( int i = 0; i < npe_dist; ++i ) + { + double * var = field_data( my_dist_field, elem_nodes[i]); + my_dist(i) = *var - iso_dist; + } + + // the interpolate master element methods require the transpose?!? + my_coords_transpose.resize( npe_coords, dim ); + for (int i = 0; i < npe_coords; i++) + { + for (int d = 0; d < dim; d++) + { + my_coords_transpose(i,d) = my_coords(d,i); + } + } +} + +ContourElement::~ContourElement() {} + +std::ostream & +ContourElement::put( std::ostream& os ) const +{ /* %TRACE% */ /* %TRACE% */ + const int dim = my_spatial_dim; + + os << "Element description:" << std::endl + << " Coordinates topolology is " << coord_topology().name() + << ", Distance topology is " << dist_topology().name() << std::endl; + + int lnn = 0; + int coord_counter = 0; + int dist_counter = 0; + const unsigned num_nodes = my_mesh.num_nodes(my_entity); + const stk::mesh::Entity* nodes = my_mesh.begin_nodes(my_entity); + for (unsigned node_index=0; node_index(my_coords_field, node); + if ( NULL != var ) + { + Vector3d coords(Vector3d::ZERO); + for ( int d = 0; d < dim; d++ ) + coords[d] = my_coords(d,coord_counter); + os << ", coords = (" << coords[0] << "," + << coords[1] << "," + << coords[2] << ")"; + coord_counter++; + } + + var = field_data(my_dist_field, node); + if ( NULL != var ) + { + os << ", dist = " << my_dist(dist_counter++); + } + os << std::endl; + } + return os ; +} + +bool +ContourElement::have_interface_sides() const +{ + ThrowAssert(my_base_subelement); + return my_base_subelement->have_interface_sides(); +} + +double +ContourElement::distance( const Vector3d & p_coords ) const + { /* %TRACE% */ /* %TRACE% */ + double dist; + my_dist_master_elem.interpolate_point(my_spatial_dim, p_coords.data(), 1, my_dist.ptr(), &dist); + return dist; + } + +Vector3d +ContourElement::coordinates( const Vector3d & p_coords ) const + { /* %TRACE% */ /* %TRACE% */ + Vector3d coords(Vector3d::ZERO); + my_coords_master_elem.interpolate_point(my_spatial_dim, p_coords.data(), my_spatial_dim, my_coords.ptr(), coords.data()); + return coords; + } + +double +ContourElement::determinant( const Vector3d & p_coords ) const + { /* %TRACE% */ /* %TRACE% */ + double detJ, detJ_error; + + const int num_coord_dofs = coord_topology().num_nodes(); + + // temporary data + static sierra::ArrayContainer d_shapef_coords; + + // reserve enough space for results + d_shapef_coords.resize(my_spatial_dim, num_coord_dofs, 1); + + my_coords_master_elem.shape_fcn_deriv(1, p_coords.data(), d_shapef_coords.ptr()); + my_coords_master_elem.determinant( + my_spatial_dim, // Number of coordinate dimensions + 1, // Number of target points + num_coord_dofs, // Number of coord shape functions + d_shapef_coords.ptr(),// Mesh shape function derivatives + 1, // Number of elements + my_coords.ptr(), // Mesh coordinate values + &detJ, // Determinant of the transformation Jacobian for each element (output) + &detJ_error ); // Determinant error (output) + return detJ; + } + +Vector3d +ContourElement::continuous_distance_gradient( const Vector3d & p_coords ) const + { /* %TRACE% */ /* %TRACE% */ + // gather continuous distance gradient + sierra::ArrayContainer grad_distance; + const int npe = dist_topology().num_nodes(); + grad_distance.resize( my_spatial_dim, npe ); + stk::mesh::FieldBase* field_ptr = my_mesh.mesh_meta_data().get_field(stk::topology::NODE_RANK, "CONT_GRAD"); + ThrowRequireMsg(nullptr != field_ptr, "Field CONT_GRAD not found."); + const FieldRef contGradField(field_ptr); + const stk::mesh::Entity* elem_nodes = my_mesh.begin_nodes(my_entity); + for ( int i = 0; i < npe; ++i ) + { + double * var = field_data(contGradField, elem_nodes[i]); + for(int d = 0; d < my_spatial_dim; ++d) + grad_distance(d,i) = var[d]; + } + + Vector3d grad_dist(Vector3d::ZERO); + my_vel_master_elem->interpolate_point(my_spatial_dim, p_coords.data(), my_spatial_dim, grad_distance.ptr(), grad_dist.data()); + return grad_dist; + } + +Vector3d +ContourElement::distance_gradient( const Vector3d & p_coords ) const + { /* %TRACE% */ /* %TRACE% */ + const int dim = my_spatial_dim; + const int num_coord_dofs = my_coords.dimension(); + const int num_dist_dofs = my_dist.dimension(); + + // temporary data + static sierra::ArrayContainer d_shapef_coords; + static sierra::ArrayContainer d_shapef_dist; + static sierra::ArrayContainer grad_op; + + // reserve enough space for results + d_shapef_coords.resize(dim, num_coord_dofs); + d_shapef_dist.resize(dim, num_dist_dofs); + grad_op.resize(dim, num_dist_dofs); + Vector3d grad_dist( Vector3d::ZERO ); + double det_J; + + // pointers to array data + const double * intg_pt_loc_ptr = p_coords.data(); + double * grad_dist_ptr = grad_dist.data(); + + double gradop_error; + + my_coords_master_elem.shape_fcn_deriv(1, intg_pt_loc_ptr, d_shapef_coords.ptr()); + my_dist_master_elem.shape_fcn_deriv(1, intg_pt_loc_ptr, d_shapef_dist.ptr()); + + my_dist_master_elem.gradient_operator( + dim, // Number of coordinate dimensions + 1, // Number of target points + num_coord_dofs, // Number of coord shape functions + d_shapef_coords.ptr(),// Mesh shape function derivatives + num_dist_dofs, // Number of dof shape functions + d_shapef_dist.ptr(), // Dof shape function derivatives + 1, // Number of elements + my_coords.ptr(), // Mesh coordinate values + grad_op.ptr(), // Gradient operator values (output) + &det_J, // Determinant of the transformation Jacobian for each element (output) + &gradop_error ); // Gradop error (output) + + my_dist_master_elem.scalar_gradient( + 1, // Number of target points + 1, // Number of elements + grad_op.ptr(), // Gradient operator values + &det_J, // Determinant of the transformation Jacobian for each element + my_dist.ptr(), // nodal distance + grad_dist_ptr ); // Gradient of distance at integration pts (output) + return( grad_dist ); + } + +void +ContourElement::compute_distance_gradient( const sierra::Array & intg_pt_locations, + sierra::ArrayContainer & grad_dist ) const + { /* %TRACE% */ /* %TRACE% */ + const int dim = intg_pt_locations.dimension(); + const int num_intg_pts = intg_pt_locations.dimension(); + + const int num_coord_dofs = my_coords.dimension(); + const int num_dist_dofs = my_dist.dimension(); + + // temporary data + static sierra::ArrayContainer d_shapef_coords; + static sierra::ArrayContainer d_shapef_dist; + static sierra::ArrayContainer det_J; + static sierra::ArrayContainer grad_op; + + // reserve enough space for results + d_shapef_coords.resize(dim, num_coord_dofs, num_intg_pts); + d_shapef_dist.resize(dim, num_dist_dofs, num_intg_pts); + det_J.resize(num_intg_pts); + grad_op.resize(dim, num_dist_dofs, num_intg_pts); + grad_dist.resize(dim, num_intg_pts); + + double gradop_error; + + my_coords_master_elem.shape_fcn_deriv(num_intg_pts, intg_pt_locations.ptr(), d_shapef_coords.ptr()); + my_dist_master_elem.shape_fcn_deriv(num_intg_pts, intg_pt_locations.ptr(), d_shapef_dist.ptr()); + + my_dist_master_elem.gradient_operator( + dim, // Number of coordinate dimensions + num_intg_pts, // Number of target points + num_coord_dofs, // Number of coord shape functions + d_shapef_coords.ptr(), // Mesh shape function derivatives + num_dist_dofs, // Number of dof shape functions + d_shapef_dist.ptr(), // Dof shape function derivatives + 1, // Number of elements + my_coords.ptr(), // Mesh coordinate values + grad_op.ptr(), // Gradient operator values (output) + det_J.ptr(), // Determinant of the transformation Jacobian for each element (output) + &gradop_error ); // Gradop error (output) + + my_dist_master_elem.scalar_gradient( + num_intg_pts, // Number of target points + 1, // Number of elements + grad_op.ptr(), // Gradient operator values + det_J.ptr(), // Determinant of the transformation Jacobian for each element + my_dist.ptr(), // nodal distance + grad_dist.ptr() ); // Gradient of distance at integration pts (output) + } + +void +ContourElement::compute_subelement_decomposition(const double in_length_scale, const double in_edge_linear_tolerance, const double in_edge_nonlinear_tolerance) const + { /* %TRACE% */ /* %TRACE% */ + my_length_scale = in_length_scale; + my_edge_linear_tolerance = in_edge_linear_tolerance; + my_edge_nonlinear_tolerance = in_edge_nonlinear_tolerance; + + stk::topology topology = dist_topology(); + const int num_sides = topology.num_sides(); + std::vector side_ids(num_sides); + for (int i=0; i( my_dist_p_coords, side_ids, this ); + } + else if (stk::topology::HEXAHEDRON_27 == topology) + { + my_base_subelement = std::make_unique( my_dist_p_coords, side_ids, this ); + } + else if (stk::topology::TETRAHEDRON_4 == topology) + { + my_base_subelement = std::make_unique( my_dist_p_coords, side_ids, this ); + } + else if (stk::topology::TETRAHEDRON_10 == topology) + { + my_base_subelement = std::make_unique( my_dist_p_coords, side_ids, this ); + } + else if (stk::topology::WEDGE_6 == topology) + { + my_base_subelement = std::make_unique( my_dist_p_coords, side_ids, this ); + } + else if (stk::topology::QUADRILATERAL_4_2D == topology) + { + my_base_subelement = std::make_unique( my_dist_p_coords, side_ids, this ); + } + else if (stk::topology::QUADRILATERAL_9_2D == topology) + { + my_base_subelement = std::make_unique( my_dist_p_coords, side_ids, this ); + } + else if (stk::topology::TRIANGLE_3_2D == topology) + { + my_base_subelement = std::make_unique( my_dist_p_coords, side_ids, this ); + } + else if (stk::topology::TRIANGLE_6_2D == topology) + { + my_base_subelement = std::make_unique( my_dist_p_coords, side_ids, this ); + } + ThrowErrorMsgIf(!my_base_subelement, "Element with topology " << topology.name() << " not supported."); + + if ( krinolog.shouldPrint(LOG_SUBELEMENT) ) + { + if ( krinolog.shouldPrint(LOG_DEBUG) ) + { + dump_subelement_details(); + } + else + { + dump_subelement_structure(); + } + } + } + +double +ContourElement::volume() const + { /* %TRACE% */ /* %TRACE% */ + + sierra::Array intg_pt_locations; + sierra::Array intg_weights; + sierra::ArrayContainer determinants; + + std_intg_pts( intg_pt_locations, intg_weights, determinants ); + + double vol = 0.0; + + const int num_intg_pts = intg_pt_locations.dimension(); + + for ( int ip = 0; ip < num_intg_pts; ++ip ) + { + vol += intg_weights(ip) * determinants(ip); + } + return vol; + } + +double +ContourElement::average_edge_length() const +{ /* %TRACE% */ /* %TRACE% */ + const stk::topology Top = coord_topology(); + int num_edges = Top.num_edges(); + + double sum_edge_lengths = 0.0; + + for ( int edge = 0; edge < num_edges; edge++ ) + { + const unsigned * const lnn = get_edge_node_ordinals(Top, edge); + + double sqr_length = 0.0; + for ( int d = 0; d < my_spatial_dim; d++ ) sqr_length += (my_coords(d,lnn[0]) - my_coords(d,lnn[1])) * + (my_coords(d,lnn[0]) - my_coords(d,lnn[1])); + sum_edge_lengths += std::sqrt(sqr_length); + } + + return sum_edge_lengths/num_edges; +} + +double +ContourElement::elem_size() const + { /* %TRACE% */ /* %TRACE% */ + const double vol = volume(); + const double vol_root = 1.0 / my_spatial_dim; + const double h = pow( vol, vol_root ); + return h; + } + +void +ContourElement::dump_subelement_structure() const + { /* %TRACE% */ /* %TRACE% */ + ThrowErrorMsgIf(!my_base_subelement, "\ncompute_subelement_decomposition(...) must be called prior to calling dump_subelement_structure()."); + + krinolog << "***********************************************" << stk::diag::dendl; + krinolog << *this; + krinolog << "Subelement structure:" << stk::diag::dendl; + my_base_subelement->dump_structure(); + krinolog << "***********************************************" << stk::diag::dendl; + } + +void +ContourElement::dump_subelement_details() const + { /* %TRACE% */ /* %TRACE% */ + ThrowErrorMsgIf(!my_base_subelement, "\ncompute_subelement_decomposition(...) must be called prior to calling dump_subelement_details()."); + + krinolog << "***********************************************" << stk::diag::dendl; + krinolog << *this; + krinolog << "Subelement details:" << stk::diag::dendl; + my_base_subelement->dump_details(); + krinolog << "***********************************************" << stk::diag::dendl; + } + +int +ContourElement::build_subelement_facets( Faceted_Surface & facets ) +{ + ThrowErrorMsgIf(!my_base_subelement, "\ncompute_subelement_decomposition(...) must be called prior to calling build_subelement_facets(...)."); + return my_base_subelement->build_facets( facets ); +} + +int +ContourElement::gather_intg_pts( const int intg_pt_sign, + sierra::ArrayContainer & intg_pt_locations, + sierra::ArrayContainer & intg_weights, + sierra::ArrayContainer & determinants, + const bool map_to_real_coords ) +{ + ThrowErrorMsgIf(0 == intg_pt_sign && !map_to_real_coords, + "\nSubelement decomposition can currently only provide the surface integration with the overall determinant."); + ThrowErrorMsgIf(!my_base_subelement, + "\ncompute_subelement_decomposition(...) must be called prior to calling gather_intg_pts(...)."); + + const int num_intg_pts = my_base_subelement->num_intg_pts(intg_pt_sign); + + intg_pt_locations.resize(my_spatial_dim,num_intg_pts); + intg_weights.resize(num_intg_pts); + determinants.resize(num_intg_pts); + + my_base_subelement->gather_intg_pts( intg_pt_sign, + intg_pt_locations, + intg_weights, + determinants ); + + if ( 0 != intg_pt_sign && map_to_real_coords ) + { + // + // Include the determinant from element to real space + // + const int num_coord_dofs = coord_topology().num_nodes(); + + // temporary data + static sierra::ArrayContainer d_shapef_coords; + static sierra::ArrayContainer det_J; + + // reserve enough space for results + d_shapef_coords.resize(my_spatial_dim, num_coord_dofs, num_intg_pts); + det_J.resize(num_intg_pts); + + double det_J_error; + + my_coords_master_elem.shape_fcn_deriv(num_intg_pts, intg_pt_locations.ptr(), d_shapef_coords.ptr()); + my_coords_master_elem.determinant( + my_spatial_dim, // Number of coordinate dimensions + num_intg_pts, // Number of target points + num_coord_dofs, // Number of coord shape functions + d_shapef_coords.ptr(),// Mesh shape function derivatives + 1, // Number of elements + my_coords.ptr(), // Mesh coordinate values + det_J.ptr(), // Determinant of the transformation Jacobian for each element (output) + &det_J_error ); // Determinant error (output) + for ( int i=0; i & intg_pt_locations, + sierra::Array & intg_weights, + sierra::ArrayContainer & det_J, + const MasterElement & me ) const + { + const int num_intg_pts = me.num_intg_pts(); + const double * intg_pt_loc_ptr = me.intg_pt_locations(); + const double * intg_wt_ptr = me.intg_weights(); + + intg_pt_locations.set(intg_pt_loc_ptr,my_spatial_dim,num_intg_pts); + intg_weights.set(intg_wt_ptr,num_intg_pts); + det_J.resize( num_intg_pts ); + + double det_J_error; + + if ( me.get_topology() == my_coords_master_elem.get_topology() ) + { + my_coords_master_elem.determinant( my_spatial_dim, 1, my_coords.ptr(), det_J.ptr(), &det_J_error ); + } + else + { + const int num_coord_dofs = coord_topology().num_nodes(); + sierra::ArrayContainer d_shapef_coords(my_spatial_dim, num_coord_dofs, num_intg_pts); + my_coords_master_elem.shape_fcn_deriv(num_intg_pts, intg_pt_locations.ptr(), d_shapef_coords.ptr()); + my_coords_master_elem.determinant( + my_spatial_dim, // Number of coordinate dimensions + num_intg_pts, // Number of target points + num_coord_dofs, // Number of coord shape functions + d_shapef_coords.ptr(),// Mesh shape function derivatives + 1, // Number of elements + my_coords.ptr(), // Mesh coordinate values + det_J.ptr(), // Determinant of the transformation Jacobian for each element (output) + &det_J_error ); // Determinant error (output) + } + + return( num_intg_pts ); + } + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_ContourElement.hpp b/packages/krino/krino/krino_lib/Akri_ContourElement.hpp new file mode 100644 index 000000000000..f7ee798316ca --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_ContourElement.hpp @@ -0,0 +1,130 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_ContourElement_h +#define Akri_ContourElement_h + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace krino { + +class ContourSubElement; + +class ContourElement { +public: + + ContourElement( const stk::mesh::BulkData & mesh, + stk::mesh::Entity mesh_obj, + const FieldRef coords_field, + const FieldRef dist_field, + const double iso_dist = 0.0 ); + ~ContourElement(); // Definition must in implementation file because SubElement is incomplete + + std::ostream & put( std::ostream& os ) const; + friend std::ostream & operator << ( std::ostream &os , const ContourElement &s ) { + return s.put(os); + } + + const MasterElement & coord_master_elem() const { return my_coords_master_elem; } + const MasterElement & dist_master_elem() const { return my_dist_master_elem; } + const MasterElement & vel_master_elem() const { return *my_vel_master_elem; } + + const stk::topology coord_topology() const { return my_coords_master_elem.get_topology(); } + const stk::topology dist_topology() const { return my_dist_master_elem.get_topology(); } + const stk::topology vel_topology() const { return my_vel_master_elem->get_topology(); } + + bool dist_is_linear() const { return dist_topology() == stk::topology::TRIANGLE_3_2D || dist_topology() == stk::topology::TETRAHEDRON_4; } + + stk::mesh::Entity entity() const { return my_entity; } + stk::mesh::EntityId elem_id() const { return my_mesh.identifier(my_entity); } + bool have_interface_sides() const; + + Vector3d coordinates( const Vector3d & p_coords ) const; + double distance( const Vector3d & p_coords ) const; + const PointVec & nodal_parametric_coordinates() const { return my_dist_p_coords; } + double determinant( const Vector3d & p_coords ) const; + + Vector3d continuous_distance_gradient( const Vector3d & p_coords ) const; + Vector3d distance_gradient( const Vector3d & p_coords ) const; + void compute_distance_gradient( const sierra::Array & intg_pt_locations, + sierra::ArrayContainer & grad_dist ) const; + + void dump_subelement_structure( void ) const; + void dump_subelement_details( void ) const; + const ContourSubElement * get_base_subelement() const { return my_base_subelement.get(); } + + int build_subelement_facets( Faceted_Surface & facets ); + + int gather_intg_pts( const int intg_pt_sign, + sierra::ArrayContainer & intg_pt_locations, + sierra::ArrayContainer & intg_weights, + sierra::ArrayContainer & determinants, + const bool map_to_real_coords ); + + int std_intg_pts( sierra::Array & intg_pt_locations, + sierra::Array & intg_weights, + sierra::ArrayContainer & determinants, + const MasterElement & me ) const; + int std_intg_pts( sierra::Array & intg_pt_locations, + sierra::Array & intg_weights, + sierra::ArrayContainer & determinants ) const { return std_intg_pts(intg_pt_locations, intg_weights, determinants, my_coords_master_elem); } + + int spatial_dim() const { return my_spatial_dim; } + double length_scale() const { return my_length_scale; } + double edge_linear_tolerance() const { return my_edge_linear_tolerance; } + double edge_nonlinear_tolerance() const { return my_edge_nonlinear_tolerance; } + + double volume() const; + double elem_size() const; + double average_edge_length() const; + + void compute_subelement_decomposition(const double length_scale, const double edge_linear_tolerance = 1.e-4, const double edge_nonlinear_tolerance = 1.0e-2) const; + +private: + const stk::mesh::BulkData & my_mesh; + stk::mesh::Entity my_entity; + const int my_spatial_dim; + + const MasterElement & my_coords_master_elem; + const MasterElement & my_dist_master_elem; + const MasterElement * my_vel_master_elem; + + const FieldRef my_coords_field; + const FieldRef my_dist_field; + const FieldRef my_vel_field; + + sierra::ArrayContainer my_coords; + sierra::ArrayContainer my_coords_transpose; + sierra::ArrayContainer my_dist; + sierra::ArrayContainer my_vel; + sierra::ArrayContainer my_vel_transpose; + + mutable PointVec my_dist_p_coords; + mutable double my_length_scale; + mutable double my_edge_linear_tolerance; + mutable double my_edge_nonlinear_tolerance; + mutable std::unique_ptr my_base_subelement; + + //: Default constructor not allowed + ContourElement(); + +}; + +} // namespace krino + +#endif // Akri_ContourElement_h diff --git a/packages/krino/krino/krino_lib/Akri_ContourSubElement.cpp b/packages/krino/krino/krino_lib/Akri_ContourSubElement.cpp new file mode 100644 index 000000000000..2ef3e52bf7c1 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_ContourSubElement.cpp @@ -0,0 +1,2565 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +namespace krino{ + +bool ContourSubElement::is_more(const Vector3d & x, const Vector3d & y) +{ + // note that in the case of x==y, this will return false + if (utility::is_more(x[0],y[0]) || (!utility::is_less(x[0],y[0]) && + (utility::is_more(x[1],y[1]) || (!utility::is_less(x[1],y[1]) && + (utility::is_more(x[2],y[2])))))) + { + return true; + } + return false; +} + +ContourSubElement::ContourSubElement( const stk::topology topo, + const PointVec & coords, + const std::vector & side_ids, + const ContourElement *in_owner, + const int in_subelement_depth, + const int subelement_sign ) + : my_master_element( MasterElementDeterminer::getMasterElement(topo) ), + my_side_master_element( MasterElementDeterminer::getMasterElement(topo.side_topology()) ), + my_coords( coords ), + my_side_ids( side_ids ), + my_owner( in_owner ), + my_subelement_depth( in_subelement_depth ), + my_sign( subelement_sign ) +{ /* %TRACE% */ /* %TRACE% */ + my_num_nodes = my_coords.size(); + ThrowAssert( (unsigned)my_num_nodes == topology().num_nodes() ); + my_num_sides = my_side_ids.size(); + ThrowAssert( (unsigned)my_num_sides == topology().num_sides() ); + + // compute distance at each node + my_dist.clear(); + my_dist.reserve(my_num_nodes); + for ( int i = 0; i < my_num_nodes; i++ ) + { + double nodal_dist = my_owner->distance( my_coords[i] ); + my_dist.push_back(nodal_dist); + } +} + +int +ContourSubElement::build_facets( Faceted_Surface & facets ) +{ /* %TRACE% */ /* %TRACE% */ + int start_size = facets.size(); + + if ( !my_subelements.empty() ) + { + for ( auto && subelem : my_subelements ) + { + subelem->build_facets( facets ); + } + } + else + { + for ( int iside = 0; iside < my_num_sides; iside++ ) + { + // side_ids == -2 indicates that sides is on interface + if ( my_side_ids[iside] == -2 ) + { + const int subelem_num_facets = side_facets( facets, iside ); + if ( krinolog.shouldPrint(LOG_SUBELEMENT) ) + { + krinolog << "Subelement with area = " << side_relative_area(iside) + << " added " << subelem_num_facets << " facets." << std::endl; + } + } + } + } + + int added_facets = facets.size() - start_size; + return added_facets; +} + +void +ContourSubElement::dump_structure() const +{ /* %TRACE% */ /* %TRACE% */ + + if ( !my_subelements.empty() ) + { + for ( int i = 0; i < my_subelement_depth; i++ ) + { + krinolog << " "; + } + krinolog << "my subelement type = " << topology().name() << ", my # of subelements = " << my_subelements.size() << std::endl; + for ( auto && subelem : my_subelements ) + { + subelem->dump_structure(); + } + } + else + { + for ( int i = 0; i < my_subelement_depth; i++ ) + { + krinolog << " "; + } + krinolog << "my subelement type = " << topology().name() << std::endl; + } +} + +void +ContourSubElement::dump_details() const +{ /* %TRACE% */ /* %TRACE% */ + if ( !my_subelements.empty() ) + { + krinolog << "my subelement type = " << topology().name() << ", my # of subelements = " << my_subelements.size() << std::endl; + for ( auto && subelem : my_subelements ) + { + subelem->dump_details(); + } + } + else + { + krinolog << "--------------------begin subelement definition--------------------" << std::endl; + krinolog << *this; + krinolog << "---------------------end subelement definition---------------------" << std::endl; + } +} + +int +ContourSubElement::side_facets( Faceted_Surface & facets, int side ) const +{ /* %TRACE% */ /* %TRACE% */ + const std::string & owner_type = my_owner->dist_topology().name(); + const std::string & sub_type = topology().name(); + ThrowRuntimeError("Subelement decomposition for subelement of type '" << sub_type + << "' which was generated from owning element of type '" << owner_type + << "' is missing the capability to generate conformal facets."); + return -1; +} + +std::ostream & +ContourSubElement::put( std::ostream& os ) const +{ /* %TRACE% */ /* %TRACE% */ + os << "Subelement description:" << std::endl; + os << " type = " << topology().name() + << ", relative volume = " << relative_volume() + << ", parametric_quality = " << parametric_quality() + << ", physical_quality = " << physical_quality() << std::endl; + for ( int i = 0; i < my_num_nodes; i++ ) + { + Vector3d x = my_owner->coordinates( my_coords[i] ); + os << " coords[" << i << "] = (" + << my_coords[i][0] << "," + << my_coords[i][1] << "," + << my_coords[i][2] << ")" + << ", x = (" + << x[0] << "," + << x[1] << "," + << x[2] << ")" + << ", dist = " << my_dist[i] + << ", sign = " << -1 + 2*LevelSet::sign_change(my_dist[i],-1.) << std::endl; + } + for ( int i = 0; i < my_num_sides; i++ ) + { + os << " side_ids[" << i << "] = " << my_side_ids[i] + << ", side_relative_area[" << i << "] = " << side_relative_area(i) + << ", side_quality[" << i << "] = " << side_quality(i) << std::endl; + } + // matlab visualization + os << " matlabvertices = ["; + for ( int i = 0; i < my_num_nodes; i++ ) + { + os << my_coords[i][0] << " " + << my_coords[i][1] << " " + << my_coords[i][2] << "; "; + } + os << "];" << std::endl; + os << " physical space matlabvertices = ["; + for ( int i = 0; i < my_num_nodes; i++ ) + { + Vector3d x = my_owner->coordinates( my_coords[i] ); + os << x[0] << " " + << x[1] << " " + << x[2] << "; "; + } + os << "];" << std::endl; + + return os ; +} + +double +ContourSubElement::relative_volume() const +{ /* %TRACE% */ /* %TRACE% */ + // This is a relative volume compared to the owner volume. + // Actually this is a relative volume if the "parametric" volume of the element is unity. + // Otherwise, it is off by a factor. + const int nelem = 1; + const int dim = spatial_dim(); + const int nint = my_master_element.num_intg_pts(); + std::vector coords(my_num_nodes * dim, 0.); + std::vector det_J(nint, 0.); + double error = 0.; + + // integration weights + const double * intg_weights = my_master_element.intg_weights(); + + // load coords + int count = 0; + for ( int i = 0; i < my_num_nodes; i++ ) + { + for ( int j = 0; j < dim; j++ ) + { + coords[count++] = my_coords[i][j]; + } + } + + // determinant at integration points + my_master_element.determinant( dim, nelem, coords.data(), det_J.data(), &error ); + + double elem_volume = 0.; + for ( int ip = 0; ip < nint; ip++ ) + { + elem_volume += det_J[ip] * intg_weights[ip]; + } + + return elem_volume; +} + +double +ContourSubElement::side_relative_area( const int side ) const +{ /* %TRACE% */ /* %TRACE% */ + // This is a relative volume compared to the owner volume. + // Actually this is a relative volume if the "parametric" volume of the element is unity. + // Otherwise, it is off by a factor. + const int nelem = 1; + const int dim = spatial_dim(); + const int nint = my_side_master_element.num_intg_pts(); + const stk::topology Top = topology(); + const stk::topology sideTop = Top.side_topology(side); + const int side_num_nodes = sideTop.num_nodes(); + const unsigned * const lnn = get_side_node_ordinals(Top, side); + + std::vector coords(side_num_nodes * dim, 0.); + std::vector det_J(nint, 0.); + double error = 0.; + + // integration weights + const double * intg_weights = my_side_master_element.intg_weights(); + + // load coords + int count = 0; + for ( int i = 0; i < side_num_nodes; i++ ) + { + for ( int j = 0; j < dim; j++ ) + { + coords[count++] = my_coords[lnn[i]][j]; + } + } + + // determinant at integration points + my_side_master_element.determinant( dim, nelem, coords.data(), det_J.data(), &error ); + + double elem_side_area = 0.; + for ( int ip = 0; ip < nint; ip++ ) + { + elem_side_area += det_J[ip] * intg_weights[ip]; + } + + return elem_side_area; +} + +bool +ContourSubElement::have_interface_sides() const +{ + if ( !my_subelements.empty()) + { + for ( auto && subelem : my_subelements ) + { + if (subelem->have_interface_sides()) + { + return true; + } + } + } + else + { + for ( int iside = 0; iside < my_num_sides; iside++ ) + { + // side_ids == -2 indicates that sides is on interface + if ( my_side_ids[iside] == -2 ) + { + return true; + } + } + } + return false; +} + +ContourSubElement::~ContourSubElement() +{ /* %TRACE% */ /* %TRACE% */ + for ( auto && subelem : my_subelements ) + delete subelem; +} + +int +ContourSubElement::num_intg_pts(const int intg_pt_sign) +{ /* %TRACE% */ /* %TRACE% */ + int num_pts = 0; + + if ( !my_subelements.empty() ) + { + for ( auto && subelem : my_subelements ) + { + num_pts += subelem->num_intg_pts(intg_pt_sign); + } + } + else + { + if ( 0 == intg_pt_sign ) // interface points + { + for ( int iside = 0; iside < my_num_sides; iside++ ) + { + // side_ids == -2 indicates that side is on interface + if ( my_side_ids[iside] == -2 ) + { + num_pts += my_side_master_element.num_intg_pts(); + } + } + } + else // volume points + { + if ( intg_pt_sign != my_sign ) + { + return(0); + } + else + { + return ( my_master_element.num_intg_pts() ); + } + } + } + + return num_pts; +} + +int +ContourSubElement::gather_intg_pts( const int intg_pt_sign, + sierra::ArrayContainer & intg_pt_locations, + sierra::ArrayContainer & intg_weights, + sierra::ArrayContainer & determinant, + int index ) +{ /* %TRACE% */ /* %TRACE% */ + if ( !my_subelements.empty() ) + { + for ( auto && subelem : my_subelements ) + { + index = subelem->gather_intg_pts( intg_pt_sign, + intg_pt_locations, + intg_weights, + determinant, + index ); + } + } + else + { + if ( 0 == intg_pt_sign ) // interface points + { + for ( int iside = 0; iside < my_num_sides; iside++ ) + { + // side_ids == -2 indicates that side is on interface + if ( my_side_ids[iside] == -2 ) + { + const int nelem = 1; + const int dim = spatial_dim(); + const stk::topology Top = topology(); + const stk::topology sideTop = Top.side_topology(iside); + const int side_num_intg_pts = my_side_master_element.num_intg_pts(); + const int side_num_nodes = sideTop.num_nodes(); + const unsigned * const lnn = get_side_node_ordinals(Top, iside ); + double error; + + // temp arrays + sierra::ArrayContainer coords(dim,side_num_nodes); + sierra::ArrayContainer det_J(side_num_intg_pts); + + // load coords + for ( int i = 0; i < side_num_nodes; i++ ) + { + Vector3d coordinates = my_owner->coordinates( my_coords[lnn[i]] ); + for ( int d = 0; d < dim; d++ ) coords(d,i) = coordinates[d]; + } + + // determinant at integration points + my_side_master_element.determinant( dim, nelem, coords.ptr(), det_J.ptr(), &error ); + + // integration weights + const double * intg_wts_ptr = my_side_master_element.intg_weights(); + const sierra::Array intg_wts(intg_wts_ptr,side_num_intg_pts); + + // basis fns at integration point locations + const double * bf_ptr = my_side_master_element.shape_fcn(); + const sierra::Array bf(bf_ptr,side_num_nodes,side_num_intg_pts); + + for ( int ip = 0; ip < side_num_intg_pts; ++ip) + { + determinant(index) = det_J(ip); + intg_weights(index) = intg_wts(ip); + + Vector3d xi(Vector3d::ZERO); + for ( int i = 0; i < side_num_nodes; i++ ) + xi += bf(i,ip) * my_coords[lnn[i]]; + + for ( int d = 0; d < dim; ++d ) + intg_pt_locations(d,index) = xi[d]; + + index++; + } + } + } + } + else // volume points + { + ThrowAssert(-1 == intg_pt_sign || 1 == intg_pt_sign); + + if ( intg_pt_sign != my_sign ) + { + return(index); + } + + const int nelem = 1; + const int dim = spatial_dim(); + const int vol_num_intg_pts = my_master_element.num_intg_pts(); + + // temp arrays + sierra::ArrayContainer coords(dim,my_num_nodes); + sierra::ArrayContainer det_J(vol_num_intg_pts); + + double error; + + // integration weights + const double * intg_wts_ptr = my_master_element.intg_weights(); + const sierra::Array intg_wts(intg_wts_ptr,vol_num_intg_pts); + + // load coords + for ( int i = 0; i < my_num_nodes; i++ ) + { + for ( int d = 0; d < dim; d++ ) coords(d,i) = my_coords[i][d]; + } + + // determinant at integration points + my_master_element.determinant( dim, nelem, coords.ptr(), det_J.ptr(), &error ); + + // basis fns at integration point locations + const double * bf_ptr = my_master_element.shape_fcn(); + const sierra::Array bf(bf_ptr,my_num_nodes,vol_num_intg_pts); + + for ( int ip = 0; ip < vol_num_intg_pts; ++ip) + { + determinant(index) = det_J(ip); + intg_weights(index) = intg_wts(ip); + + Vector3d xi(Vector3d::ZERO); + for ( int i = 0; i < my_num_nodes; i++ ) + xi += bf(i,ip) * my_coords[i]; + + for ( int d = 0; d < dim; d++ ) + intg_pt_locations(d,index) = xi[d]; + + index++; + } + } + } + return(index); +} + +double +ContourSubElement::parametric_quality() const +{ /* %TRACE% */ /* %TRACE% */ + const int nelem = 1; + const int nint = my_master_element.num_intg_pts(); + const int dim = spatial_dim(); + std::vector coords(my_num_nodes * dim, 0.); + std::vector det_J(nint, 0.); + double error = 0.; + double sub_quality = 0.; + + // load coords + int count = 0; + for ( int i = 0; i < my_num_nodes; i++ ) + { + for ( int j = 0; j < dim; j++ ) + { + coords[count++] = my_coords[i][j]; + } + } + + // determinant at integration points + my_master_element.determinant( dim, nelem, coords.data(), det_J.data(), &error ); + + double min_det_J = 0., sum_det_J = 0.; + for ( int ip = 0; ip < nint; ip++ ) + { + if ( ip == 0 || det_J[ip] < min_det_J ) + min_det_J = det_J[ip]; + sum_det_J += std::fabs( det_J[ip] ); + } + + if ( sum_det_J < std::pow(std::numeric_limits::epsilon(),1./dim) ) + sub_quality = 1.; // element too small to consider + else + sub_quality = min_det_J * nint / sum_det_J; + + return sub_quality; +} + +double +ContourSubElement::physical_quality() const +{ /* %TRACE% */ /* %TRACE% */ + const int nelem = 1; + const int nint = my_master_element.num_intg_pts(); + const int dim = spatial_dim(); + std::vector coords(my_num_nodes * dim, 0.); + std::vector det_J(nint, 0.); + double error = 0.; + double sub_quality = 0.; + + // load coords + int count = 0; + for ( int i = 0; i < my_num_nodes; i++ ) + { + const Vector3d phys_coords = my_owner->coordinates(my_coords[i]); + for ( int j = 0; j < dim; j++ ) + { + coords[count++] = phys_coords[j]; + } + } + + // determinant at integration points + my_master_element.determinant( dim, nelem, coords.data(), det_J.data(), &error ); + + double min_det_J = 0., sum_det_J = 0.; + for ( int ip = 0; ip < nint; ip++ ) + { + if ( ip == 0 || det_J[ip] < min_det_J ) + min_det_J = det_J[ip]; + sum_det_J += std::fabs( det_J[ip] ); + } + + if ( sum_det_J < std::pow(std::numeric_limits::epsilon(),1./dim) * std::pow(my_owner->length_scale(),1.*dim) ) + sub_quality = 1.; // element too small to consider + else + sub_quality = min_det_J * nint / sum_det_J; + + return sub_quality; +} + +double +ContourSubElement::side_quality(const int side) const +{ /* %TRACE% */ /* %TRACE% */ + const int nelem = 1; + const int nint = my_side_master_element.num_intg_pts(); + const stk::topology Top = topology(); + const stk::topology sideTop = Top.side_topology(side); + const int side_num_nodes = sideTop.num_nodes(); + const unsigned * const lnn = get_side_node_ordinals(Top, side); + const int dim = spatial_dim(); + std::vector coords(side_num_nodes * dim, 0.); + std::vector det_J(nint, 0.); + double error = 0.; + double quality = 0.; + + // load coords on side + int count = 0; + for ( int i = 0; i < side_num_nodes; i++ ) + { + for ( int j = 0; j < dim; j++ ) + { + coords[count++] = my_coords[lnn[i]][j]; + } + } + + // determinant at integration points + my_side_master_element.determinant( dim, nelem, coords.data(), det_J.data(), &error ); + + double min_det_J = 0., sum_det_J = 0.; + for ( int ip = 0; ip < nint; ip++ ) + { + if ( ip == 0 || det_J[ip] < min_det_J ) + min_det_J = det_J[ip]; + sum_det_J += std::fabs( det_J[ip] ); + } + + if ( sum_det_J < std::pow(std::numeric_limits::epsilon(),1./dim) ) + quality = 1.; // element too small to consider + else + quality = min_det_J * nint / sum_det_J; + + return quality; +} + +double +ContourSubElement::find_quadratic_crossing( double d0, + double d1, + double d2 ) +{ /* %TRACE% */ /* %TRACE% */ + const double epsilon = std::numeric_limits::epsilon()*std::sqrt(d0*d0 + d1*d1 + d2*d2); + if ( std::fabs(d0) < epsilon ) return 0.0; + if ( std::fabs(d1) < epsilon ) return 1.0; + if ( std::fabs(d2) < epsilon ) return 0.5; + + ThrowAssert(d0*d1 < 0.0 && (d0*d2 < 0.0 || d1*d2 < 0.0)); // Insist on one and only one crossing + + const double a = 2.0*(d0 - 2.0*d2 + d1); + const double b = -3.0*d0 - d1 + 4.0*d2; + const double c = d0; + const int sign_b = ( b < 0.0 ) ? -1 : 1; + const double q = -0.5*(b + sign_b*std::sqrt(b*b-4.0*a*c)); + + const int sign_a = ( a < 0.0 ) ? -1 : 1; + + if (q*sign_a > 0.0 && q*sign_a < a*sign_a) + { + ThrowAssert(!(c*(( q < 0.0 ) ? -1 : 1) > 0.0 && c*(( q < 0.0 ) ? -1 : 1) < q*(( q < 0.0 ) ? -1 : 1))); // Insist on only one crossing + return (q/a); + } + else + { + ThrowAssert(c*(( q < 0.0 ) ? -1 : 1) > 0.0 && c*(( q < 0.0 ) ? -1 : 1) < q*(( q < 0.0 ) ? -1 : 1)); + return (c/q); + } +} + +ContourSubElement_Quad_4::ContourSubElement_Quad_4( + const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner ) + : ContourSubElement( stk::topology::QUADRILATERAL_4_2D, + coords, + side_ids, + in_owner, + 0, /* in_subelement_depth*/ + 0 /* subelement_sign=0 for now, correct this below if this element is entirely on one side */ ) +{ /* %TRACE% */ /* %TRACE% */ + + // Determine if we will continue to look for crossing within this element. + // This test should be conservative, proceeding to look for crossings if there + // is even a remote chance of a crossing (To avoid cracks in the surface). + + // find extrema + double max_dist = -std::numeric_limits::max(); + double min_dist = std::numeric_limits::max(); + for ( int n = 0; n < my_num_nodes; n++ ) + { + if (my_dist[n] < min_dist) min_dist = my_dist[n]; + if (my_dist[n] > max_dist) max_dist = my_dist[n]; + } + + const double variation = max_dist - min_dist; + + const bool all_hi = (min_dist - variation) > 0.0; + const bool all_lo = (max_dist + variation) < 0.0; + + if (all_hi || all_lo) + { + // correct the sign since we lie entirely on one side of the interface + my_sign = LevelSet::sign(my_dist[0]); + return; + } + + non_conformal_decomposition(); +} + +int +ContourSubElement_Quad_4::non_conformal_decomposition() +{ /* %TRACE% */ /* %TRACE% */ + int success = true; // optimism + + // create 4, 3-noded, adaptive triangles + my_subelements.reserve(4); + ContourSubElement *sub = NULL; + PointVec sub_coords(3,Vector3d::ZERO); + std::vector sub_ids(3); + + Vector3d center = 0.25*(my_coords[0]+my_coords[1]+my_coords[2]+my_coords[3]); + + // triangle #1 + sub_coords[0] = my_coords[0]; + sub_coords[1] = my_coords[1]; + sub_coords[2] = center; + sub_ids[0] = my_side_ids[0]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tri_3( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // triangle #2 + sub_coords[0] = my_coords[1]; + sub_coords[1] = my_coords[2]; + sub_coords[2] = center; + sub_ids[0] = my_side_ids[1]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tri_3( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // triangle #3 + sub_coords[0] = my_coords[2]; + sub_coords[1] = my_coords[3]; + sub_ids[0] = my_side_ids[2]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tri_3( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // triangle #4 + sub_coords[0] = my_coords[3]; + sub_coords[1] = my_coords[0]; + sub_coords[2] = center; + sub_ids[0] = my_side_ids[3]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tri_3( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + return success; +} + +ContourSubElement_Quad_9::ContourSubElement_Quad_9( + const PointVec & coords, + const std::vector & side_ids, + const ContourElement *in_owner ) + : ContourSubElement( stk::topology::QUADRILATERAL_9_2D, + coords, + side_ids, + in_owner, + 0, /* in_subelement_depth*/ + 0 /* subelement_sign=0 for now, correct this below if this element is entirely on one side */ ) +{ /* %TRACE% */ /* %TRACE% */ + + // Determine if we will continue to look for crossing within this element. + // This test should be conservative, proceeding to look for crossings if there + // is even a remote chance of a crossing (To avoid cracks in the surface). + + // find extrema + double max_dist = -std::numeric_limits::max(); + double min_dist = std::numeric_limits::max(); + for ( int n = 0; n < my_num_nodes; n++ ) + { + if (my_dist[n] < min_dist) min_dist = my_dist[n]; + if (my_dist[n] > max_dist) max_dist = my_dist[n]; + } + + const double variation = max_dist - min_dist; + + const bool all_hi = (min_dist - variation) > 0.0; + const bool all_lo = (max_dist + variation) < 0.0; + + if (all_hi || all_lo) + { + // correct the sign since we lie entirely on one side of the interface + my_sign = LevelSet::sign(my_dist[0]); + return; + } + + non_conformal_decomposition(); +} + +int +ContourSubElement_Quad_9::non_conformal_decomposition() +{ /* %TRACE% */ /* %TRACE% */ + int success = true; // optimism + + // create 4, 3-noded, adaptive triangles + my_subelements.reserve(4); + ContourSubElement *sub = NULL; + PointVec sub_coords(3,Vector3d::ZERO); + std::vector sub_ids(3); + + // triangle #1 + sub_coords[0] = my_coords[0]; + sub_coords[1] = my_coords[1]; + sub_coords[2] = my_coords[8]; + sub_ids[0] = my_side_ids[0]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tri_3( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // triangle #2 + sub_coords[0] = my_coords[1]; + sub_coords[1] = my_coords[2]; + sub_coords[2] = my_coords[8]; + sub_ids[0] = my_side_ids[1]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tri_3( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // triangle #3 + sub_coords[0] = my_coords[2]; + sub_coords[1] = my_coords[3]; + sub_coords[2] = my_coords[8]; + sub_ids[0] = my_side_ids[2]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tri_3( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // triangle #4 + sub_coords[0] = my_coords[3]; + sub_coords[1] = my_coords[0]; + sub_coords[2] = my_coords[8]; + sub_ids[0] = my_side_ids[3]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tri_3( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + return success; +} + +ContourSubElement_Tri_3::ContourSubElement_Tri_3( + const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner, + const int in_subelement_depth, + const int subelement_sign ) + : ContourSubElement( stk::topology::TRIANGLE_3_2D, + coords, + side_ids, + in_owner, + in_subelement_depth, + subelement_sign ) +{ /* %TRACE% */ /* %TRACE% */ + // if this is a conformal element, return quickly + if ( subelement_sign != 0 ) + { + return; + } + + // snap to mesh + for (int n = 0; n < 3; ++n) + { + if (std::fabs(my_dist[n]) < my_owner->edge_linear_tolerance() * my_owner->length_scale() ) + { + my_dist[n] = 0.0; + } + } + + // see if there is a crossing + bool have_crossing = false; + for ( int i = 1; i < my_num_nodes; i++ ) + { + if ( LevelSet::sign_change(my_dist[0], my_dist[i]) ) have_crossing = true; + } + + if ( have_crossing ) + { + // attempt conformal decomposition + int success = conformal_decomposition(); + ThrowErrorMsgIf(!success, " Conformal decomposition failed.\n"); + } + else + { + // correct the sign since we lie entirely on one side of the interface + my_sign = LevelSet::sign(my_dist[0]); + } +} + +int +ContourSubElement_Tri_3::conformal_decomposition() +{ /* %TRACE% */ /* %TRACE% */ + + // create 4 conforming triangular subelements + + // create 4, 3-noded tris + my_subelements.clear(); + my_subelements.reserve(4); + ContourSubElement *sub = NULL; + PointVec sub_coords(3,Vector3d::ZERO); + + // For any edge with a crossing, we will move the + // mid side node for that egdge to the crossing + // we will keep the modified locations of the nodes + // in a local vector of nodes (lcoords). + // We will also create local vectors for the distance and side_ids + // so that we can reorient the tri as discussed below. + PointVec lcoords = my_coords; + lcoords.resize(6,Vector3d::ZERO); + std::vector sub_ids(3); + std::vector is_on_surf(6); // initializes to 0 (false) + int sub_sign; + std::vector edge_node_ids(6); + + // find edge crossings + edge_node_ids[0] = 0; + edge_node_ids[1] = 1; + edge_node_ids[2] = 2; + edge_node_ids[3] = process_edge( 0, 1, 3, is_on_surf, lcoords, my_dist ); + edge_node_ids[4] = process_edge( 1, 2, 4, is_on_surf, lcoords, my_dist ); + edge_node_ids[5] = process_edge( 2, 0, 5, is_on_surf, lcoords, my_dist ); + + const int zero_sign = LevelSet::sign(0.0); + std::vector sub_degenerate(4); // initializes to zero (false) + + sub_degenerate[0] = is_degenerate(edge_node_ids,0,3,5); + sub_degenerate[1] = is_degenerate(edge_node_ids,3,1,4); + sub_degenerate[2] = is_degenerate(edge_node_ids,5,4,2); + sub_degenerate[3] = is_degenerate(edge_node_ids,3,4,5); + + // tri #1 + if (!sub_degenerate[0]) + { + sub_coords[0] = lcoords[0]; + sub_coords[1] = lcoords[3]; + sub_coords[2] = lcoords[5]; + sub_sign = LevelSet::sign(my_dist[0]); + sub_ids[0] = my_side_ids[0]; + sub_ids[1] = ((is_on_surf[3] && is_on_surf[5]) && (zero_sign != sub_sign || sub_degenerate[3])) ? -2 : -1; + sub_ids[2] = my_side_ids[2]; + sub = new ContourSubElement_Tri_3( sub_coords, sub_ids, my_owner, my_subelement_depth+1, sub_sign ); + my_subelements.push_back( sub ); + } + + // tri #2 + if (!sub_degenerate[1]) + { + sub_coords[0] = lcoords[3]; + sub_coords[1] = lcoords[1]; + sub_coords[2] = lcoords[4]; + sub_sign = LevelSet::sign(my_dist[1]); + sub_ids[0] = my_side_ids[0]; + sub_ids[1] = my_side_ids[1]; + sub_ids[2] = ((is_on_surf[3] && is_on_surf[4]) && (zero_sign != sub_sign || sub_degenerate[3])) ? -2 : -1; + sub = new ContourSubElement_Tri_3( sub_coords, sub_ids, my_owner, my_subelement_depth+1, sub_sign ); + my_subelements.push_back( sub ); + } + + // tri #3 + if (!sub_degenerate[2]) + { + sub_coords[0] = lcoords[5]; + sub_coords[1] = lcoords[4]; + sub_coords[2] = lcoords[2]; + sub_sign = LevelSet::sign(my_dist[2]); + sub_ids[0] = ((is_on_surf[5] && is_on_surf[4]) && (zero_sign != sub_sign || sub_degenerate[3])) ? -2 : -1; + sub_ids[1] = my_side_ids[1]; + sub_ids[2] = my_side_ids[2]; + sub = new ContourSubElement_Tri_3( sub_coords, sub_ids, my_owner, my_subelement_depth+1, sub_sign ); + my_subelements.push_back( sub ); + } + + // tri #4 + if (!sub_degenerate[3]) + { + sub_coords[0] = lcoords[3]; + sub_coords[1] = lcoords[4]; + sub_coords[2] = lcoords[5]; + sub_sign = LevelSet::sign( (is_on_surf[3] ? 0.0 : my_dist[0]+my_dist[1]) + + (is_on_surf[4] ? 0.0 : my_dist[1]+my_dist[2]) + + (is_on_surf[5] ? 0.0 : my_dist[2]+my_dist[0]) ); + sub_ids[0] = ((is_on_surf[3] && is_on_surf[4]) && (zero_sign != sub_sign || sub_degenerate[1])) ? -2 : -1; + sub_ids[1] = ((is_on_surf[4] && is_on_surf[5]) && (zero_sign != sub_sign || sub_degenerate[2])) ? -2 : -1; + sub_ids[2] = ((is_on_surf[5] && is_on_surf[3]) && (zero_sign != sub_sign || sub_degenerate[0])) ? -2 : -1; + sub = new ContourSubElement_Tri_3( sub_coords, sub_ids, my_owner, my_subelement_depth+1, sub_sign ); + my_subelements.push_back( sub ); + } + + // check quality of subelements + // Here we assume that the linear tri is always decomposed into reasonable quality sub-tris + int success = true; + + if (krinolog.shouldPrint(LOG_DEBUG)) + { + for ( unsigned i=0; iphysical_quality() < 0.0 ) + { + krinolog << "low quality subelement: " << i << "\n" + << *my_subelements[i] << "\n" + << "parent:" << "\n" + << *this << "\n"; + } + } + } + + return success; +} + +int +ContourSubElement_Tri_3::process_edge( const int i0, + const int i1, + const int i2, + std::vector & is_on_surf, + PointVec & lcoords, + const std::vector & ldist ) +{ /* %TRACE% */ /* %TRACE% */ + int edge_node_id = i2; + is_on_surf[i2] = LevelSet::sign_change( ldist[i0], ldist[i1] ); + if ( is_on_surf[i2] ) + { + // tolerance chosen very small since degeneracies should already be eliminated + const double tol = std::numeric_limits::epsilon(); + + const double d0 = std::fabs(ldist[i0]); + const double d1 = std::fabs(ldist[i1]); + + // make calculation completely symmetric + if (d0 < d1) + { + const double alpha = my_owner->dist_is_linear() ? d0/(d0+d1) : find_quadratic_crossing(ldist[i0],ldist[i1],my_owner->distance(0.5*(lcoords[i0]+lcoords[i1]))); + if (alpha < tol) + { + edge_node_id = i0; + lcoords[i2] = lcoords[edge_node_id]; + } + else + { + lcoords[i2] = (1.-alpha) * lcoords[i0] + alpha * lcoords[i1]; + } + } + else + { + const double alpha = my_owner->dist_is_linear() ? d1/(d1+d0) : find_quadratic_crossing(ldist[i1],ldist[i0],my_owner->distance(0.5*(lcoords[i1]+lcoords[i0]))); + if (alpha < tol) + { + edge_node_id = i1; + lcoords[i2] = lcoords[edge_node_id]; + } + else + { + lcoords[i2] = (1.-alpha) * lcoords[i1] + alpha * lcoords[i0]; + } + } + } + else + { + // eliminate side node by sliding to one end or the other + const double d0 = std::fabs(ldist[i0]); + const double d1 = std::fabs(ldist[i1]); + const double epsilon = std::sqrt(std::numeric_limits::epsilon()) * (d0 + d1); + + if ( d0 > d1 + epsilon ) + { + edge_node_id = i0; + } + else if ( d1 > d0 + epsilon ) + { + edge_node_id = i1; + } + else + { + // tie breaker + const Vector3d phys0 = my_owner->coordinates(lcoords[i0]); + const Vector3d phys1 = my_owner->coordinates(lcoords[i1]); + + if ( is_more(phys1,phys0) ) + { + edge_node_id = i0; + } + else + { + edge_node_id = i1; + } + } + lcoords[i2] = lcoords[edge_node_id]; + } + return edge_node_id; +} + +bool +ContourSubElement_Tri_3::is_degenerate( const std::vector & edge_node_ids, + const int i0, const int i1, const int i2 ) +{ /* %TRACE% */ /* %TRACE% */ + + // DRN: This is really ugly, hand-optimized code for looking for degenerate tris + // Basically, it checks if any of the edges are degenerate. Then it has to look for + // the entire tri being degerate because it consists of 3 colinear points. + // This is handled by checking against the 3 specific bad cases. + + if ( edge_node_ids[i0] == edge_node_ids[i1] || + edge_node_ids[i0] == edge_node_ids[i2] || + edge_node_ids[i1] == edge_node_ids[i2] ) + { + // this tri is degenerate with two coincident nodes + return true; + } + + if ( edge_node_ids[i0]==i0 && + edge_node_ids[i1]==i1 && + edge_node_ids[i2]==i2 ) + { + // this tri is not degenerate since is has no degenerate nodes + return false; + } + + // look for a colinear triangle + std::vector is_used(6); // initializes to zero (false); + is_used[edge_node_ids[i0]] = true; + is_used[edge_node_ids[i1]] = true; + is_used[edge_node_ids[i2]] = true; + + if ((is_used[0] && ((is_used[1] && is_used[3]) || (is_used[2] && is_used[5]))) || + (is_used[1] && is_used[2] && is_used[4])) + { + // this tri is colinear + return true; + } + + return false; +} + +int +ContourSubElement_Tri_3::side_facets( Faceted_Surface & facets, + int side ) const +{ /* %TRACE% */ /* %TRACE% */ + ThrowAssert( my_side_ids[side] == -2 ); + + // just one linear facet per side + const int num_facets = 1; + + const unsigned * const lnn = get_side_node_ordinals(topology(), side); + + if ( LevelSet::sign_change(0.0, (double) my_sign) ) + { + std::unique_ptr facet = std::make_unique( my_owner->coordinates(my_coords[lnn[0]]), my_owner->coordinates(my_coords[lnn[1]]) ); + facets.add( std::move(facet) ); + } + else + { + std::unique_ptr facet = std::make_unique( my_owner->coordinates(my_coords[lnn[1]]), my_owner->coordinates(my_coords[lnn[0]]) ); + facets.add( std::move(facet) ); + } + + return( num_facets ); +} + +const int ContourSubElement_Adaptive_Tri_3::MAX_REFINMENT_LEVELS = 6; + +ContourSubElement_Adaptive_Tri_3::ContourSubElement_Adaptive_Tri_3( + const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner, + const int in_subelement_depth ) + : ContourSubElement( stk::topology::TRIANGLE_3_2D, + coords, + side_ids, + in_owner, + in_subelement_depth, + 0 ) +{ /* %TRACE% */ /* %TRACE% */ + my_edge_age.resize(3); // initializes to zero + non_conformal_decomposition(); +} + +ContourSubElement_Adaptive_Tri_3::ContourSubElement_Adaptive_Tri_3( + const PointVec & coords, + const std::vector & side_ids, + const std::vector & edge_age, + const ContourElement * in_owner, + const int in_subelement_depth ) + : ContourSubElement( stk::topology::TRIANGLE_3_2D, + coords, + side_ids, + in_owner, + in_subelement_depth, + 0 ) +{ /* %TRACE% */ /* %TRACE% */ + my_edge_age = edge_age; + non_conformal_decomposition(); +} + +int +ContourSubElement_Adaptive_Tri_3::non_conformal_decomposition() +{ /* %TRACE% */ /* %TRACE% */ + int success = true; // optimism + + // Determine if we will continue to look for crossing within this element. + // This test should be conservative, proceeding to look for crossings if there + // is even a remote chance of a crossing (To avoid cracks in the surface). + + // find extrema + double max_dist = -std::numeric_limits::max(); + double min_dist = std::numeric_limits::max(); + for ( int n = 0; n < my_num_nodes; n++ ) + { + if (my_dist[n] < min_dist) min_dist = my_dist[n]; + if (my_dist[n] > max_dist) max_dist = my_dist[n]; + } + + const double variation = max_dist - min_dist; + + const bool all_hi = (min_dist - variation) > 0.0; + const bool all_lo = (max_dist + variation) < 0.0; + + if (all_hi || all_lo) + { + // correct the sign since we lie entirely on one side of the interface + my_sign = LevelSet::sign(my_dist[0]); + return success; + } + + int longest_bad_edge = -1; + + // use temporary storage for vertex and side nodes + PointVec lcoords = my_coords; + lcoords.resize(6,Vector3d::ZERO); + lcoords[3] = 0.5 * (my_coords[0] + my_coords[1]); + lcoords[4] = 0.5 * (my_coords[1] + my_coords[2]); + lcoords[5] = 0.5 * (my_coords[2] + my_coords[0]); + + PointVec lphyscoords(6); + for (int n = 0; n < 6; ++n) + { + lphyscoords[n] = my_owner->coordinates( lcoords[n] ); + } + + std::vector ldist = my_dist; + ldist.resize(6); + for (int n = 3; n < 6; ++n) + { + ldist[n] = my_owner->distance( lcoords[n] ); + } + + const stk::topology Top = stk::topology::TRIANGLE_6_2D; + int num_edges = Top.num_edges(); + + std::vector bad_edges; + bad_edges.reserve(num_edges); + + std::vector edge_lengths(num_edges); + + for ( int edge = 0; edge < num_edges; edge++ ) + { + const unsigned * const lnn = get_edge_node_ordinals(Top, edge); + + ThrowAssert(Top.edge_topology(edge).num_nodes() == 3); + + const double edge_straight_length = (lphyscoords[lnn[0]] - lphyscoords[lnn[1]]).length(); + ThrowRequire(edge_straight_length > 0.0); + edge_lengths[edge] = edge_straight_length; + + const double edge_curve_error = (lphyscoords[lnn[2]] - 0.5*(lphyscoords[lnn[0]] + lphyscoords[lnn[1]])).length(); + + const double edge_dist_error = std::fabs(ldist[lnn[2]] - 0.5*(ldist[lnn[0]]+ldist[lnn[1]])); + + const double scale = std::min(std::sqrt(std::numeric_limits::max()),std::fabs(ldist[lnn[0]]) + std::fabs(ldist[lnn[1]]) + my_owner->length_scale()); + + const double edge_error = (edge_curve_error + edge_dist_error)*edge_straight_length/(scale*scale); + + if (edge_error > my_owner->edge_nonlinear_tolerance() && my_edge_age[edge] < MAX_REFINMENT_LEVELS) + { + bad_edges.push_back(edge); + } + } + + double max_length = 0.0; + for (auto edge : bad_edges) + { + const double edge_length = edge_lengths[edge]; + ThrowRequire(edge_length > 0.0); + + // we need an absolute mechanism for selecting the edge to bisect so that all elements that share + // common edges will make the same decisions + if (utility::is_more(edge_length,max_length)) + { + longest_bad_edge = edge; + max_length = edge_length; + } + else if (!utility::is_less(edge_length,max_length)) // tie breaker + { + const Vector3d & edge_midside_coords = lphyscoords[get_edge_node_ordinals(Top, edge)[2]]; + // note that it is safe to assume that longest_bad_edge is already assigned if edge_length == max_length + const Vector3d longest_edge_midside_coords = lphyscoords[get_edge_node_ordinals(Top, longest_bad_edge)[2]]; + + ThrowAssert((utility::is_not_equal(edge_midside_coords[0],longest_edge_midside_coords[0]) || + utility::is_not_equal(edge_midside_coords[1],longest_edge_midside_coords[1]))); + + if (utility::is_more(edge_midside_coords[0],longest_edge_midside_coords[0]) || + (!utility::is_less(edge_midside_coords[0],longest_edge_midside_coords[0]) && + (utility::is_more(edge_midside_coords[1],longest_edge_midside_coords[1])))) + { + longest_bad_edge = edge; + max_length = edge_length; + } + } + } + + if ( longest_bad_edge == -1 ) + { + // no bad edges + + // use a single nonconformal linear tet subelement + my_subelements.clear(); + my_subelements.reserve(1); + + ContourSubElement *sub = new ContourSubElement_Tri_3( my_coords, my_side_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + } + else + { + // + // create 2, adaptive, 3-noded triangles by cutting the longest_bad_edge + // + + my_subelements.clear(); + my_subelements.reserve(2); + ContourSubElement *sub = NULL; + PointVec sub_coords(3,Vector3d::ZERO); + std::vector sub_ids(3); + std::vector sub_edge_age(3); + + static const unsigned permute_0[] = { 0,1,2 }; + static const unsigned permute_1[] = { 1,2,0 }; + static const unsigned permute_2[] = { 2,0,1 }; + static const unsigned * permute_table[] = { permute_0, permute_1, permute_2 }; + + const unsigned * lnn = permute_table[longest_bad_edge]; + const unsigned * lsn = lnn; // side permutation mirrors node permutation + + const Vector3d edge_node = 0.5 * (my_coords[lnn[0]] + my_coords[lnn[1]]); + + // tri #1 + sub_coords[0] = my_coords[lnn[0]]; + sub_coords[1] = edge_node; + sub_coords[2] = my_coords[lnn[2]]; + sub_ids[0] = my_side_ids[lsn[0]]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = my_side_ids[lsn[2]]; + sub_edge_age[0] = my_edge_age[lsn[0]]+1; + sub_edge_age[1] = my_edge_age[lsn[0]]+1; + sub_edge_age[2] = my_edge_age[lsn[2]]; + sub = new ContourSubElement_Adaptive_Tri_3( sub_coords, sub_ids, sub_edge_age, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // tri #2 + sub_coords[0] = edge_node; + sub_coords[1] = my_coords[lnn[1]]; + sub_coords[2] = my_coords[lnn[2]]; + sub_ids[0] = my_side_ids[lsn[0]]; + sub_ids[1] = my_side_ids[lsn[1]]; + sub_edge_age[0] = my_edge_age[lsn[0]]+1; + sub_edge_age[1] = my_edge_age[lsn[1]]; + sub_edge_age[2] = my_edge_age[lsn[0]]+1; + sub_ids[2] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tri_3( sub_coords, sub_ids, sub_edge_age, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + } + + return success; +} + +ContourSubElement_Tri_6::ContourSubElement_Tri_6( + const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner, + const int in_subelement_depth, + const int subelement_sign ) + : ContourSubElement( stk::topology::TRIANGLE_6_2D, + coords, + side_ids, + in_owner, + in_subelement_depth, + subelement_sign ) +{ /* %TRACE% */ /* %TRACE% */ + + // Determine if we will continue to look for crossing within this element. + // This test should be conservative, proceeding to look for crossings if there + // is even a remote chance of a crossing (To avoid cracks in the surface). + + // find extrema + double max_dist = -std::numeric_limits::max(); + double min_dist = std::numeric_limits::max(); + for ( int n = 0; n < my_num_nodes; n++ ) + { + if (my_dist[n] < min_dist) min_dist = my_dist[n]; + if (my_dist[n] > max_dist) max_dist = my_dist[n]; + } + + const double variation = max_dist - min_dist; + + const bool all_hi = (min_dist - variation) > 0.0; + const bool all_lo = (max_dist + variation) < 0.0; + + if (all_hi || all_lo) + { + // correct the sign since we lie entirely on one side of the interface + my_sign = LevelSet::sign(my_dist[0]); + return; + } + + // use a single non-conformal, adaptive 4-noded tet + my_subelements.clear(); + my_subelements.reserve(1); + ContourSubElement *sub = NULL; + PointVec sub_coords(3,Vector3d::ZERO); + + sub_coords[0] = my_coords[0]; + sub_coords[1] = my_coords[1]; + sub_coords[2] = my_coords[2]; + + sub = new ContourSubElement_Adaptive_Tri_3( sub_coords, my_side_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); +} + +ContourSubElement_Hex_8::ContourSubElement_Hex_8( + const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner ) + : ContourSubElement( stk::topology::HEXAHEDRON_8, + coords, + side_ids, + in_owner, + 0, /* in_subelement_depth*/ + 0 /* subelement_sign=0 for now, correct this below if this element is entirely on one side */ ) +{ /* %TRACE% */ /* %TRACE% */ + + // Determine if we will continue to look for crossing within this element. + // This test should be conservative, proceeding to look for crossings if there + // is even a remote chance of a crossing (To avoid cracks in the surface). + + // find extrema + double max_dist = -std::numeric_limits::max(); + double min_dist = std::numeric_limits::max(); + for ( int n = 0; n < my_num_nodes; n++ ) + { + if (my_dist[n] < min_dist) min_dist = my_dist[n]; + if (my_dist[n] > max_dist) max_dist = my_dist[n]; + } + + const double variation = max_dist - min_dist; + + const bool all_hi = (min_dist - variation) > 0.0; + const bool all_lo = (max_dist + variation) < 0.0; + + if (all_hi || all_lo) + { + // correct the sign since we lie entirely on one side of the interface + my_sign = LevelSet::sign(my_dist[0]); + return; + } + + // create 24, 4-noded, adaptive tetrahedra + my_subelements.reserve(24); + + // Conceptually, hex is broken into 6 prisms, with the + // bases of the prisms corresponding to a face of the hex. + int success = true; // optimism + for ( int face = 0; face < 6 && success; ++face ) + { + success &= subpyramid_non_conformal_decomposition( face ); + } +} + +int +ContourSubElement_Hex_8::subpyramid_non_conformal_decomposition( const int face ) +{ /* %TRACE% */ /* %TRACE% */ + int success = true; // optimism + ContourSubElement *sub = NULL; + PointVec sub_coords(4,Vector3d::ZERO); + std::vector sub_ids(4); + + static const unsigned face_0[] = { 0,1,5,4 }; + static const unsigned face_1[] = { 1,2,6,5 }; + static const unsigned face_2[] = { 2,3,7,6 }; + static const unsigned face_3[] = { 0,4,7,3 }; + static const unsigned face_4[] = { 0,3,2,1 }; + static const unsigned face_5[] = { 4,5,6,7 }; + + static const unsigned * face_table[] = { face_0 , face_1 , face_2 , face_3 , face_4 , face_5 }; + + const unsigned * lnn = face_table[face]; + + // + // create 4, 4-noded adaptive tetrahedra + // + // The advantage of 4 tets per face over 2 tets per face is that all corners + // will be bisected. This eliminates some of the pathologies that occur when + // 3 nodes have the same value while the 4th node on the face has a different + // sign. Note that this problem can be mitigated, however, if the non-conformal + // refinement of the sub-tets will do longest edge bisection rather than the + // self-similar 8 subtet refinement. + + Vector3d vol_center = 0.125*(my_coords[0]+my_coords[1]+my_coords[2]+my_coords[3]+ + my_coords[4]+my_coords[5]+my_coords[6]+my_coords[7]); + Vector3d face_center = 0.25*(my_coords[lnn[0]]+my_coords[lnn[1]]+my_coords[lnn[2]]+my_coords[lnn[3]]); + + // tet #1 + sub_coords[0] = my_coords[lnn[0]]; + sub_coords[1] = face_center; + sub_coords[2] = my_coords[lnn[1]]; + sub_coords[3] = vol_center; + sub_ids[0] = my_side_ids[face]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub_ids[3] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // tet #2 + sub_coords[0] = my_coords[lnn[1]]; + sub_coords[1] = face_center; + sub_coords[2] = my_coords[lnn[2]]; + sub_coords[3] = vol_center; + sub_ids[0] = my_side_ids[face]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub_ids[3] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // tet #3 + sub_coords[0] = my_coords[lnn[2]]; + sub_coords[1] = face_center; + sub_coords[2] = my_coords[lnn[3]]; + sub_coords[3] = vol_center; + sub_ids[0] = my_side_ids[face]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub_ids[3] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // tet #4 + sub_coords[0] = my_coords[lnn[3]]; + sub_coords[1] = face_center; + sub_coords[2] = my_coords[lnn[0]]; + sub_coords[3] = vol_center; + sub_ids[0] = my_side_ids[face]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub_ids[3] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + return success; +} + +ContourSubElement_Hex_27::ContourSubElement_Hex_27( + const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner ) + : ContourSubElement( stk::topology::HEXAHEDRON_27, + coords, + side_ids, + in_owner, + 0, /* in_subelement_depth*/ + 0 /* subelement_sign=0 for now, correct this below if this element is entirely on one side */ ) +{ /* %TRACE% */ /* %TRACE% */ + + // Determine if we will continue to look for crossing within this element. + // This test should be conservative, proceeding to look for crossings if there + // is even a remote chance of a crossing (To avoid cracks in the surface). + + // find extrema + double max_dist = -std::numeric_limits::max(); + double min_dist = std::numeric_limits::max(); + for ( int n = 0; n < my_num_nodes; n++ ) + { + if (my_dist[n] < min_dist) min_dist = my_dist[n]; + if (my_dist[n] > max_dist) max_dist = my_dist[n]; + } + + const double variation = max_dist - min_dist; + + const bool all_hi = (min_dist - variation) > 0.0; + const bool all_lo = (max_dist + variation) < 0.0; + + if (all_hi || all_lo) + { + // correct the sign since we lie entirely on one side of the interface + my_sign = LevelSet::sign(my_dist[0]); + return; + } + + // create 24, 4-noded, adaptive tetrahedra + my_subelements.reserve(24); + + // Conceptually, hex is broken into 6 prisms, with the + // bases of the prisms corresponding to a face of the hex. + int success = true; // optimism + for ( int face = 0; face < 6 && success; ++face ) + { + success &= subpyramid_non_conformal_decomposition( face ); + } +} + +int +ContourSubElement_Hex_27::subpyramid_non_conformal_decomposition( const int face ) +{ /* %TRACE% */ /* %TRACE% */ + int success = true; // optimism + ContourSubElement *sub = NULL; + PointVec sub_coords(4,Vector3d::ZERO); + std::vector sub_ids(4); + + static const unsigned face_0[] = { 0,1,5,4, 25 }; + static const unsigned face_1[] = { 1,2,6,5, 24 }; + static const unsigned face_2[] = { 2,3,7,6, 26 }; + static const unsigned face_3[] = { 0,4,7,3, 23 }; + static const unsigned face_4[] = { 0,3,2,1, 21 }; + static const unsigned face_5[] = { 4,5,6,7, 22 }; + + static const unsigned * face_table[] = { face_0 , face_1 , face_2 , face_3 , face_4 , face_5 }; + + const unsigned * lnn = face_table[face]; + + // + // create 4, 4-noded adaptive tetrahedra + // + // The advantage of 4 tets per face over 2 tets per face is that all corners + // will be bisected. This eliminates some of the pathologies that occur when + // 3 nodes have the same value while the 4th node on the face has a different + // sign. Note that this problem can be mitigated, however, if the non-conformal + // refinement of the sub-tets will do longest edge bisection rather than the + // self-similar 8 subtet refinement. + + // tet #1 + sub_coords[0] = my_coords[lnn[0]]; + sub_coords[1] = my_coords[lnn[4]]; + sub_coords[2] = my_coords[lnn[1]]; + sub_coords[3] = my_coords[20]; + sub_ids[0] = my_side_ids[face]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub_ids[3] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // tet #2 + sub_coords[0] = my_coords[lnn[1]]; + sub_coords[1] = my_coords[lnn[4]]; + sub_coords[2] = my_coords[lnn[2]]; + sub_coords[3] = my_coords[20]; + sub_ids[0] = my_side_ids[face]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub_ids[3] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // tet #3 + sub_coords[0] = my_coords[lnn[2]]; + sub_coords[1] = my_coords[lnn[4]]; + sub_coords[2] = my_coords[lnn[3]]; + sub_coords[3] = my_coords[20]; + sub_ids[0] = my_side_ids[face]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub_ids[3] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // tet #4 + sub_coords[0] = my_coords[lnn[3]]; + sub_coords[1] = my_coords[lnn[4]]; + sub_coords[2] = my_coords[lnn[0]]; + sub_coords[3] = my_coords[20]; + sub_ids[0] = my_side_ids[face]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub_ids[3] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + return success; +} + +ContourSubElement_Wedge_6::ContourSubElement_Wedge_6( + const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner ) + : ContourSubElement( stk::topology::WEDGE_6, + coords, + side_ids, + in_owner, + 0, /* in_subelement_depth*/ + 0 /* subelement_sign=0 for now, correct this below if this element is entirely on one side */ ) +{ /* %TRACE% */ /* %TRACE% */ + + // Determine if we will continue to look for crossing within this element. + // This test should be conservative, proceeding to look for crossings if there + // is even a remote chance of a crossing (To avoid cracks in the surface). + + // find extrema + double max_dist = -std::numeric_limits::max(); + double min_dist = std::numeric_limits::max(); + for ( int n = 0; n < my_num_nodes; n++ ) + { + if (my_dist[n] < min_dist) min_dist = my_dist[n]; + if (my_dist[n] > max_dist) max_dist = my_dist[n]; + } + + const double variation = max_dist - min_dist; + + const bool all_hi = (min_dist - variation) > 0.0; + const bool all_lo = (max_dist + variation) < 0.0; + + if (all_hi || all_lo) + { + // correct the sign since we lie entirely on one side of the interface + my_sign = LevelSet::sign(my_dist[0]); + return; + } + + // create 12, 4-noded, adaptive tetrahedra + my_subelements.reserve(12); + + int success = true; // optimism + for ( int face = 0; face < 3 && success; ++face ) + { + success &= subpyramid_non_conformal_decomposition( face ); + } +} + +int +ContourSubElement_Wedge_6::subpyramid_non_conformal_decomposition( const int face ) +{ /* %TRACE% */ /* %TRACE% */ + int success = true; // optimism + ContourSubElement *sub = NULL; + PointVec sub_coords(4,Vector3d::ZERO); + std::vector sub_ids(4); + + static const unsigned face_0[] = {0, 1, 4, 3}; + static const unsigned face_1[] = {1, 2, 5, 4}; + static const unsigned face_2[] = {0, 3, 5, 2}; + static const unsigned * face_table[] = { face_0 , face_1 , face_2 }; + + const unsigned * lnn = face_table[face]; + + // + // create 4, 4-noded adaptive tetrahedra + // + // The advantage of 4 tets per face over 2 tets per face is that all corners + // will be bisected. This eliminates some of the pathologies that occur when + // 3 nodes have the same value while the 4th node on the face has a different + // sign. Note that this problem can be mitigated, however, if the non-conformal + // refinement of the sub-tets will do longest edge bisection rather than the + // self-similar 8 subtet refinement. + + // Not guaranteed to be within a highly deformed wedge + const Vector3d centroid = (my_coords[0]+my_coords[1]+my_coords[2]+my_coords[3]+my_coords[4]+my_coords[5])/6.; + const Vector3d face_center = 0.25*(my_coords[lnn[0]]+my_coords[lnn[1]]+my_coords[lnn[2]]+my_coords[lnn[3]]); + + // tet #1 + sub_coords[0] = my_coords[lnn[0]]; + sub_coords[1] = face_center; + sub_coords[2] = my_coords[lnn[1]]; + sub_coords[3] = centroid; + sub_ids[0] = my_side_ids[face]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub_ids[3] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // tet #2 + sub_coords[0] = my_coords[lnn[1]]; + sub_coords[1] = face_center; + sub_coords[2] = my_coords[lnn[2]]; + sub_coords[3] = centroid; + sub_ids[0] = my_side_ids[face]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub_ids[3] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // tet #3 + sub_coords[0] = my_coords[lnn[2]]; + sub_coords[1] = face_center; + sub_coords[2] = my_coords[lnn[3]]; + sub_coords[3] = centroid; + sub_ids[0] = my_side_ids[face]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub_ids[3] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // tet #4 + sub_coords[0] = my_coords[lnn[3]]; + sub_coords[1] = face_center; + sub_coords[2] = my_coords[lnn[0]]; + sub_coords[3] = centroid; + sub_ids[0] = my_side_ids[face]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = -1; /* not on any parent side */ + sub_ids[3] = -1; /* not on any parent side */ + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + return success; +} + +ContourSubElement_Tet_4::ContourSubElement_Tet_4( + const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner, + const int in_subelement_depth, + const int subelement_sign ) + : ContourSubElement( stk::topology::TETRAHEDRON_4, + coords, + side_ids, + in_owner, + in_subelement_depth, + subelement_sign ) +{ /* %TRACE% */ /* %TRACE% */ + // if this is a conformal element, return quickly + if ( subelement_sign != 0 ) + { + return; + } + + // snap to mesh + for (int n = 0; n < 4; ++n) + { + if (std::fabs(my_dist[n]) < my_owner->edge_linear_tolerance() * my_owner->length_scale()) + { + my_dist[n] = 0.0; + } + } + + // see if there is a crossing + bool have_crossing = false; + for ( int i = 1; i < my_num_nodes; i++ ) + { + if ( LevelSet::sign_change(my_dist[0], my_dist[i]) ) have_crossing = true; + } + + if ( have_crossing ) + { + // attempt conformal decomposition + int success = conformal_decomposition(); + ThrowErrorMsgIf(!success, " Conformal decomposition failed.\n"); + } + else + { + // correct the sign since we lie entirely on one side of the interface + my_sign = LevelSet::sign(my_dist[0]); + } +} + +int +ContourSubElement_Tet_4::conformal_decomposition() +{ /* %TRACE% */ /* %TRACE% */ + + // attempt to create 8 conforming tetrahedral subelements + // This attempt may unsuccessful if the resulting subelements + // are of poor quality + int success = true; // optimism + + // create 8, 4-noded tets + my_subelements.clear(); + my_subelements.reserve(8); + ContourSubElement *sub = NULL; + PointVec sub_coords(4,Vector3d::ZERO); + + // For any edge with a crossing, we will move the + // mid side node for that egdge to the crossing + // we will keep the modified locations of the nodes + // in a local vector of nodes (lcoords). + // We will also create local vectors for the distance and side_ids + // so that we can reorient the tet as discussed below. + PointVec lcoords = my_coords; + lcoords.resize(10,Vector3d::ZERO); + std::vector lsides = my_side_ids; + std::vector ldist = my_dist; + std::vector sub_ids(4); + std::vector is_on_surf(10); // initializes to 0 (false) + int sub_sign; + + // Find orientation of tet + // Specifically, orient such that we don't have nodes 0 and 2 on one side and + // nodes 1 and 3 on the other + if ( LevelSet::sign_change(my_dist[0],my_dist[1]) && + LevelSet::sign_change(my_dist[1],my_dist[2]) && + LevelSet::sign_change(my_dist[2],my_dist[3]) ) + { + lcoords[0] = my_coords[0]; + lcoords[1] = my_coords[3]; + lcoords[2] = my_coords[1]; + lcoords[3] = my_coords[2]; + ldist[0] = my_dist[0]; + ldist[1] = my_dist[3]; + ldist[2] = my_dist[1]; + ldist[3] = my_dist[2]; + lsides[0] = my_side_ids[2]; + lsides[1] = my_side_ids[1]; + lsides[2] = my_side_ids[3]; + lsides[3] = my_side_ids[0]; + } + + std::vector edge_node_ids(10); + edge_node_ids[0] = 0; + edge_node_ids[1] = 1; + edge_node_ids[2] = 2; + edge_node_ids[3] = 3; + edge_node_ids[4] = process_edge( 0, 1, 4, is_on_surf, lcoords, ldist ); + edge_node_ids[5] = process_edge( 1, 2, 5, is_on_surf, lcoords, ldist ); + edge_node_ids[6] = process_edge( 2, 0, 6, is_on_surf, lcoords, ldist ); + edge_node_ids[7] = process_edge( 0, 3, 7, is_on_surf, lcoords, ldist ); + edge_node_ids[8] = process_edge( 1, 3, 8, is_on_surf, lcoords, ldist ); + edge_node_ids[9] = process_edge( 2, 3, 9, is_on_surf, lcoords, ldist ); + + const int zero_sign = LevelSet::sign(0.0); + std::vector sub_degenerate(8); // initializes to zero (false) + + sub_degenerate[0] = is_degenerate(edge_node_ids,0,4,6,7); + sub_degenerate[1] = is_degenerate(edge_node_ids,4,1,5,8); + sub_degenerate[2] = is_degenerate(edge_node_ids,6,5,2,9); + sub_degenerate[3] = is_degenerate(edge_node_ids,7,8,9,3); + sub_degenerate[4] = is_degenerate(edge_node_ids,8,7,6,4); + sub_degenerate[5] = is_degenerate(edge_node_ids,6,9,8,5); + sub_degenerate[6] = is_degenerate(edge_node_ids,9,8,7,6); + sub_degenerate[7] = is_degenerate(edge_node_ids,5,6,4,8); + + // tet #1 + if (!sub_degenerate[0]) + { + sub_coords[0] = lcoords[0]; + sub_coords[1] = lcoords[4]; + sub_coords[2] = lcoords[6]; + sub_coords[3] = lcoords[7]; + sub_sign = LevelSet::sign(ldist[0]); + sub_ids[0] = lsides[0]; + sub_ids[1] = ((is_on_surf[4] && is_on_surf[6] && is_on_surf[7]) && (zero_sign != sub_sign || sub_degenerate[4])) ? -2 : -1; + sub_ids[2] = lsides[2]; + sub_ids[3] = lsides[3]; + sub = new ContourSubElement_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1, sub_sign ); + my_subelements.push_back( sub ); + } + + // tet #2 + if (!sub_degenerate[1]) + { + sub_coords[0] = lcoords[4]; + sub_coords[1] = lcoords[1]; + sub_coords[2] = lcoords[5]; + sub_coords[3] = lcoords[8]; + sub_sign = LevelSet::sign(ldist[1]); + sub_ids[0] = lsides[0]; + sub_ids[1] = lsides[1]; + sub_ids[2] = ((is_on_surf[4] && is_on_surf[5] && is_on_surf[8]) && (zero_sign != sub_sign || sub_degenerate[7])) ? -2 : -1; + sub_ids[3] = lsides[3]; + sub = new ContourSubElement_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1, sub_sign ); + my_subelements.push_back( sub ); + } + + // tet #3 + if (!sub_degenerate[2]) + { + sub_coords[0] = lcoords[6]; + sub_coords[1] = lcoords[5]; + sub_coords[2] = lcoords[2]; + sub_coords[3] = lcoords[9]; + sub_sign = LevelSet::sign(ldist[2]); + sub_ids[0] = ((is_on_surf[5] && is_on_surf[6] && is_on_surf[9]) && (zero_sign != sub_sign || sub_degenerate[5])) ? -2 : -1; + sub_ids[1] = lsides[1]; + sub_ids[2] = lsides[2]; + sub_ids[3] = lsides[3]; + sub = new ContourSubElement_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1, sub_sign ); + my_subelements.push_back( sub ); + } + + // tet #4 + if (!sub_degenerate[3]) + { + sub_coords[0] = lcoords[7]; + sub_coords[1] = lcoords[8]; + sub_coords[2] = lcoords[9]; + sub_coords[3] = lcoords[3]; + sub_sign = LevelSet::sign(ldist[3]); + sub_ids[0] = lsides[0]; + sub_ids[1] = lsides[1]; + sub_ids[2] = lsides[2]; + sub_ids[3] = ((is_on_surf[7] && is_on_surf[8] && is_on_surf[9]) && (zero_sign != sub_sign || sub_degenerate[6])) ? -2 : -1; + sub = new ContourSubElement_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1, sub_sign ); + my_subelements.push_back( sub ); + } + + // tet #5 + if (!sub_degenerate[4]) + { + sub_coords[0] = lcoords[8]; + sub_coords[1] = lcoords[7]; + sub_coords[2] = lcoords[6]; + sub_coords[3] = lcoords[4]; + sub_sign = LevelSet::sign( (is_on_surf[8] ? 0.0 : ldist[1]+ldist[3]) + + (is_on_surf[7] ? 0.0 : ldist[0]+ldist[3]) + + (is_on_surf[6] ? 0.0 : ldist[0]+ldist[2]) + + (is_on_surf[4] ? 0.0 : ldist[0]+ldist[1]) ); + sub_ids[0] = lsides[0]; // 8-7-4 + sub_ids[1] = ((is_on_surf[7] && is_on_surf[6] && is_on_surf[4]) && (zero_sign != sub_sign || sub_degenerate[0])) ? -2 : -1; + sub_ids[2] = ((is_on_surf[8] && is_on_surf[6] && is_on_surf[4]) && (zero_sign != sub_sign || sub_degenerate[7])) ? -2 : -1; + sub_ids[3] = ((is_on_surf[8] && is_on_surf[7] && is_on_surf[6]) && (zero_sign != sub_sign || sub_degenerate[6])) ? -2 : -1; + sub = new ContourSubElement_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1, sub_sign ); + my_subelements.push_back( sub ); + } + + // tet #6 + if (!sub_degenerate[5]) + { + sub_coords[0] = lcoords[6]; + sub_coords[1] = lcoords[9]; + sub_coords[2] = lcoords[8]; + sub_coords[3] = lcoords[5]; + sub_sign = LevelSet::sign( (is_on_surf[6] ? 0.0 : ldist[0]+ldist[2]) + + (is_on_surf[9] ? 0.0 : ldist[2]+ldist[3]) + + (is_on_surf[8] ? 0.0 : ldist[1]+ldist[3]) + + (is_on_surf[5] ? 0.0 : ldist[1]+ldist[2]) ); + sub_ids[0] = ((is_on_surf[6] && is_on_surf[9] && is_on_surf[5]) && (zero_sign != sub_sign || sub_degenerate[2])) ? -2 : -1; + sub_ids[1] = lsides[1]; // 8-9-5 + sub_ids[2] = ((is_on_surf[6] && is_on_surf[8] && is_on_surf[5]) && (zero_sign != sub_sign || sub_degenerate[7])) ? -2 : -1; + sub_ids[3] = ((is_on_surf[6] && is_on_surf[9] && is_on_surf[8]) && (zero_sign != sub_sign || sub_degenerate[6])) ? -2 : -1; + sub = new ContourSubElement_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1, sub_sign ); + my_subelements.push_back( sub ); + } + + // tet #7 + if (!sub_degenerate[6]) + { + sub_coords[0] = lcoords[9]; + sub_coords[1] = lcoords[8]; + sub_coords[2] = lcoords[7]; + sub_coords[3] = lcoords[6]; + sub_sign = LevelSet::sign( (is_on_surf[9] ? 0.0 : ldist[2]+ldist[3]) + + (is_on_surf[8] ? 0.0 : ldist[1]+ldist[3]) + + (is_on_surf[7] ? 0.0 : ldist[0]+ldist[3]) + + (is_on_surf[6] ? 0.0 : ldist[0]+ldist[2]) ); + sub_ids[0] = ((is_on_surf[9] && is_on_surf[8] && is_on_surf[6]) && (zero_sign != sub_sign || sub_degenerate[5])) ? -2 : -1; + sub_ids[1] = ((is_on_surf[8] && is_on_surf[7] && is_on_surf[6]) && (zero_sign != sub_sign || sub_degenerate[4])) ? -2 : -1; + sub_ids[2] = lsides[2]; // 9-7-6 + sub_ids[3] = ((is_on_surf[9] && is_on_surf[8] && is_on_surf[7]) && (zero_sign != sub_sign || sub_degenerate[3])) ? -2 : -1; + sub = new ContourSubElement_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1, sub_sign ); + my_subelements.push_back( sub ); + } + + // tet #8 + if (!sub_degenerate[7]) + { + sub_coords[0] = lcoords[5]; + sub_coords[1] = lcoords[6]; + sub_coords[2] = lcoords[4]; + sub_coords[3] = lcoords[8]; + sub_sign = LevelSet::sign( (is_on_surf[5] ? 0.0 : ldist[1]+ldist[2]) + + (is_on_surf[6] ? 0.0 : ldist[0]+ldist[2]) + + (is_on_surf[4] ? 0.0 : ldist[0]+ldist[1]) + + (is_on_surf[8] ? 0.0 : ldist[1]+ldist[3]) ); + sub_ids[0] = ((is_on_surf[5] && is_on_surf[6] && is_on_surf[8]) && (zero_sign != sub_sign || sub_degenerate[5])) ? -2 : -1; + sub_ids[1] = ((is_on_surf[6] && is_on_surf[4] && is_on_surf[8]) && (zero_sign != sub_sign || sub_degenerate[4])) ? -2 : -1; + sub_ids[2] = ((is_on_surf[5] && is_on_surf[4] && is_on_surf[8]) && (zero_sign != sub_sign || sub_degenerate[1])) ? -2 : -1; + sub_ids[3] = lsides[3]; // 4-5-6 + sub = new ContourSubElement_Tet_4( sub_coords, sub_ids, my_owner, my_subelement_depth+1, sub_sign ); + my_subelements.push_back( sub ); + } + + // check quality of subelements + // Here we assume that the linear tet is always decomposed into reasonable quality sub-tets + success = true; + + if (krinolog.shouldPrint(LOG_DEBUG)) + { + for ( unsigned i=0; iphysical_quality() < 0.0 ) + { + krinolog << "low quality subelement: " << i << "\n" + << *my_subelements[i] << "\n" + << "parent:" << "\n" + << *this << "\n"; + } + } + } + + return success; +} + +int +ContourSubElement_Tet_4::process_edge( const int i0, + const int i1, + const int i2, + std::vector & is_on_surf, + PointVec & lcoords, + const std::vector & ldist ) +{ /* %TRACE% */ /* %TRACE% */ + int edge_node_id = i2; + is_on_surf[i2] = LevelSet::sign_change( ldist[i0], ldist[i1] ); + if ( is_on_surf[i2] ) + { + // tolerance chosen very small since degeneracies should already be eliminated + const double tol = std::numeric_limits::epsilon(); + + const double d0 = std::fabs(ldist[i0]); + const double d1 = std::fabs(ldist[i1]); + + // make calculation completely symmetric + if (d0 < d1) + { + const double linear_alpha = d0/(d0+d1); + + if (linear_alpha < tol) + { + edge_node_id = i0; + lcoords[i2] = lcoords[edge_node_id]; + } + else + { + const double alpha = my_owner->dist_is_linear() ? linear_alpha : + find_quadratic_crossing(ldist[i0],ldist[i1],my_owner->distance(0.5*(lcoords[i0]+lcoords[i1]))); + lcoords[i2] = (1.-alpha) * lcoords[i0] + alpha * lcoords[i1]; + } + } + else + { + const double linear_alpha = d1/(d1+d0); + + if (linear_alpha < tol) + { + edge_node_id = i1; + lcoords[i2] = lcoords[edge_node_id]; + } + else + { + const double alpha = my_owner->dist_is_linear() ? linear_alpha : + find_quadratic_crossing(ldist[i1],ldist[i0],my_owner->distance(0.5*(lcoords[i1]+lcoords[i0]))); + lcoords[i2] = (1.-alpha) * lcoords[i1] + alpha * lcoords[i0]; + } + } + } + else + { + // eliminate side node by sliding to one end or the other + const double d0 = std::fabs(ldist[i0]); + const double d1 = std::fabs(ldist[i1]); + const double epsilon = std::sqrt(std::numeric_limits::epsilon()) * (d0 + d1); + + if ( d0 > d1 + epsilon ) + { + edge_node_id = i0; + } + else if ( d1 > d0 + epsilon ) + { + edge_node_id = i1; + } + else + { + // tie breaker + const Vector3d phys0 = my_owner->coordinates(lcoords[i0]); + const Vector3d phys1 = my_owner->coordinates(lcoords[i1]); + + if ( is_more(phys1,phys0) ) + { + edge_node_id = i0; + } + else + { + edge_node_id = i1; + } + } + lcoords[i2] = lcoords[edge_node_id]; + } + + return edge_node_id; +} + +bool +ContourSubElement_Tet_4::is_degenerate( const std::vector & edge_node_ids, + const int i0, const int i1, const int i2, const int i3 ) +{ /* %TRACE% */ /* %TRACE% */ + + // DRN: This is really ugly, hand-optimized code for looking for degenerate tets + // Basically, it checks if any of the edges are degenerate. Then it has to look for degenerate + // faces that consist of 3 colinear points. These are handled by checking against + // the 6 specific bad cases. + + if ( edge_node_ids[i0] == edge_node_ids[i1] || + edge_node_ids[i0] == edge_node_ids[i2] || + edge_node_ids[i0] == edge_node_ids[i3] || + edge_node_ids[i1] == edge_node_ids[i2] || + edge_node_ids[i1] == edge_node_ids[i3] || + edge_node_ids[i2] == edge_node_ids[i3] ) + { + // this tet is degenerate with two coincident nodes + return true; + } + + if ( edge_node_ids[i0]==i0 && + edge_node_ids[i1]==i1 && + edge_node_ids[i2]==i2 && + edge_node_ids[i3]==i3 ) + { + // this tet is not degenerate since is has no degenerate nodes + return false; + } + + // look for a colinear face + std::vector is_used(10); // initializes to zero (false); + is_used[edge_node_ids[i0]] = true; + is_used[edge_node_ids[i1]] = true; + is_used[edge_node_ids[i2]] = true; + is_used[edge_node_ids[i3]] = true; + + if ((is_used[0] && ((is_used[1] && is_used[4]) || (is_used[2] && is_used[6]) || (is_used[3] && is_used[7]))) || + (is_used[1] && ((is_used[2] && is_used[5]) || (is_used[3] && is_used[8]))) || + (is_used[2] && is_used[3] && is_used[9])) + { + // this tet has a colinear face + return true; + } + + // look for all nodes on the same face + if ((!is_used[0] && !is_used[4] && !is_used[6] && !is_used[7]) || // all on face 1 + (!is_used[4] && !is_used[1] && !is_used[5] && !is_used[8]) || // all on face 2 + (!is_used[6] && !is_used[5] && !is_used[2] && !is_used[9]) || // all on face 0 + (!is_used[7] && !is_used[8] && !is_used[9] && !is_used[3])) // all on face 3 + { + // this tet has all nodes on one face + return true; + } + + return false; +} + +int +ContourSubElement_Tet_4::side_facets( Faceted_Surface & facets, + int side ) const +{ /* %TRACE% */ /* %TRACE% */ + ThrowAssert( my_side_ids[side] == -2 ); + + // just one linear facet per linear triangle + const int num_facets = 1; + + const unsigned * const lnn = get_side_node_ordinals(topology(), side); + + if ( LevelSet::sign_change(0.0, (double) my_sign) ) + { + std::unique_ptr facet = std::make_unique( my_owner->coordinates(my_coords[lnn[0]]), my_owner->coordinates(my_coords[lnn[1]]), my_owner->coordinates(my_coords[lnn[2]]) ); + facets.add( std::move(facet) ); + } + else + { + std::unique_ptr facet = std::make_unique( my_owner->coordinates(my_coords[lnn[0]]), my_owner->coordinates(my_coords[lnn[2]]), my_owner->coordinates(my_coords[lnn[1]]) ); + facets.add( std::move(facet) ); + } + + return( num_facets ); +} + +ContourSubElement_Tet_10::ContourSubElement_Tet_10( + const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner, + const int in_subelement_depth, + const int subelement_sign ) + : ContourSubElement( stk::topology::TETRAHEDRON_10, + coords, + side_ids, + in_owner, + in_subelement_depth, + subelement_sign ) +{ /* %TRACE% */ /* %TRACE% */ + + // Determine if we will continue to look for crossing within this element. + // This test should be conservative, proceeding to look for crossings if there + // is even a remote chance of a crossing (To avoid cracks in the surface). + + // find extrema + double max_dist = -std::numeric_limits::max(); + double min_dist = std::numeric_limits::max(); + for ( int n = 0; n < my_num_nodes; n++ ) + { + if (my_dist[n] < min_dist) min_dist = my_dist[n]; + if (my_dist[n] > max_dist) max_dist = my_dist[n]; + } + + const double variation = max_dist - min_dist; + + const bool all_hi = (min_dist - variation) > 0.0; + const bool all_lo = (max_dist + variation) < 0.0; + + if (all_hi || all_lo) + { + // correct the sign since we lie entirely on one side of the interface + my_sign = LevelSet::sign(my_dist[0]); + return; + } + + // use a single non-conformal, adaptive 4-noded tet + my_subelements.clear(); + my_subelements.reserve(1); + ContourSubElement *sub = NULL; + PointVec sub_coords(4,Vector3d::ZERO); + + sub_coords[0] = my_coords[0]; + sub_coords[1] = my_coords[1]; + sub_coords[2] = my_coords[2]; + sub_coords[3] = my_coords[3]; + + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, my_side_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); +} + +const int ContourSubElement_Adaptive_Tet_4::MAX_REFINMENT_LEVELS = 6; + +ContourSubElement_Adaptive_Tet_4::ContourSubElement_Adaptive_Tet_4( + const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner, + const int in_subelement_depth ) + : ContourSubElement( stk::topology::TETRAHEDRON_4, + coords, + side_ids, + in_owner, + in_subelement_depth, + 0 ) +{ /* %TRACE% */ /* %TRACE% */ + my_edge_age.resize(6); // initializes to zero + non_conformal_decomposition(); +} + +ContourSubElement_Adaptive_Tet_4::ContourSubElement_Adaptive_Tet_4( + const PointVec & coords, + const std::vector & side_ids, + const std::vector & edge_age, + const ContourElement * in_owner, + const int in_subelement_depth ) + : ContourSubElement( stk::topology::TETRAHEDRON_4, + coords, + side_ids, + in_owner, + in_subelement_depth, + 0 ) +{ /* %TRACE% */ /* %TRACE% */ + my_edge_age = edge_age; + non_conformal_decomposition(); +} + +int +ContourSubElement_Adaptive_Tet_4::non_conformal_decomposition() +{ /* %TRACE% */ /* %TRACE% */ + int success = true; // optimism + + // Determine if we will continue to look for crossing within this element. + // This test should be conservative, proceeding to look for crossings if there + // is even a remote chance of a crossing (To avoid cracks in the surface). + + // find extrema + double max_dist = -std::numeric_limits::max(); + double min_dist = std::numeric_limits::max(); + for ( int n = 0; n < my_num_nodes; n++ ) + { + if (my_dist[n] < min_dist) min_dist = my_dist[n]; + if (my_dist[n] > max_dist) max_dist = my_dist[n]; + } + + const double variation = max_dist - min_dist; + + const bool all_hi = (min_dist - variation) > 0.0; + const bool all_lo = (max_dist + variation) < 0.0; + + if (all_hi || all_lo) + { + // correct the sign since we lie entirely on one side of the interface + my_sign = LevelSet::sign(my_dist[0]); + return success; + } + + int longest_bad_edge = -1; + + // use temporary storage for vertex and side nodes + PointVec lcoords = my_coords; + lcoords.resize(10,Vector3d::ZERO); + lcoords[4] = 0.5 * (my_coords[0] + my_coords[1]); + lcoords[5] = 0.5 * (my_coords[1] + my_coords[2]); + lcoords[6] = 0.5 * (my_coords[2] + my_coords[0]); + lcoords[7] = 0.5 * (my_coords[0] + my_coords[3]); + lcoords[8] = 0.5 * (my_coords[1] + my_coords[3]); + lcoords[9] = 0.5 * (my_coords[2] + my_coords[3]); + + PointVec lphyscoords(10); + for (int n = 0; n < 10; ++n) + { + lphyscoords[n] = my_owner->coordinates( lcoords[n] ); + } + + std::vector ldist = my_dist; + ldist.resize(10); + for (int n = 4; n < 10; ++n) + { + ldist[n] = my_owner->distance( lcoords[n] ); + } + + const stk::topology Top = stk::topology::TETRAHEDRON_10; + int num_edges = Top.num_edges(); + + std::vector bad_edges; + bad_edges.reserve(num_edges); + + std::vector edge_lengths(num_edges); + + for ( int edge = 0; edge < num_edges; edge++ ) + { + const unsigned * const lnn = get_edge_node_ordinals(Top, edge); + + ThrowAssert(Top.edge_topology(edge).num_nodes() == 3); + + const double edge_straight_length = (lphyscoords[lnn[0]] - lphyscoords[lnn[1]]).length(); + ThrowRequire(edge_straight_length > 0.0); + edge_lengths[edge] = edge_straight_length; + + const double edge_curve_error = (lphyscoords[lnn[2]] - 0.5*(lphyscoords[lnn[0]] + lphyscoords[lnn[1]])).length(); + + const double edge_dist_error = std::fabs(ldist[lnn[2]] - 0.5*(ldist[lnn[0]]+ldist[lnn[1]])); + + const double scale = std::min(std::sqrt(std::numeric_limits::max()),std::fabs(ldist[lnn[0]]) + std::fabs(ldist[lnn[1]]) + my_owner->length_scale()); + + const double edge_error = (edge_curve_error + edge_dist_error)*edge_straight_length/(scale*scale); + + if (edge_error > my_owner->edge_nonlinear_tolerance() && my_edge_age[edge] < MAX_REFINMENT_LEVELS) + { + bad_edges.push_back(edge); + } + } + + double max_length = 0.0; + for (auto edge : bad_edges) + { + const double edge_length = edge_lengths[edge]; + ThrowRequire(edge_length > 0.0); + + // we need an absolute mechanism for selecting the edge to bisect so that all elements that share + // common edges will make the same decisions + if (utility::is_more(edge_length,max_length)) + { + longest_bad_edge = edge; + max_length = edge_length; + } + else if (!utility::is_less(edge_length,max_length)) // tie breaker + { + const Vector3d & edge_midside_coords = lphyscoords[get_edge_node_ordinals(Top, edge)[2]]; + // note that it is safe to assume that longest_bad_edge is already assigned if edge_length == max_length + const Vector3d longest_edge_midside_coords = lphyscoords[get_edge_node_ordinals(Top, longest_bad_edge)[2]]; + + ThrowAssert((utility::is_not_equal(edge_midside_coords[0],longest_edge_midside_coords[0]) || + utility::is_not_equal(edge_midside_coords[1],longest_edge_midside_coords[1]) || + utility::is_not_equal(edge_midside_coords[2],longest_edge_midside_coords[2]))); + + if (utility::is_more(edge_midside_coords[0],longest_edge_midside_coords[0]) || + (!utility::is_less(edge_midside_coords[0],longest_edge_midside_coords[0]) && + (utility::is_more(edge_midside_coords[1],longest_edge_midside_coords[1]) || + (!utility::is_less(edge_midside_coords[1],longest_edge_midside_coords[1]) && + (utility::is_more(edge_midside_coords[2],longest_edge_midside_coords[2])))))) + { + longest_bad_edge = edge; + max_length = edge_length; + } + } + } + + if ( longest_bad_edge == -1 ) + { + // no bad edges + + // use a single nonconformal linear tet subelement + my_subelements.clear(); + my_subelements.reserve(1); + + ContourSubElement *sub = new ContourSubElement_Tet_4( my_coords, my_side_ids, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + } + else + { + // + // create 2, adaptive, 4-noded tetrahedra by cutting the longest_bad_edge + // + + my_subelements.clear(); + my_subelements.reserve(2); + ContourSubElement *sub = NULL; + PointVec sub_coords(4,Vector3d::ZERO); + std::vector sub_ids(4); + std::vector sub_edge_age(6); + + static const unsigned node_permute_0[] = { 0,1,2,3 }; + static const unsigned node_permute_1[] = { 1,2,0,3 }; + static const unsigned node_permute_2[] = { 2,0,1,3 }; + static const unsigned node_permute_3[] = { 3,0,2,1 }; + static const unsigned node_permute_4[] = { 1,3,2,0 }; + static const unsigned node_permute_5[] = { 3,2,1,0 }; + static const unsigned side_permute_0[] = { 0,1,2,3 }; + static const unsigned side_permute_1[] = { 1,2,0,3 }; + static const unsigned side_permute_2[] = { 2,0,1,3 }; + static const unsigned side_permute_3[] = { 0,3,1,2 }; + static const unsigned side_permute_4[] = { 0,2,3,1 }; + static const unsigned side_permute_5[] = { 2,3,0,1 }; + static const unsigned edge_permute_0[] = { 0,1,2,3,4,5 }; + static const unsigned edge_permute_1[] = { 1,2,0,4,5,3 }; + static const unsigned edge_permute_2[] = { 2,0,1,5,3,4 }; + static const unsigned edge_permute_3[] = { 3,2,5,4,0,1 }; + static const unsigned edge_permute_4[] = { 4,5,1,0,3,2 }; + static const unsigned edge_permute_5[] = { 5,1,4,3,2,0 }; + static const unsigned * node_permute_table[] = { node_permute_0, node_permute_1, node_permute_2, node_permute_3, node_permute_4, node_permute_5 }; + static const unsigned * side_permute_table[] = { side_permute_0, side_permute_1, side_permute_2, side_permute_3, side_permute_4, side_permute_5 }; + static const unsigned * edge_permute_table[] = { edge_permute_0, edge_permute_1, edge_permute_2, edge_permute_3, edge_permute_4, edge_permute_5 }; + + const unsigned * lnn = node_permute_table[longest_bad_edge]; + const unsigned * lsn = side_permute_table[longest_bad_edge]; + const unsigned * len = edge_permute_table[longest_bad_edge]; + + const Vector3d edge_node = 0.5 * (my_coords[lnn[0]] + my_coords[lnn[1]]); + + // tet #1 + sub_coords[0] = my_coords[lnn[0]]; + sub_coords[1] = edge_node; + sub_coords[2] = my_coords[lnn[2]]; + sub_coords[3] = my_coords[lnn[3]]; + sub_ids[0] = my_side_ids[lsn[0]]; + sub_ids[1] = -1; /* not on any parent side */ + sub_ids[2] = my_side_ids[lsn[2]]; + sub_ids[3] = my_side_ids[lsn[3]]; + sub_edge_age[0] = my_edge_age[len[0]]+1; + sub_edge_age[1] = my_edge_age[len[0]]+1; + sub_edge_age[2] = my_edge_age[len[2]]; + sub_edge_age[3] = my_edge_age[len[3]]; + sub_edge_age[4] = my_edge_age[len[0]]+1; + sub_edge_age[5] = my_edge_age[len[5]]; + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, sub_edge_age, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + + // tet #2 + sub_coords[0] = edge_node; + sub_coords[1] = my_coords[lnn[1]]; + sub_coords[2] = my_coords[lnn[2]]; + sub_coords[3] = my_coords[lnn[3]]; + sub_ids[0] = my_side_ids[lsn[0]]; + sub_ids[1] = my_side_ids[lsn[1]]; + sub_ids[2] = -1; /* not on any parent side */ + sub_ids[3] = my_side_ids[lsn[3]]; + sub_edge_age[0] = my_edge_age[len[0]]+1; + sub_edge_age[1] = my_edge_age[len[1]]; + sub_edge_age[2] = my_edge_age[len[0]]+1; + sub_edge_age[3] = my_edge_age[len[0]]+1; + sub_edge_age[4] = my_edge_age[len[4]]; + sub_edge_age[5] = my_edge_age[len[5]]; + sub = new ContourSubElement_Adaptive_Tet_4( sub_coords, sub_ids, sub_edge_age, my_owner, my_subelement_depth+1 ); + my_subelements.push_back( sub ); + } + + return success; +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_ContourSubElement.hpp b/packages/krino/krino/krino_lib/Akri_ContourSubElement.hpp new file mode 100644 index 000000000000..b00b27325508 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_ContourSubElement.hpp @@ -0,0 +1,311 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_ContourSubElement_h +#define Akri_ContourSubElement_h + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace krino { + +template +bool compare_ptr_by_global_id (const T* i, const T* j) { return (i->global_id() < j->global_id()); } + +class ContourSubElement { +public: + + ContourSubElement( const stk::topology topo, + const PointVec & coords, + const std::vector & side_ids, + const ContourElement * owner, + const int in_subelement_depth, + const int subelement_sign ); + + virtual ~ContourSubElement(); + + static bool is_more(const Vector3d & v1, const Vector3d & v2); + + double relative_volume() const; + double side_relative_area( const int side ) const; + double parametric_quality() const; + double physical_quality() const; + double side_quality(const int side) const; + + int num_intg_pts( const int intg_pt_sign ); + + int gather_intg_pts( const int intg_pt_sign, + sierra::ArrayContainer & intg_pt_locations, + sierra::ArrayContainer & intg_weights, + sierra::ArrayContainer & determinants, + int index = 0 ); + + int build_facets( Faceted_Surface & facets ); + + // default implementation + virtual int side_facets( Faceted_Surface & facets, int side ) const; + + stk::topology topology() const { return my_master_element.get_topology(); } + + int spatial_dim() const { return my_owner->spatial_dim(); } + int num_subelements() const { return my_subelements.size(); } + + std::vector & get_side_ids() { return my_side_ids; } + const std::vector & get_side_ids() const { return my_side_ids; } + + const PointVec & get_coords() const { return my_coords; } + const ContourElement * owner() const { return my_owner; } + int subelement_depth() const { return my_subelement_depth; } + + void dump_structure() const; + void dump_details() const; + + bool have_interface_sides() const; + + virtual std::ostream & put( std::ostream& os ) const; + + friend std::ostream & operator << ( std::ostream &os , const ContourSubElement &s ) { + return s.put(os); + } + + static double find_quadratic_crossing( const double d0, + const double d1, + const double d2 ); + +protected: + + const MasterElement& my_master_element; + const MasterElement& my_side_master_element; + int my_num_nodes; + int my_num_sides; + PointVec my_coords; + std::vector my_side_ids; + std::vector my_dist; + std::vector< ContourSubElement * > my_subelements; + const ContourElement * my_owner; + int my_subelement_depth; // depth down the tree of subelements (0 for the base_subelement) + int my_sign; // -1 for elements on negative side, +1 for positive side, 0 for non-conformal elements spanning interface + +private: + //: Default constructor not allowed + ContourSubElement(); +}; + +class ContourSubElement_Quad_4 : public ContourSubElement { +public: + ContourSubElement_Quad_4( const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner ); + virtual ~ContourSubElement_Quad_4() {} + +private: + //: Default constructor not allowed + ContourSubElement_Quad_4(); + + int non_conformal_decomposition(); +}; + +class ContourSubElement_Quad_9 : public ContourSubElement { +public: + ContourSubElement_Quad_9( const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner ); + + virtual ~ContourSubElement_Quad_9() {} + +private: + //: Default constructor not allowed + ContourSubElement_Quad_9(); + + int non_conformal_decomposition(); +}; + +class ContourSubElement_Hex_8 : public ContourSubElement { +public: + ContourSubElement_Hex_8( const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner ); + virtual ~ContourSubElement_Hex_8() {} + +private: + //: Default constructor not allowed + ContourSubElement_Hex_8(); + + int subpyramid_non_conformal_decomposition( const int face ); +}; + +class ContourSubElement_Hex_27 : public ContourSubElement { +public: + ContourSubElement_Hex_27( const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner ); + virtual ~ContourSubElement_Hex_27() {} + +private: + //: Default constructor not allowed + ContourSubElement_Hex_27(); + + int subpyramid_non_conformal_decomposition( const int face ); +}; + +class ContourSubElement_Wedge_6 : public ContourSubElement { +public: + ContourSubElement_Wedge_6( const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner ); + virtual ~ContourSubElement_Wedge_6() {} + +private: + int subpyramid_non_conformal_decomposition( const int face ); +}; + +class ContourSubElement_Tri_3 : public ContourSubElement { +public: + ContourSubElement_Tri_3( const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner, + const int in_subelement_depth = 0, + const int subelement_sign = 0 ); + virtual ~ContourSubElement_Tri_3() {} + + virtual int side_facets( Faceted_Surface & facets, int side ) const; + +private: + //: Default constructor not allowed + ContourSubElement_Tri_3(); + + int conformal_decomposition(); + + int process_edge( const int i0, + const int i1, + const int i2, + std::vector & is_on_surf, + PointVec & lnodes, + const std::vector & ldist ); + + bool is_degenerate( const std::vector & edge_node_ids, + const int i0, const int i1, const int i2 ); +}; + +class ContourSubElement_Adaptive_Tri_3 : public ContourSubElement { +public: + ContourSubElement_Adaptive_Tri_3( const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner, + const int in_subelement_depth = 0); + ContourSubElement_Adaptive_Tri_3( const PointVec & coords, + const std::vector & side_ids, + const std::vector & edge_age, + const ContourElement * in_owner, + const int in_subelement_depth = 0); + virtual ~ContourSubElement_Adaptive_Tri_3() {} + +private: + //: Default constructor not allowed + ContourSubElement_Adaptive_Tri_3(); + + static const int MAX_REFINMENT_LEVELS; + + int non_conformal_decomposition(); + + std::vector my_edge_age; +}; + +class ContourSubElement_Tri_6 : public ContourSubElement { +public: + ContourSubElement_Tri_6( const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner, + const int in_subelement_depth = 0, + const int subelement_sign = 0 ); + virtual ~ContourSubElement_Tri_6() {} + +private: + //: Default constructor not allowed + ContourSubElement_Tri_6(); +}; + +class ContourSubElement_Tet_4 : public ContourSubElement { +public: + ContourSubElement_Tet_4( const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner, + const int in_subelement_depth = 0, + const int subelement_sign = 0 ); + virtual ~ContourSubElement_Tet_4() {} + + virtual int side_facets( Faceted_Surface & facets, int side ) const; + +private: + //: Default constructor not allowed + ContourSubElement_Tet_4(); + + int conformal_decomposition(); + + int process_edge( const int i0, + const int i1, + const int i2, + std::vector & is_on_surf, + PointVec & lnodes, + const std::vector & ldist ); + + bool is_degenerate( const std::vector & edge_node_ids, + const int i0, const int i1, const int i2, const int i3 ); +}; + +class ContourSubElement_Adaptive_Tet_4 : public ContourSubElement { +public: + ContourSubElement_Adaptive_Tet_4( const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner, + const int in_subelement_depth = 0); + ContourSubElement_Adaptive_Tet_4( const PointVec & coords, + const std::vector & side_ids, + const std::vector & edge_age, + const ContourElement * in_owner, + const int in_subelement_depth = 0); + virtual ~ContourSubElement_Adaptive_Tet_4() {} + +private: + //: Default constructor not allowed + ContourSubElement_Adaptive_Tet_4(); + + static const int MAX_REFINMENT_LEVELS; + + int non_conformal_decomposition(); + + std::vector my_edge_age; +}; + +class ContourSubElement_Tet_10 : public ContourSubElement { +public: + ContourSubElement_Tet_10( const PointVec & coords, + const std::vector & side_ids, + const ContourElement * in_owner, + const int in_subelement_depth = 0, + const int subelement_sign = 0 ); + virtual ~ContourSubElement_Tet_10() {} + +private: + //: Default constructor not allowed + ContourSubElement_Tet_10(); +}; + +} // namespace krino + +#endif // Akri_ContourSubElement_h diff --git a/packages/krino/krino/krino_lib/Akri_Cutting_Surface.cpp b/packages/krino/krino/krino_lib/Akri_Cutting_Surface.cpp new file mode 100644 index 000000000000..2cef540c7dd0 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Cutting_Surface.cpp @@ -0,0 +1,170 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include + +#include +#include + +namespace krino{ + +int +Plane_Cutting_Surface::sign_at_position(const Vector3d & p_coords) const +{ + return my_plane.signed_distance(p_coords) < 0. ? -1 : 1; +} + +bool +Plane_Cutting_Surface::on_surface(const Vector3d & p_coords, const double tol) const +{ + return std::abs(my_plane.signed_distance(p_coords)) < tol; +} + +double +Plane_Cutting_Surface::interface_crossing_position(const Segment3d & edge) const +{ + const double signed_dist_node0 = my_plane.signed_distance(edge.GetNode(0)); + const double signed_dist_node1 = my_plane.signed_distance(edge.GetNode(1)); + + if((signed_dist_node0 < 0) == (signed_dist_node1 < 0)) + { + std::stringstream str; + str << "Failed to find intersection of plane " << my_plane << " with segment " << edge; + throw std::runtime_error(str.str()); + } + + const double pos = signed_dist_node0 / (signed_dist_node0-signed_dist_node1); + + return pos; +} + +std::string Plane_Cutting_Surface::print() const +{ + std::ostringstream os; + os << "Plane cutting surface, plane = " << my_plane; + return os.str(); +} + +Intersecting_Planes_Cutting_Surface::Intersecting_Planes_Cutting_Surface(const Vector3d & p0, const Vector3d & p1, const Vector3d & p2, const Vector3d & p3) +: Cutting_Surface() +{ + // Points p0 and p2 lie on the line common to the two planes + my_plane0.set_from_most_orthogonal_angle_of_triangle(p0,p1,p2); + my_plane1.set_from_most_orthogonal_angle_of_triangle(p2,p3,p0); + + myTriangleForPlane0IsLarger = Cross(p2-p0, p1-p0).length_squared() > Cross(p2-p0, p3-p0).length_squared(); + + // Need to keep track of whether the normals to the plane point "toward" each other (positive_dihedral = true) + // or "away" from each other (positive_dihedral = false). + // We check this by seeing if p1 and p2 are on the positive side of the other plane. + const double dist1 = my_plane1.signed_distance(p1); + const double dist3 = my_plane0.signed_distance(p3); + my_positive_dihedral = (std::abs(dist1) > std::abs(dist3)) ? (dist1>0) : (dist3>0); +} + +int +Intersecting_Planes_Cutting_Surface::sign_at_position(const Vector3d & p_coords) const +{ + const double signed_dist0 = my_plane0.signed_distance(p_coords); + const double signed_dist1 = my_plane1.signed_distance(p_coords); + if (my_positive_dihedral) + { + const double min = std::min(signed_dist0,signed_dist1); + return min < 0. ? -1 : 1; + } + const double max = std::max(signed_dist0,signed_dist1); + return max < 0. ? -1 : 1; +} + +bool +Intersecting_Planes_Cutting_Surface::on_surface(const Vector3d & p_coords, const double tol) const +{ + const double signed_dist0 = my_plane0.signed_distance(p_coords); + const double signed_dist1 = my_plane1.signed_distance(p_coords); + if (my_positive_dihedral) + { + return (signed_dist0 > -tol && std::abs(signed_dist1) < tol) || + (signed_dist1 > -tol && std::abs(signed_dist0) < tol); + } + return (signed_dist0 < tol && std::abs(signed_dist1) < tol) || + (signed_dist1 < tol && std::abs(signed_dist0) < tol); +} + +double +Intersecting_Planes_Cutting_Surface::interface_crossing_position(const Segment3d & edge) const +{ + const double signed_dist0_node0 = my_plane0.signed_distance(edge.GetNode(0)); + const double signed_dist0_node1 = my_plane0.signed_distance(edge.GetNode(1)); + const double signed_dist1_node0 = my_plane1.signed_distance(edge.GetNode(0)); + const double signed_dist1_node1 = my_plane1.signed_distance(edge.GetNode(1)); + + double pos0 = signed_dist0_node0 / (signed_dist0_node0-signed_dist0_node1); + double pos1 = signed_dist1_node0 / (signed_dist1_node0-signed_dist1_node1); + + const int sign_case_id = + (((signed_dist0_node0 < 0) == my_positive_dihedral) ? 0 : 1) + + (((signed_dist1_node0 < 0) == my_positive_dihedral) ? 0 : 2) + + (((signed_dist0_node1 < 0) == my_positive_dihedral) ? 0 : 4) + + (((signed_dist1_node1 < 0) == my_positive_dihedral) ? 0 : 8); + static const int case_id_from_sign_case_id [] = {-1,-1,-1, 2,-1,-1,-1, 1,-1,-1,-1, 0, 3, 1, 0,-1}; + + const int case_id = case_id_from_sign_case_id[sign_case_id]; + switch (case_id) + { + case 0: return pos0; + case 1: return pos1; + case 2: return (pos0 < pos1) ? pos0 : pos1; + case 3: return (pos0 < pos1) ? pos1 : pos0; + } + + // Either 0 or 2 true crossings. Not ok. + std::stringstream str; + str << "Failed to find intersection of Intersecting_Planes_Cutting_Surface with planes " + << my_plane0 << " and " << my_plane1 + << " with segment " << edge + << " with crossings " << pos0 << " and " << pos1 << " with diff " << pos0-pos1 << " sign_case_id " << sign_case_id; + throw std::runtime_error(str.str()); +} + +std::string Intersecting_Planes_Cutting_Surface::print() const +{ + std::ostringstream os; + os << "Intersecting planes cutting surface. Plane 0 = " << my_plane0 << " Plane 1 = " << my_plane1 + << " positive dihedral = " << std::boolalpha << my_positive_dihedral; + return os.str(); +} + +std::string print_plane_visualization(const Plane3d & plane, std::array & geomIds, const std::string & description) +{ + std::ostringstream os; + const Vector3d x0 = plane.constant() * plane.normal(); + const Vector3d x1 = x0 + plane.normal(); + os << "Create vertex " << x0[0] << " " << x0[1] << " " << x0[2] << std::endl; + os << "Create vertex " << x1[0] << " " << x1[1] << " " << x1[2] << std::endl; + os << "Create curve vertex " << ++geomIds[0] << " " << ++geomIds[0] << std::endl; + os << "Create planar surface curve " << ++geomIds[1] << " distance 0. # Plane " << ++geomIds[2] << ": " << description << std::endl; + geomIds[0] += 4; // 4 vertices created for plane + geomIds[1] += 4; // 4 curves created for plane + return os.str(); +} + +std::string Plane_Cutting_Surface::print_visualization(std::array & geomIds, const std::string & description) const +{ + return print_plane_visualization(my_plane, geomIds, description); +} + +std::string Intersecting_Planes_Cutting_Surface::print_visualization(std::array & geomIds, const std::string & description) const +{ + const std::string viz0 = print_plane_visualization(my_plane0, geomIds, description+", plane 1/2"); + const std::string viz1 = print_plane_visualization(my_plane1, geomIds, description+", plane 2/2"); + return viz0 + viz1; +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_Cutting_Surface.hpp b/packages/krino/krino/krino_lib/Akri_Cutting_Surface.hpp new file mode 100644 index 000000000000..51e764864f50 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Cutting_Surface.hpp @@ -0,0 +1,81 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Cutting_Surface_h +#define Akri_Cutting_Surface_h + +#include +#include +#include +#include +#include + +namespace krino { + +class Cutting_Surface +{ +public: + Cutting_Surface() {} + virtual ~Cutting_Surface() {} + virtual bool on_surface(const Vector3d & p_coords, const double tol) const = 0; + virtual int sign_at_position(const Vector3d & p_coords) const = 0; + virtual double interface_crossing_position(const Segment3d & edge) const = 0; + virtual std::string print() const = 0; + virtual std::string print_visualization(std::array & geomIds, const std::string & description) const = 0; + virtual const Plane3d & get_plane() const = 0; +}; + +class Plane_Cutting_Surface : public Cutting_Surface +{ +public: + // for 3D from 3 points on plane + Plane_Cutting_Surface(const Vector3d & p0, const Vector3d & p1, const Vector3d & p2) : Cutting_Surface() {my_plane.set_from_most_orthogonal_angle_of_triangle(p0,p1,p2);} + // for 2D or 3D from direction and point on plane + Plane_Cutting_Surface(const Vector3d & direction, const Vector3d & p0) : my_plane(direction,p0) {} + virtual ~Plane_Cutting_Surface() {} + virtual bool on_surface(const Vector3d & p_coords, const double tol) const override; + int sign_at_position(const Vector3d & p_coords) const override; + double interface_crossing_position(const Segment3d & edge) const override; + std::string print() const override; + virtual std::string print_visualization(std::array & geomIds, const std::string & description) const override; + virtual const Plane3d & get_plane() const override { return my_plane; } +private: + Plane3d my_plane; +}; + +class Plane_Cutting_Surface_2D : public Plane_Cutting_Surface +{ +public: + // for 2D from 2 points on line + Plane_Cutting_Surface_2D(const Vector3d & p0, const Vector3d & p1) : Plane_Cutting_Surface(Vector3d(p1[1]-p0[1],p0[0]-p1[0],0),p0) {} + virtual ~Plane_Cutting_Surface_2D() {} +private: +}; + + +class Intersecting_Planes_Cutting_Surface : public Cutting_Surface +{ +public: + Intersecting_Planes_Cutting_Surface(const Vector3d & p0, const Vector3d & p1, const Vector3d & p2, const Vector3d & p3); // 3D + virtual ~Intersecting_Planes_Cutting_Surface() {} + virtual bool on_surface(const Vector3d & p_coords, const double tol) const override; + int sign_at_position(const Vector3d & p_coords) const override; + double interface_crossing_position(const Segment3d & edge) const override; + std::string print() const override; + virtual std::string print_visualization(std::array & geomIds, const std::string & description) const override; + virtual const Plane3d & get_plane() const override { /* THIS IS A BAD IDEA. SHOULD WE TEST PLANARITY? */ return myTriangleForPlane0IsLarger ? my_plane0 : my_plane1; } +private: + bool myTriangleForPlane0IsLarger; + Plane3d my_plane0; + Plane3d my_plane1; + bool my_positive_dihedral; +}; + +} // namespace krino + +#endif // Akri_Cutting_Surface_h diff --git a/packages/krino/krino/krino_lib/Akri_DecompositionHasChanged.cpp b/packages/krino/krino/krino_lib/Akri_DecompositionHasChanged.cpp new file mode 100644 index 000000000000..71bc5488dd90 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_DecompositionHasChanged.cpp @@ -0,0 +1,302 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../interface_geometry_interface/Akri_InterfaceGeometry.hpp" + +namespace krino { + +static bool owned_nodes_are_handled_by_other_procs(const stk::mesh::BulkData & mesh, + const std::vector & interfaceNodesWithoutMatchingCrossing, + const std::vector & interfaceNodesWithMatchingCrossing) +{ + stk::CommSparse commSparse(mesh.parallel()); + pack_entities_for_owning_proc(mesh, interfaceNodesWithMatchingCrossing, commSparse); + std::set ownedNodesWithRemoteMatchingCrossing; + unpack_entities_from_other_procs(mesh, ownedNodesWithRemoteMatchingCrossing, commSparse); + + for (auto node : interfaceNodesWithoutMatchingCrossing) + if (mesh.bucket(node).owned() && ownedNodesWithRemoteMatchingCrossing.count(node) == 0) + return false; + + return true; +} + +static bool any_node_of_edge_including_children_is_on_interface(const stk::mesh::BulkData & mesh, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + const ParentsToChildMapper & parentToChildMapper, + const EdgeIntersection & edgeCrossing) +{ + std::vector edgeNodesIncludingChildren; + fill_edge_nodes(mesh, edgeCrossing.nodes[0], edgeCrossing.nodes[1], parentToChildMapper, edgeNodesIncludingChildren); + for (auto node : edgeNodesIncludingChildren) + if (node_is_on_interface(mesh, cdfemSupport, phaseSupport, node, edgeCrossing.interface)) + return true; + return false; +} + +static bool edge_crossings_have_matching_interface_node(const stk::mesh::BulkData & mesh, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + const ParentsToChildMapper & parentToChildMapper, + const EdgeIntersection & edgeCrossing, + const double snapTol) +{ + const auto & edgeNodes = edgeCrossing.nodes; + const auto & crossingInterface = edgeCrossing.interface; + const double crossingPosition = edgeCrossing.crossingLocation; + if (crossingPosition < snapTol) + { + if (!node_is_on_interface(mesh, cdfemSupport, phaseSupport, edgeNodes[0], crossingInterface)) + return false; + } + else if (crossingPosition > 1.-snapTol) + { + if (!node_is_on_interface(mesh, cdfemSupport, phaseSupport, edgeNodes[1], crossingInterface)) + return false; + } + else if (!any_node_of_edge_including_children_is_on_interface(mesh, cdfemSupport, phaseSupport, parentToChildMapper, edgeCrossing)) + { + return false; + } + return true; +} + +static bool edges_with_crossings_have_matching_interface_nodes(const stk::mesh::BulkData & mesh, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + const ParentsToChildMapper & parentToChildMapper, + const std::vector & edgeIntersections) +{ + const double snapTol = cdfemSupport.get_snapper().get_edge_tolerance(); + bool edgesWithCrossingHaveMatchingInterfaceNodes = true; + for (auto && edgeIntersection : edgeIntersections) + { + const EdgeIntersection edge(edgeIntersection); + if (!edge_crossings_have_matching_interface_node(mesh, cdfemSupport, phaseSupport, parentToChildMapper, edge, snapTol)) + { + edgesWithCrossingHaveMatchingInterfaceNodes = false; + break; + } + } + return stk::is_true_on_all_procs(mesh.parallel(), edgesWithCrossingHaveMatchingInterfaceNodes); +} + +static std::map> build_nodes_to_interfaces_within_tolerance(const stk::mesh::BulkData & mesh, + const CDFEM_Support & cdfemSupport, + const std::vector & edgeIntersections, + const ParentsToChildMapper & parentToChildMapper) +{ + + const double snapTol = cdfemSupport.get_snapper().get_edge_tolerance(); + std::vector parentEdgeNodes; + std::vector parentEdgeNodePositions; + + std::map> nodesToInterfacesWithinTolerance; + for (auto && edgeIntersection : edgeIntersections) + { + const EdgeIntersection edgeCrossing(edgeIntersection); + const auto & crossingInterface = edgeCrossing.interface; + const double crossingPosition = edgeCrossing.crossingLocation; + + parentEdgeNodes.clear(); + parentEdgeNodePositions.clear(); + fill_edge_nodes_and_positions(mesh, edgeCrossing.nodes[0], edgeCrossing.nodes[1], parentToChildMapper, parentEdgeNodes, parentEdgeNodePositions); + + for (size_t iNode=0; iNode> & nodesToInterfacesWithinTolerance) +{ + const auto mapIter = nodesToInterfacesWithinTolerance.find(node); + if (mapIter == nodesToInterfacesWithinTolerance.end()) + return false; + + const PhaseTag nodePhase = determine_phase_for_entity(mesh, node, phaseSupport); + const auto & nodeInterfacesWithinTolerance = mapIter->second; + for (auto && interfaceWithinTolerance : nodeInterfacesWithinTolerance) + if (phase_matches_interface(cdfemSupport, nodePhase, interfaceWithinTolerance)) + return true; + + return false; +} + +static void fill_interface_nodes_with_and_without_matching_crossing(const stk::mesh::BulkData & mesh, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + const ParentsToChildMapper & parentToChildMapper, + const std::vector & edgeIntersections, + std::vector & interfaceNodesWithMatchingCrossing, + std::vector & interfaceNodesWithoutMatchingCrossing) +{ + interfaceNodesWithMatchingCrossing.clear(); + interfaceNodesWithoutMatchingCrossing.clear(); + + const std::map> nodesToInterfacesWithinTolerance = build_nodes_to_interfaces_within_tolerance(mesh, cdfemSupport, edgeIntersections, parentToChildMapper); + + for ( auto && bucket : mesh.buckets(stk::topology::NODE_RANK) ) + { + if (nodes_are_on_any_interface(mesh, phaseSupport, *bucket)) + { + for ( auto && node : *bucket ) + { + if (node_has_matching_interface_within_tolerance(mesh, cdfemSupport, phaseSupport, node, nodesToInterfacesWithinTolerance)) + interfaceNodesWithMatchingCrossing.push_back(node); + else + interfaceNodesWithoutMatchingCrossing.push_back(node); + } + } + } +} + +static bool interface_nodes_have_matching_edge_crossing(const stk::mesh::BulkData & mesh, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + const ParentsToChildMapper & parentToChildMapper, + const std::vector & edgeIntersections) +{ + std::vector interfaceNodesWithMatchingCrossing; + std::vector interfaceNodesWithoutMatchingCrossing; + fill_interface_nodes_with_and_without_matching_crossing(mesh, cdfemSupport, phaseSupport, parentToChildMapper, edgeIntersections, interfaceNodesWithMatchingCrossing, interfaceNodesWithoutMatchingCrossing); + + if (stk::is_true_on_all_procs(mesh.parallel(), interfaceNodesWithoutMatchingCrossing.empty())) + return true; + + bool allOwnedNotSharedInterfaceNodesHaveMatchingEdgeCrossing = true; + for (auto node : interfaceNodesWithoutMatchingCrossing) + { + if (mesh.bucket(node).owned() && !mesh.bucket(node).shared()) + { + allOwnedNotSharedInterfaceNodesHaveMatchingEdgeCrossing = false; + break; + } + } + + if (!stk::is_true_on_all_procs(mesh.parallel(), allOwnedNotSharedInterfaceNodesHaveMatchingEdgeCrossing)) + return false; + + const bool allInterfaceNodesHaveMatchingEdgeCrossing = + owned_nodes_are_handled_by_other_procs(mesh, interfaceNodesWithoutMatchingCrossing, interfaceNodesWithMatchingCrossing); + + return stk::is_true_on_all_procs(mesh.parallel(), allInterfaceNodesHaveMatchingEdgeCrossing); +} + +void fill_neighbor_nodes(const stk::mesh::BulkData & mesh, + stk::mesh::Entity node, + const stk::mesh::Selector & elementSelector, + std::vector & neighborNodes) +{ + neighborNodes.clear(); + for (auto element : StkMeshEntities{mesh.begin_elements(node), mesh.end_elements(node)}) + if (elementSelector(mesh.bucket(element))) + for (auto nbr : StkMeshEntities{mesh.begin_nodes(element), mesh.end_nodes(element)}) + if (nbr != node) + neighborNodes.push_back(nbr); + stk::util::sort_and_unique(neighborNodes); +} + +static bool snap_displacements_at_node_are_small_compared_to_parent_edges(const stk::mesh::BulkData & mesh, + stk::mesh::Entity node, + FieldRef cdfemSnapField, + FieldRef coordsField, + const stk::mesh::Selector & parentElementSelector, + const double snapTol) +{ + const Vector3d snapDisplacements(field_data(cdfemSnapField, node), mesh.mesh_meta_data().spatial_dimension()); + const double snapDisplacmentsSqrLength = snapDisplacements.length_squared(); + const Vector3d nodeCoords(field_data(coordsField, node), mesh.mesh_meta_data().spatial_dimension()); + std::vector neighborNodes; + fill_neighbor_nodes(mesh, node, parentElementSelector, neighborNodes); + for (auto && nbr : neighborNodes) + { + const Vector3d nbrCoords(field_data(coordsField, nbr), mesh.mesh_meta_data().spatial_dimension()); + const double edgeSqrLength = (nbrCoords-nodeCoords).length_squared(); + if (snapDisplacmentsSqrLength > snapTol*snapTol * edgeSqrLength) + return false; + } + return true; +} + +static bool locally_snap_displacements_are_small_on_nodes_with_interfaces(const stk::mesh::BulkData & mesh, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport) +{ + FieldRef cdfemSnapField = cdfemSupport.get_cdfem_snap_displacements_field(); + if (cdfemSnapField.valid()) + { + const double snapTol = cdfemSupport.get_snapper().get_edge_tolerance(); + FieldRef oldCdfemSnapField = cdfemSupport.get_cdfem_snap_displacements_field().field_state(stk::mesh::StateOld); + const stk::mesh::Selector parentElementSelector = get_owned_parent_element_selector(mesh, activePart, cdfemSupport, phaseSupport); + const FieldRef coordsField(mesh.mesh_meta_data().coordinate_field()); + for ( auto && bucket : mesh.buckets(stk::topology::NODE_RANK) ) + if (bucket->field_data_is_allocated(oldCdfemSnapField) && nodes_are_on_any_interface(mesh, phaseSupport, *bucket)) + for ( auto && node : *bucket ) + if (!snap_displacements_at_node_are_small_compared_to_parent_edges(mesh, node, oldCdfemSnapField, coordsField, parentElementSelector, snapTol)) + return false; + } + return true; +} + +static bool snap_displacements_are_small_on_nodes_with_interfaces(const stk::mesh::BulkData & mesh, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport) +{ + return stk::is_true_on_all_procs(mesh.parallel(), locally_snap_displacements_are_small_on_nodes_with_interfaces(mesh, activePart, cdfemSupport, phaseSupport)); +} + +bool decomposition_has_changed(const stk::mesh::BulkData & mesh, + const InterfaceGeometry & interfaceGeometry, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport) +{ + if (!snap_displacements_are_small_on_nodes_with_interfaces(mesh, activePart, cdfemSupport, phaseSupport)) + return true; + + const NodeToCapturedDomainsMap nodesToCapturedDomains; + const std::vector edgeIntersections = interfaceGeometry.get_edge_intersection_points(mesh, nodesToCapturedDomains); + + ParentsToChildMapper parentToChildMapper; + parentToChildMapper.build_map(mesh, activePart, cdfemSupport, phaseSupport); + + if (edges_with_crossings_have_matching_interface_nodes(mesh, cdfemSupport, phaseSupport, parentToChildMapper, edgeIntersections) && + interface_nodes_have_matching_edge_crossing(mesh, cdfemSupport, phaseSupport, parentToChildMapper, edgeIntersections)) + { + return false; + } + + return true; +} + + +} // namespace diff --git a/packages/krino/krino/krino_lib/Akri_DecompositionHasChanged.hpp b/packages/krino/krino/krino_lib/Akri_DecompositionHasChanged.hpp new file mode 100644 index 000000000000..f6aedb4fe6ad --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_DecompositionHasChanged.hpp @@ -0,0 +1,25 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_DECOMPOSITIONHASCHANGED_H_ +#define KRINO_INCLUDE_AKRI_DECOMPOSITIONHASCHANGED_H_ +#include +#include +#include +#include "../interface_geometry_interface/Akri_InterfaceGeometry.hpp" +namespace krino { + +bool decomposition_has_changed(const stk::mesh::BulkData & mesh, + const InterfaceGeometry & interfaceGeometry, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport); + +} + +#endif /* KRINO_INCLUDE_AKRI_DECOMPOSITIONHASCHANGED_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_DiagWriter.cpp b/packages/krino/krino/krino_lib/Akri_DiagWriter.cpp new file mode 100644 index 000000000000..6135a722231b --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_DiagWriter.cpp @@ -0,0 +1,54 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include + +#include + +namespace krino { + +DiagWriterParser & +theDiagWriterParser() +{ + /* %TRACE% */ /* %TRACE% */ + static DiagWriterParser parser; + return parser; +} + + +stk::diag::Writer & +theDiagWriter() +{ + /* %TRACE[NONE]% */ /* %TRACE% */ + static stk::diag::Writer s_diagWriter(sierra::dwout().rdbuf(), theDiagWriterParser().parse(std::getenv("KRINOLOG"))); + + return s_diagWriter; +} + +DiagWriterParser::DiagWriterParser() + : stk::diag::WriterParser() +{ + /* %TRACE% */ /* %TRACE% */ + mask("debug", (unsigned long) (LOG_DEBUG), "Display debug diagnostic information"); + mask("subelement", (unsigned long) (LOG_SUBELEMENT), "Display subelement decomposition diagnostic information"); + mask("facets", (unsigned long) (LOG_FACETS), "Output exodus file with facets data"); + mask("parts", (unsigned long) (LOG_PARTS), "Display CDFEM parts diagnostic information"); +} + +namespace +{ + +void bootstrap() { sierra::Diag::registerWriter("krinolog", krinolog, theDiagWriterParser()); } + +stk::Bootstrap x(&bootstrap); +} // namespace + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_DiagWriter.hpp b/packages/krino/krino/krino_lib/Akri_DiagWriter.hpp new file mode 100644 index 000000000000..915dd4b42ae7 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_DiagWriter.hpp @@ -0,0 +1,63 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_DiagWriter_h +#define Akri_DiagWriter_h + +#include +#include +#include +#include + +#include + +namespace krino +{ + class DiagWriterParser : public stk::diag::WriterParser + { + public: + DiagWriterParser(); + }; + + stk::diag::Writer &theDiagWriter(); + DiagWriterParser &theDiagWriterParser(); + +#define krinolog krino::theDiagWriter() + +#ifdef KRINO_TRACE_ENABLED + typedef stk::diag::Tracespec Tracespec; + typedef stk::diag::Traceback Traceback; + + class Trace : public stk::diag::Trace + { + public: + Trace(const char *message) + : stk::diag::Trace(krinolog, message) + {} + }; + +#elif defined(KRINO_TRACE_TRACEBACK) + typedef stk::diag::Tracespec Tracespec; + typedef stk::diag::Traceback Traceback; + typedef stk::diag::Traceback Trace; + +#else + typedef stk::diag::Tracespec Tracespec; + typedef stk::diag::Tracespec Traceback; + typedef stk::diag::Tracespec Trace; +#endif + +#define ThrowRuntimeError(message) \ +{ \ + std::ostringstream internal_throw_runtime_message; \ + internal_throw_runtime_message << message; \ + throw std::runtime_error(internal_throw_runtime_message.str()); \ +} +} // namespace krino + +#endif // Akri_DiagWriter_h diff --git a/packages/krino/krino/krino_lib/Akri_DiagWriter_fwd.hpp b/packages/krino/krino/krino_lib/Akri_DiagWriter_fwd.hpp new file mode 100644 index 000000000000..d5aa04dc9174 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_DiagWriter_fwd.hpp @@ -0,0 +1,37 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_DiagWriter_fwd_h +#define Akri_DiagWriter_fwd_h + +#define KRINO_TRACE_ENABLED +// #define KRINO_TRACE_TRACEBACK + +#include + +namespace krino +{ + class DebugOut; + + enum + { + LOG_ALWAYS = stk::LOG_ALWAYS, + LOG_MEMBERS = stk::LOG_MEMBERS, + LOG_TRACE = stk::LOG_TRACE, + LOG_TRACE_STATS = stk::LOG_TRACE_STATS, + LOG_TRACE_SUB_CALLS = stk::LOG_TRACE_SUB_CALLS, + + LOG_DEBUG = 0x00010000, + LOG_SUBELEMENT = 0x00020000, + LOG_FACETS = 0x00040000, + LOG_PARTS = 0x00080000 + }; + +} // namespace krino + +#endif // Akri_DiagWriter_fwd_h diff --git a/packages/krino/krino/krino_lib/Akri_DistanceSweeper.cpp b/packages/krino/krino/krino_lib/Akri_DistanceSweeper.cpp new file mode 100644 index 000000000000..49a96dc635ea --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_DistanceSweeper.cpp @@ -0,0 +1,177 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace krino{ + +namespace { + +bool node_has_other_sign(stk::mesh::Entity node, const FieldRef distance_field, const double signed_narrow_band) +{ + double * distance = field_data(distance_field, node); + ThrowAssert(nullptr != distance); + return (signed_narrow_band < 0.) ? (*distance > 0.) : (*distance < 0.); +} + +bool node_is_inside_narrow_band(stk::mesh::Entity node, const FieldRef distance_field, const double signed_narrow_band) +{ + double * distance = field_data(distance_field, node); + ThrowAssert(nullptr != distance); + return (signed_narrow_band < 0.) ? (*distance > signed_narrow_band) : (*distance < signed_narrow_band); +} + +void get_nodes_ready_to_update(const AuxMetaData & aux_meta, const stk::mesh::BulkData& mesh, const FieldRef distance_field, const double signed_narrow_band, std::set & nodes_ready_to_update) +{ + stk::mesh::Selector field_not_ghost = aux_meta.active_not_ghost_selector() & stk::mesh::selectField(distance_field); + const stk::mesh::BucketVector & buckets = mesh.get_buckets( stk::topology::ELEMENT_RANK, field_not_ghost ); + std::vector elem_nodes_to_update; + + for ( auto && bucket : buckets ) + { + stk::topology elem_topology = bucket->topology(); + const unsigned num_nodes = elem_topology.num_nodes(); + + for ( auto && elem : *bucket ) + { + elem_nodes_to_update.clear(); + const stk::mesh::Entity* nodes = mesh.begin_nodes(elem); + bool have_node_with_other_sign = false; + for ( unsigned i = 0; i < num_nodes; ++i ) + { + stk::mesh::Entity node = nodes[i]; + if (has_field_data(distance_field, node)) + { + if (node_has_other_sign(node, distance_field, signed_narrow_band)) + { + have_node_with_other_sign = true; + } + else if (!node_is_inside_narrow_band(node, distance_field, signed_narrow_band)) + { + elem_nodes_to_update.push_back(node); + } + } + } + if (have_node_with_other_sign) + { + nodes_ready_to_update.insert(elem_nodes_to_update.begin(), elem_nodes_to_update.end()); + } + } + } +} + +void get_neighbor_nodes_ready_to_update(const AuxMetaData & aux_meta, + const stk::mesh::BulkData& mesh, + const FieldRef distance_field, + const double signed_narrow_band, + const bool check_if_nbr_is_outside_band, + stk::mesh::Entity node, + std::set & nodes_ready_to_update) +{ + const stk::mesh::Entity* node_elems = mesh.begin_elements(node); + const unsigned num_node_elements = mesh.num_elements(node); + + for (unsigned ielem = 0; ielem < num_node_elements; ++ ielem) + { + stk::mesh::Entity elem = node_elems[ielem]; + const unsigned num_nodes = mesh.num_nodes(elem); + const stk::mesh::Entity* elem_nodes = mesh.begin_nodes(elem); + for ( unsigned inode = 0; inode < num_nodes; ++inode ) + { + stk::mesh::Entity nbr_node = elem_nodes[inode]; + if (nbr_node != node && + has_field_data(distance_field, nbr_node) && + !node_has_other_sign(nbr_node, distance_field, signed_narrow_band) && + (!check_if_nbr_is_outside_band || !node_is_inside_narrow_band(nbr_node, distance_field, signed_narrow_band))) + { + nodes_ready_to_update.insert(nbr_node); + } + } + } +} + +void get_shared_nodes_ready_to_update(const AuxMetaData & aux_meta, const stk::mesh::BulkData& mesh, const FieldRef distance_field, const double signed_narrow_band, std::set & nodes_ready_to_update) +{ + stk::mesh::Selector field_globally_shared = mesh.mesh_meta_data().globally_shared_part() & stk::mesh::selectField(distance_field); + std::vector< stk::mesh::Entity> shared_nodes; + stk::mesh::get_selected_entities( field_globally_shared, mesh.buckets( stk::topology::NODE_RANK ), shared_nodes ); + const bool check_if_nbr_is_outside_band = true; + + for (auto && node : shared_nodes) + { + if (node_has_other_sign(node, distance_field, signed_narrow_band)) + { + get_neighbor_nodes_ready_to_update(aux_meta, mesh, distance_field, signed_narrow_band, check_if_nbr_is_outside_band, node, nodes_ready_to_update); + } + } +} + +} // anonymous + +void DistanceSweeper::fix_sign_by_sweeping(const stk::mesh::BulkData& mesh, const FieldRef distance_field, const double signed_narrow_band) +{ + const AuxMetaData & aux_meta = AuxMetaData::get(mesh.mesh_meta_data()); + if (signed_narrow_band == 0.0) return; + const int sign_to_fix = (signed_narrow_band < 0.) ? -1 : 1; + + std::set nodes_ready_to_update; + + get_nodes_ready_to_update(aux_meta, mesh, distance_field, signed_narrow_band, nodes_ready_to_update); + + bool done = false; + while (!done) + { + size_t num_nodes_updated = 0; + + const bool check_if_nbr_is_outside_band = false; + while (!nodes_ready_to_update.empty()) + { + ++num_nodes_updated; + stk::mesh::Entity node = *nodes_ready_to_update.begin(); + nodes_ready_to_update.erase(nodes_ready_to_update.begin()); + + double * distance = field_data(distance_field, node); + ThrowAssert(nullptr != distance); + *distance = -signed_narrow_band; + + get_neighbor_nodes_ready_to_update(aux_meta, mesh, distance_field, signed_narrow_band, check_if_nbr_is_outside_band, node, nodes_ready_to_update); + } + + done = true; + if (mesh.parallel_size() > 1) + { + const size_t local_num_nodes_updated = num_nodes_updated; + stk::all_reduce_sum(mesh.parallel(), &local_num_nodes_updated, &num_nodes_updated, 1); + + if (num_nodes_updated > 0) + { + done = false; + + std::vector parallel_fields(1, &distance_field.field()); + if (sign_to_fix > 0) stk::mesh::parallel_min(mesh, parallel_fields); + else stk::mesh::parallel_max(mesh, parallel_fields); + + get_shared_nodes_ready_to_update(aux_meta, mesh, distance_field, signed_narrow_band, nodes_ready_to_update); + } + } + } +} + +//---------------------------------------------------------------- +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_DistanceSweeper.hpp b/packages/krino/krino/krino_lib/Akri_DistanceSweeper.hpp new file mode 100644 index 000000000000..051cf9dc37cb --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_DistanceSweeper.hpp @@ -0,0 +1,22 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_DistanceSweeper_h +#define Akri_DistanceSweeper_h + +namespace stk { namespace mesh { class BulkData; } } +namespace krino { class FieldRef; } + +namespace krino { +namespace DistanceSweeper { + + void fix_sign_by_sweeping(const stk::mesh::BulkData& mesh, const FieldRef distance_field, const double signed_narrow_band); + +}} + +#endif // Akri_DistanceSweeper_h diff --git a/packages/krino/krino/krino_lib/Akri_Element.cpp b/packages/krino/krino/krino_lib/Akri_Element.cpp new file mode 100644 index 000000000000..3eb348e43e1a --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Element.cpp @@ -0,0 +1,1077 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../interface_geometry_interface/Akri_InterfaceGeometry.hpp" +#include +namespace krino{ + +ElementObj::ElementObj(const stk::mesh::BulkData & stkMesh, stk::mesh::Entity elemEntity) + : my_master_elem(MasterElementDeterminer::getMasterElement(stkMesh.bucket(elemEntity).topology())), + my_entity(elemEntity), + my_entityId(stkMesh.identifier(elemEntity)), + my_prolongation_data(NULL) +{ +} + +ElementObj::ElementObj(const stk::topology elem_topology, const NodeVec & nodes) + : my_master_elem(MasterElementDeterminer::getMasterElement(elem_topology)), + my_nodes(nodes), + my_entity(), + my_entityId(0), + my_prolongation_data(NULL) +{ +} + +ElementObj::~ElementObj() {} + +void +ElementObj::integration_locations( + std::vector & intg_pt_locations, + const MasterElement & me) +{ + const unsigned num_intg_pts = me.num_intg_pts(); + const double * intg_pt_loc_ptr = me.intg_pt_locations(); + const unsigned dim = me.topology_dimension(); + + intg_pt_locations.resize(num_intg_pts); + for(unsigned i=0; i & intg_weights, // includes both gauss point weight and detJ + const int numCoordDims, + const std::vector & mesh_coords, + const MasterElement & me, + const MasterElement & mesh_me) +{ + const unsigned num_intg_pts = me.num_intg_pts(); + const unsigned dim = me.topology_dimension(); + + std::vector det_J(num_intg_pts); + double det_J_error; + + if ( me.get_topology() == mesh_me.get_topology() ) + { + mesh_me.determinant( numCoordDims, 1, mesh_coords.data(), det_J.data(), &det_J_error ); + } + else + { + const int num_coord_dofs = mesh_me.get_topology().num_nodes(); + const double * intg_pt_loc_ptr = me.intg_pt_locations(); + std::vector d_shapef_coords(num_coord_dofs*dim*num_intg_pts); + mesh_me.shape_fcn_deriv(num_intg_pts, intg_pt_loc_ptr, d_shapef_coords.data()); + mesh_me.determinant( + numCoordDims, // Number of coordinate dimensions + num_intg_pts, // Number of target points + num_coord_dofs, // Number of coord shape functions + d_shapef_coords.data(), // Mesh shape function derivatives + 1, // Number of elements + mesh_coords.data(), // Mesh coordinate values + det_J.data(), // Determinant of the transformation Jacobian for each element (output) + &det_J_error ); // Determinant error (output) + } + + const double * intg_wt_ptr = me.intg_weights(); + + intg_weights.resize(num_intg_pts); + for(unsigned i=0; i & result, unsigned dim, unsigned npe) +{ + if (npe == 0) + { + npe = mesh.bucket(element).topology().num_nodes(); + } + result.resize(npe*dim); + + const stk::mesh::Entity* elem_nodes = mesh.begin_nodes(element); + + for (unsigned n=0; n(field, elem_nodes[n]); + for (unsigned d=0; d coords; + ElementObj::gather_nodal_field(mesh, element, coords_field, coords, dim); + + const MasterElement& master_elem = MasterElementDeterminer::getMasterElement(mesh.bucket(element).topology()); + + std::vector intg_weights; + ElementObj::integration_weights( intg_weights, dim, coords, master_elem ); + + double vol = 0.0; + for ( double intg_wt : intg_weights ) + { + vol += intg_wt; + } + return vol; +} + +PhaseTag +ElementObj::update_phase(const CDMesh & mesh, const PhaseTag & startPhase, const InterfaceID interface_key, const int sign) +{ + PhaseTag phase = startPhase; + if (sign == -1 || sign == 1) + { + if (interface_key.is_single_ls()) + { + phase.add(mesh.ls_field(interface_key.first_ls()).identifier,sign); + } + else + { + const int other_ls_id = (sign < 0) ? interface_key.second_ls() : interface_key.first_ls(); + if (phase.contain(mesh.ls_field(other_ls_id).identifier, -1)) + { + const int ls_id = (sign < 0) ? interface_key.first_ls() : interface_key.second_ls(); + + phase.clear(); + phase.add(mesh.ls_field(ls_id).identifier, -1); + } + } + } + + return phase; +} + +PhaseTag +ElementObj::update_phase(const CDMesh & mesh, const PhaseTag & startPhase, const std::vector & interfaces, const std::vector & interfaceSigns) +{ + PhaseTag phase = startPhase; + const int numInterfaces = interfaces.size(); + ThrowRequire((int)interfaceSigns.size() == numInterfaces); + + if (numInterfaces > 0) + { + bool badConfig = false; + int iterCount = 0; + while (true) + { + PhaseTag iterationPhase = phase; + for (int i=0; i numInterfaces) + { + krinolog << "BAD INTERFACE CROSSING CONFIG WITH STARTING PHASE: " << startPhase << stk::diag::dendl; + for (int i=0; i & nodeOwnerCoords) const +{ + nodeOwnerCoords.clear(); + nodeOwnerCoords.reserve(my_nodes.size()); + + for ( auto && node : my_nodes ) + nodeOwnerCoords.push_back(node->owner_coords(owner)); +} + +Vector3d +ElementObj::compute_local_coords_from_owner_coordinates(const Mesh_Element * owner, const Vector3d & ptOwnerCoords) const +{ + std::vector nodeOwnerCoords; + fill_node_owner_coords(owner, nodeOwnerCoords); + + ThrowAssert((spatial_dim() == 2 && (nodeOwnerCoords.size() == 3 || nodeOwnerCoords.size() == 6)) || + (spatial_dim() == 3 && (nodeOwnerCoords.size() == 4 || nodeOwnerCoords.size() == 10))); + if (spatial_dim() == 2 && nodeOwnerCoords.size() == 6) + nodeOwnerCoords.resize(3); + else if (spatial_dim() == 3 && nodeOwnerCoords.size() == 10) + nodeOwnerCoords.resize(4); + + return get_parametric_coordinates_of_point(nodeOwnerCoords, ptOwnerCoords); +} + +void +ElementObj::integration_weights( + std::vector & intg_weights, // includes both gauss point weight and detJ + const MasterElement & me ) const +{ + const unsigned dim = spatial_dim(); + const unsigned nnodes = my_nodes.size(); + std::vector flat_coords(nnodes*dim); + for ( unsigned n = 0; n < nnodes; n++ ) + { + const Vector3d & node_coords = my_nodes[n]->coordinates(); + for (unsigned d=0; d intg_weights; + integration_weights( intg_weights, my_master_elem ); + + double vol = 0.0; + + for ( double intg_weight : intg_weights ) + { + vol += intg_weight; + } + return vol; +} + +void ElementObj::add_subelement(std::unique_ptr subelem) +{ + my_subelements.emplace_back(std::move(subelem)); +} + +void ElementObj::get_subelements( std::vector & subelems ) const +{ + if ( !my_subelements.empty() ) + { + for ( auto && subelem : my_subelements ) + { + subelem->get_subelements(subelems); + } + return; + } + + const SubElement * subelem = dynamic_cast( this ); + if (NULL != subelem) subelems.push_back(subelem); +} + +void ElementObj::get_subelements( std::vector & subelems ) +{ + if ( !my_subelements.empty() ) + { + for ( auto && subelem : my_subelements ) + { + subelem->get_subelements(subelems); + } + return; + } + + SubElement * subelem = dynamic_cast( this ); + if (NULL != subelem) subelems.push_back(subelem); +} + +bool +ElementObj::have_refined_edges() const +{ /* %TRACE% */ /* %TRACE% */ + + const stk::topology Top = topology(); + const int num_edges = Top.num_edges(); + + for ( int edge = 0; edge < num_edges; edge++ ) + { + const unsigned * const lnn = get_edge_node_ordinals(Top, edge); + + const int num_edge_nodes = Top.edge_topology(edge).num_nodes(); + ThrowRequire(2 == num_edge_nodes || 3 == num_edge_nodes); + + if ((2 == num_edge_nodes && + NULL != SubElementNode::common_child({my_nodes[lnn[0]], my_nodes[lnn[1]]})) || + (3 == num_edge_nodes && + (NULL != SubElementNode::common_child({my_nodes[lnn[0]], my_nodes[lnn[2]]}) || + NULL != SubElementNode::common_child({my_nodes[lnn[1]], my_nodes[lnn[2]]})))) + { + return true; + } + } + return false; +} + +void ElementObj::cut_interior_intersection_point(CDMesh & mesh, const Vector3d & pCoords, const std::vector & sortedDomains) +{ + throw std::runtime_error("Incorrect usage of ElementObj. The type of element cannot cut_interior_intersection_point."); +} + +void ElementObj::cut_face_intersection_point(const int iFace, const Vector3d & pCoords, const std::vector & sortedDomains) +{ + throw std::runtime_error("Incorrect usage of ElementObj. The type of element cannot cut_face_intersection_point."); +} + +bool ElementObj::captures_intersection_point_domains(const std::vector & intersectionPointDomains) const +{ + for (auto && node : my_nodes) + if (node->captures_intersection_point_domains(intersectionPointDomains)) + return true; + return false; +} + +void +ElementObj::prolongate_fields(const CDMesh & mesh) const +{ + const CDMesh* old_mesh = mesh.get_old_mesh(); + if (nullptr != old_mesh) + { + const FieldSet & element_fields = mesh.get_element_fields(); + if (element_fields.empty()) return; + + const ProlongationElementData * prolong_element = NULL; + const SubElement * subelem = dynamic_cast(this); + if (NULL == subelem) + { + prolong_element = old_mesh->fetch_prolong_element(entityId()); + } + else + { + prolong_element = old_mesh->fetch_prolong_element(subelem->get_owner().entityId()); + } + + //I think that adaptivity currently can lead to prolong_element == NULL: ThrowAssert(NULL != prolong_element); + + for(FieldSet::const_iterator it = element_fields.begin(); it != element_fields.end(); ++it) + { + const FieldRef field = *it; + + double * val = field_data(field, entity()); + if (NULL == val) continue; + + const unsigned field_length = field.length(); + + const double * owner_val = (NULL == prolong_element) ? NULL : prolong_element->get_field_data(field); + if (NULL == owner_val) + { + std::fill(val, val+field_length, 0.); + } + else + { + std::copy(owner_val, owner_val+field_length, val); + } + } + } +} + +const MasterElement* +ElementObj::get_evaluation_master_element(const FieldRef field) const +{ /* %TRACE% */ /* %TRACE% */ + // Supports Q1Q1, Q2Q2, and Q2Q1 cases + const MasterElement* calc_master_elem = &master_elem(); + const unsigned full_npe = master_elem().get_topology().num_nodes(); + ThrowAssert(field.type_is()); + + for ( unsigned n = 0; n < full_npe; n++ ) + { + double * data = field_data(field, my_nodes[n]->entity()); + if (nullptr == data) + { + calc_master_elem = &MasterElementDeterminer::getMasterElement(master_elem().get_topology().base()); + ThrowRequire(n >= calc_master_elem->num_nodes()); + break; + } + } + return calc_master_elem; +} + +void +ElementObj::evaluate_prolongation_field(const CDMesh & mesh, const FieldRef field, const unsigned field_length, const Vector3d & p_coords, double * result) const +{ /* %TRACE% */ /* %TRACE% */ + + // Figuring out the field master element here is actually quite hard since the entity may not exist any more. + // We'll assume that the field master element is the master_elem or the one with topology master_elem->get_topology().base(). + // This will handle the Q2Q1 case. + + for (unsigned i=0; i node_data(full_npe, nullptr); + const std::vector zeros(field_length,0.0); + + for ( int n = 0; n < full_npe; n++ ) + { + const ProlongationNodeData * prolong_data = mesh.fetch_prolong_node(my_nodes[n]->entityId()); + if (nullptr != prolong_data) node_data[n] = prolong_data->get_field_data(field); + if (node_data[n] == nullptr) + { + if (!initial_field.valid()) + { + initial_field = mesh.get_cdfem_support().get_initial_prolongation_field( field ); + } + if (initial_field.valid()) + { + node_data[n] = prolong_data->get_field_data(initial_field); + } + } + if (node_data[n] == nullptr) + { + calc_master_elem = &MasterElementDeterminer::getMasterElement(master_elem().get_topology().base()); + node_data[n] = zeros.data(); + } + } + + const int npe = calc_master_elem->get_topology().num_nodes(); + std::vector shapefcn (npe,0.); + calc_master_elem->shape_fcn(1, p_coords.data(), shapefcn.data()); + + for ( int n = 0; n < npe; n++ ) + { + ThrowRequire(nullptr != node_data[n]); + for (unsigned i=0; idetermine_decomposed_elem_phase(mesh); + } + // Phase for Mesh_Element with subelements is left empty + return; + } +} + +static std::function &)> +cut_element_intersecting_planes_diagonal_picker(const NodeVec & cutElemNodes) +{ + auto intersectingPlanesDiagonalPicker = + [&cutElemNodes](const std::array & faceNodes) + { + return ElementObj::determine_diagonal_for_internal_quad_of_cut_tet_from_owner_nodes(cutElemNodes[faceNodes[0]], cutElemNodes[faceNodes[1]], cutElemNodes[faceNodes[2]], cutElemNodes[faceNodes[3]]); + }; + return intersectingPlanesDiagonalPicker; +} + +std::function &)> +Mesh_Element::get_diagonal_picker() const +{ + return cut_element_intersecting_planes_diagonal_picker(my_nodes); +} + + +bool +Mesh_Element::is_single_coincident() const +{ + std::vector conformal_subelems; + get_subelements(conformal_subelems); + if(conformal_subelems.size() != 1) return false; + + const SubElement * subelem = conformal_subelems[0]; + if(subelem->topology() != coord_topology()) return false; + if (get_nodes() != subelem->get_nodes()) return false; + return true; +} + +Mesh_Element::~Mesh_Element() {} + +Mesh_Element::Mesh_Element(CDMesh & mesh, + stk::mesh::Entity elemEntity) + : ElementObj(mesh.stk_bulk(), elemEntity), + my_subelement_order(0), + my_have_interface(false) +{ + // set subelement specs + const stk::topology me_topology = my_master_elem.get_topology(); + + std::tie(my_subelement_topology, my_subelement_order) = determine_subelement_topology(me_topology); + ThrowRequire(stk::topology::INVALID_TOPOLOGY != my_subelement_topology); + + const unsigned npe_coords = me_topology.num_nodes(); + const stk::mesh::Entity* elem_nodes = mesh.stk_bulk().begin_nodes(my_entity); + + my_nodes.reserve(npe_coords); + const unsigned npe_base = me_topology.base().num_nodes(); + for (unsigned i=0; i npe_base) + { + my_nodes.resize(npe_coords); + ThrowRequireMsg(npe_coords == npe_base + me_topology.num_edges(), "Unexpected topology"); + for (unsigned edge_i=0; edge_i= npe_base && inode +Mesh_Element::determine_subelement_topology(stk::topology elem_topology) +{ + switch(elem_topology()) + { + case stk::topology::TRIANGLE_3_2D: + return std::pair(stk::topology::TRIANGLE_3_2D, 1); + case stk::topology::TRIANGLE_6_2D: + return std::pair(stk::topology::TRIANGLE_6_2D, 2); + case stk::topology::TETRAHEDRON_4: + return std::pair(stk::topology::TETRAHEDRON_4, 1); + case stk::topology::TETRAHEDRON_10: + return std::pair(stk::topology::TETRAHEDRON_10, 2); + default: + return std::pair(stk::topology::INVALID_TOPOLOGY, 0); + } +} + +Vector3d +Mesh_Element::get_node_parametric_coords( const int lnn ) const +{ + const double * nodal_parametric_coordinates = my_master_elem.nodal_parametric_coordinates(); + const int dim = spatial_dim(); + return Vector3d(&nodal_parametric_coordinates[lnn*dim],dim); +} + +static int get_local_node_number( const NodeVec & nodes, const SubElementNode * node ) +{ + for (size_t iNode=0; iNode conformal_subelems; + get_subelements(conformal_subelems); + + double minSqrDist = std::numeric_limits::max(); + for ( auto&& subelement : conformal_subelems ) + { + const Vector3d currentChildPCoords = subelement->compute_local_coords_from_owner_coordinates(this, ownerCoordinates); + const double currentChildSqrDist = compute_parametric_square_distance(currentChildPCoords); + if (currentChildSqrDist < minSqrDist) + { + minSqrDist = currentChildSqrDist; + child = subelement; + childPCoords = currentChildPCoords; + } + } +} + +Vector3d +Mesh_Element::coordinates( const Vector3d & p_coords ) const +{ /* %TRACE% */ /* %TRACE% */ + + const int npeCoords = my_nodes.size(); + std::vector shapeFcn(npeCoords); + my_master_elem.shape_fcn(1, p_coords.data(), shapeFcn.data()); + + Vector3d coords(Vector3d::ZERO); + for ( int n = 0; n < npeCoords; n++ ) + coords += shapeFcn[n] * my_nodes[n]->coordinates(); + + return coords; +} + +std::string Mesh_Element::visualize(const CDMesh & mesh) const +{ + std::ostringstream os; + os << "Debugging mesh element " << entityId() << " with cutter:\n" << myCutter->visualize(mesh.stk_bulk()) << "\n"; + return os.str(); +} + +double +Mesh_Element::interface_crossing_position(const InterfaceID interface, const Segment3d & edge) const +{ + ThrowRequire(get_cutter()); + return get_cutter()->interface_crossing_position(interface, edge); +} + +static ElementIntersectionPointFilter build_element_intersection_filter(const NodeVec & nodes) +{ + auto filter = + [&nodes](const std::vector & intersectionPointDomains) + { + for (auto && node : nodes) + if (node->captures_intersection_point_domains(intersectionPointDomains)) + return false; + return true; + }; + return filter; +} + +void +Mesh_Element::fill_face_interior_intersections(const NodeVec & faceNodes, const InterfaceID & interface1, const InterfaceID & interface2, std::vector & faceIntersectionPoints) const +{ + ThrowRequire(get_cutter() && faceNodes.size() == 3); + const std::array faceNodeOwnerCoords = {{faceNodes[0]->owner_coords(this), faceNodes[1]->owner_coords(this), faceNodes[2]->owner_coords(this)}}; + const ElementIntersectionPointFilter intersectionPointFilter = build_element_intersection_filter(faceNodes); + get_cutter()->fill_tetrahedron_face_interior_intersections(faceNodeOwnerCoords, interface1, interface2, intersectionPointFilter, faceIntersectionPoints); +} + +int +Mesh_Element::interface_node_sign(const InterfaceID interface, const SubElementNode * node) const +{ + ThrowRequire(get_cutter()); + return get_cutter()->sign_at_position(interface, node->owner_coords(this)); +} + +double +Mesh_Element::interface_crossing_position(const InterfaceID interface, const SubElementNode * node1, const SubElementNode * node2) const +{ + ThrowRequire(get_cutter()); + Segment3d edge(node1->owner_coords(this), node2->owner_coords(this)); + return get_cutter()->interface_crossing_position(interface, edge); +} + +bool +Mesh_Element::is_prolonged() const +{ /* %TRACE% */ /* %TRACE% */ + for (auto && node : my_nodes) + { + if (!(node->is_prolonged())) return false; + } + return true; +} + +int Mesh_Element::get_interface_index(const InterfaceID interface) const +{ + ThrowAssert(have_interface(interface)); + const auto iter = std::lower_bound(myCuttingInterfaces.begin(), myCuttingInterfaces.end(), interface); + return std::distance(myCuttingInterfaces.begin(), iter); +} + +bool +Mesh_Element::triangulate(const CDMesh & mesh, const InterfaceGeometry & interfaceGeometry) +{ /* %TRACE% */ /* %TRACE% */ + + const auto & decomposedBlocksSelector = mesh.get_phase_support().get_all_decomposed_blocks_selector(); + const bool inDecomposedBlock = decomposedBlocksSelector(mesh.stk_bulk().bucket(entity())); + if (inDecomposedBlock) + { + create_cutter(mesh, interfaceGeometry); + + if (!myCuttingInterfaces.empty()) + my_have_interface = true; + + if (my_phase.empty()) + my_phase = interfaceGeometry.get_starting_phase(myCutter.get()); + else + my_have_interface = false; // uncut element with phase already set + } + + if (!have_subelements() && (have_interface() || have_refined_edges())) + { + create_base_subelement(); + } + + return false; +} + +const SubElementNode * get_node_matching_entity(const std::vector & nodes, stk::mesh::Entity stkNode) +{ + for (auto && node : nodes) + if (stkNode == node->entity()) + return node; + return nullptr; +} + +static IntersectionPointFilter +keep_all_intersecion_points_filter() +{ + auto filter = + [](const std::vector & intersectionPointNodes, const std::vector & intersectionPointSortedDomains) + { + return true; + }; + return filter; +} + +Vector3d Mesh_Element::get_intersection_point_parametric_coordinates(const IntersectionPoint & intersectionPoint) const +{ + Vector3d pCoords(Vector3d::ZERO); + const auto & intersectionPointNodes = intersectionPoint.get_nodes(); + const auto & intersectionPointWeights = intersectionPoint.get_weights(); + const size_t numIntersectionPointNodes = intersectionPointNodes.size(); + for (size_t iNode=0; iNodeowner_coords(this); + } + return pCoords; +} + +std::vector +Mesh_Element::get_interface_signs_based_on_crossings(const NodeVec & nodes) const +{ + std::vector nodeCoords; + std::vector*> nodeDomains; + + nodeCoords.clear(); + nodeDomains.clear(); + nodeCoords.reserve(nodes.size()); + nodeDomains.reserve(nodes.size()); + for (auto && node : nodes) + { + nodeCoords.push_back(node->owner_coords(this)); + nodeDomains.push_back(&node->get_sorted_node_domains()); + } + return get_cutter()->get_interface_signs_based_on_crossings(nodeCoords, nodeDomains); +} + +void +Mesh_Element::cut_interior_intersection_points(CDMesh & mesh) +{ /* %TRACE% */ /* %TRACE% */ + if (!have_interface()) + return; + + std::vector intersectionPoints; + stk::topology elementTopology = mesh.stk_bulk().bucket(entity()).topology(); + const MasterElement & masterElement = MasterElementDeterminer::getMasterElement(elementTopology); + const std::vector elementNodes(mesh.stk_bulk().begin_nodes(entity()), mesh.stk_bulk().end_nodes(entity())); + append_intersection_points_from_element_interior(masterElement, elementNodes, *myCutter, keep_all_intersecion_points_filter(), intersectionPoints); + + for (auto && intersectionPoint : intersectionPoints) + { + const Vector3d pCoords = get_intersection_point_parametric_coordinates(intersectionPoint); + + const ElementObj * containingElem = nullptr; + Vector3d containingElemPCoords; + find_child_coordinates_at_owner_coordinates(pCoords, containingElem, containingElemPCoords); + ThrowAssert(containingElem); + + ElementObj * elem = const_cast(containingElem); + if (!elem->captures_intersection_point_domains(intersectionPoint.get_sorted_domains())) + elem->cut_interior_intersection_point(mesh, containingElemPCoords, intersectionPoint.get_sorted_domains()); + } + + const std::vector interfaces = get_sorted_cutting_interfaces(); + for (size_t i1=0; i1cut_face_interior_intersection_points(mesh, interfaces[i1], interfaces[i2]); + } + } + + std::vector leafSubElements; + get_subelements(leafSubElements); + for (auto && leafSubElement : leafSubElements) + { + SubElement * subElem = const_cast(leafSubElement); + const std::vector interfaceSigns = get_interface_signs_based_on_crossings(subElem->get_nodes()); + subElem->set_interface_signs(interfaceSigns); + } +} + +void +Mesh_Element::create_cutter(const CDMesh & mesh, const InterfaceGeometry & interfaceGeometry) +{ /* %TRACE% */ /* %TRACE% */ + const auto intersectingPlanesDiagonalPicker = get_diagonal_picker(); + myCutter = interfaceGeometry.build_element_cutter(mesh.stk_bulk(), entity(), intersectingPlanesDiagonalPicker); + myCuttingInterfaces = myCutter->get_sorted_cutting_interfaces(); +} + +void +Mesh_Element::create_base_subelement() +{ /* %TRACE% */ /* %TRACE% */ + + stk::topology base_topology = coord_topology(); + + const unsigned num_sides = base_topology.num_sides(); + + std::vector parent_side_ids(num_sides); + for (unsigned i=0; i base_subelement; + if (stk::topology::TETRAHEDRON_4 == base_topology) + { + base_subelement = std::make_unique( my_nodes, parent_side_ids, this); + } + else if (stk::topology::TETRAHEDRON_10 == base_topology) + { + // purposely use lower order base subelement + std::vector sub_nodes(my_nodes.begin(), my_nodes.begin()+4); + base_subelement = std::make_unique( sub_nodes, parent_side_ids, this); + } + else if (stk::topology::TRIANGLE_3_2D == base_topology) + { + base_subelement = std::make_unique( my_nodes, parent_side_ids, this); + } + else if (stk::topology::TRIANGLE_6_2D == base_topology) + { + // purposely use lower order base subelement + std::vector sub_nodes(my_nodes.begin(), my_nodes.begin()+3); + base_subelement = std::make_unique( sub_nodes, parent_side_ids, this); + } + ThrowErrorMsgIf(!base_subelement, "Elements with topology " << base_topology.name() << " not supported for CDFEM."); + + base_subelement->initialize_interface_signs(); + add_subelement(std::move(base_subelement)); +} + +void +Mesh_Element::determine_node_signs(const CDMesh & mesh, const InterfaceID interface_key) +{ + if (have_interface(interface_key)) + { + for(auto && subelement : my_subelements) + { + subelement->SubElement::determine_node_signs(mesh, interface_key); + } + } +} + +void +Mesh_Element::determine_node_scores(const CDMesh & mesh, const InterfaceID interface_key) +{ + if (have_interface(interface_key)) + { + for(auto && subelement : my_subelements) + { + subelement->SubElement::determine_node_scores(mesh, interface_key); + } + } +} + +void +Mesh_Element::decompose(CDMesh & mesh, const InterfaceID interface_key) +{ + if (have_interface(interface_key)) + { + for(auto && subelement : my_subelements) + { + subelement->decompose(mesh, interface_key); + } + } +} + +void +Mesh_Element::handle_hanging_children(CDMesh & mesh, const InterfaceID & interface) +{ + if (!have_subelements() && have_edges_with_children()) + { + create_base_subelement(); + } + + for(auto && subelement : my_subelements) + { + subelement->handle_hanging_children(mesh, interface); + } +} + +void +Mesh_Element::build_quadratic_subelements(CDMesh & mesh) +{ + if (2 == subelement_order()) + { + for (auto && subelement : my_subelements) + { + subelement->build_quadratic_subelements(mesh); + } + } +} + +int +ElementObj::evaluate_quad(const SubElementNode * n0, const SubElementNode * n1, const SubElementNode * n2, const SubElementNode * n3) +{ /* %TRACE% */ /* %TRACE% */ + return evaluate_quad(n0->coordinates(), n1->coordinates(), n2->coordinates(), n3->coordinates()); +} + +int +ElementObj::evaluate_quad(const Vector3d & x0, const Vector3d & x1, const Vector3d & x2, const Vector3d & x3) +{ /* %TRACE% */ /* %TRACE% */ + // given 4 angles + // angle0 - angle subtended by (x3-x0) and (x1-x0) + // angle1 - angle subtended by (x0-x1) and (x2-x1) + // angle2 - angle subtended by (x1-x2) and (x3-x2) + // angle3 - angle subtended by (x2-x3) and (x0-x3) + // returns -1 if the max of angle 0 and angle 2 is significant larger than the max of angle 1 and angle 3 + // returns +1 if the opposite is true + // returns 0 if neither is true (the max angles are basically the same) OR one of the sides is degenerate + + const Vector3d side0 = x1 - x0; + const Vector3d side1 = x2 - x1; + const Vector3d side2 = x3 - x2; + const Vector3d side3 = x0 - x3; + + const double side_len0 = side0.length(); + const double side_len1 = side1.length(); + const double side_len2 = side2.length(); + const double side_len3 = side3.length(); + + // here, meas0 = -cos(angle0) * side_len0*side_len1*side_len2*side_len3 + const double meas0 = Dot(side3,side0) * side_len1*side_len2; + const double meas1 = Dot(side0,side1) * side_len2*side_len3; + const double meas2 = Dot(side1,side2) * side_len3*side_len0; + const double meas3 = Dot(side2,side3) * side_len0*side_len1; + + if ( krinolog.shouldPrint(LOG_DEBUG) ) + { + krinolog << "Lengths: " << side_len0 << ", " << side_len1 << ", " << side_len2 << ", " << side_len3 << "\n"; + krinolog << "Angle measures: " << meas0 << ", " << meas1 << ", " << meas2 << ", " << meas3 << "\n"; + krinolog << "Angles: " << std::acos(-meas0/(side_len0*side_len1*side_len2*side_len3))*180.0/3.141592654 << ", " + << std::acos(-meas1/(side_len0*side_len1*side_len2*side_len3))*180.0/3.141592654 << ", " + << std::acos(-meas2/(side_len0*side_len1*side_len2*side_len3))*180.0/3.141592654 << ", " + << std::acos(-meas3/(side_len0*side_len1*side_len2*side_len3))*180.0/3.141592654 << "\n"; + } + + const double meas02 = std::max(meas0,meas2); + const double meas13 = std::max(meas1,meas3); + + const double tol = std::numeric_limits::epsilon()*(side_len0*side_len1*side_len2*side_len3); + + if (meas02 > (meas13 + tol)) + { + return -1; + } + else if (meas13 > (meas02 + tol)) + { + return +1; + } + else + { + return 0; + } +} + +bool +ElementObj::have_edges_with_children() const +{ /* %TRACE% */ /* %TRACE% */ + const int num_edges = topology().num_edges(); + + // Iterate edges looking for any common children of the edge nodes + for ( int edge = 0; edge < num_edges; ++edge ) + { + const unsigned * edge_node_ordinals = get_edge_node_ordinals(topology(), edge); + + const SubElementNode * node0 = my_nodes[edge_node_ordinals[0]]; + const SubElementNode * node1 = my_nodes[edge_node_ordinals[1]]; + const SubElementNode * child = SubElementNode::common_child({node0, node1}); + if( child ) + { + return true; + } + } + return false; +} + +bool +ElementObj::determine_diagonal_for_internal_quad_of_cut_tet_from_owner_nodes(const SubElementNode * pn0, const SubElementNode * pn1, const SubElementNode * pn2, const SubElementNode * pn3) +{ + // true: connect child of pn0 and pn2 (n0) to child of pn1 and pn3 (n2) + // false: connect child of pn1 and pn2 (n1) to child of pn0 and pn3 (n3) + + // This method is used for the cutter. For CUT_QUADS_BY_GLOBAL_IDENTIFIER, use logic that is consistent with the way elements are cut. + // For other strategies, still use id based logic because this is only being used for the cutter, and it will be removed when + // we use the actual facets instead of the cutter. + + stk::mesh::EntityId pn0_id = pn0->entityId(); + stk::mesh::EntityId pn1_id = pn1->entityId(); + stk::mesh::EntityId pn2_id = pn2->entityId(); + stk::mesh::EntityId pn3_id = pn3->entityId(); + + ThrowAssert(pn0_id != 0 && pn1_id != 0 && pn2_id != 0 && pn3_id != 0); + + if ((pn0_id < pn1_id) == (pn2_id < pn3_id)) + { + return true; + } + return false; +} + +bool +ElementObj::determine_diagonal_for_internal_quad_of_cut_tet_from_edge_nodes(const Simplex_Generation_Method simplexMethod, const SubElementNode * n0, const SubElementNode * n1, const SubElementNode * n2, const SubElementNode * n3, + const bool face0, const bool face1, const bool face2, const bool face3) +{ + // true: connect n0 and n2, false: connect n1 and n3 + + if (simplexMethod == CUT_QUADS_BY_GLOBAL_IDENTIFIER) + { + SubElementNodeAncestry ancestry0 = n0->get_ancestry(); + SubElementNodeAncestry ancestry1 = n1->get_ancestry(); + SubElementNodeAncestry ancestry2 = n2->get_ancestry(); + SubElementNodeAncestry ancestry3 = n3->get_ancestry(); + + SubElementNodeAncestry ancestry02 = std::min(ancestry0, ancestry2); + SubElementNodeAncestry ancestry13 = std::min(ancestry1, ancestry3); + + return (ancestry02 < ancestry13); + } + + // Make sure that diagonal can be picked either way and still avoid Steiner point + const int caseId = (face0 ? 1 : 0) + (face1 ? 2 : 0) + (face2 ? 4 : 0) + (face3 ? 8 : 0); + ThrowRequire(caseId == 3 || caseId == 5 || caseId == 10 || caseId == 12); + + return (evaluate_quad(n0,n1,n2,n3) == -1); +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_Element.hpp b/packages/krino/krino/krino_lib/Akri_Element.hpp new file mode 100644 index 000000000000..10f0f06a1da5 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Element.hpp @@ -0,0 +1,207 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Element_h +#define Akri_Element_h + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../interface_geometry_interface/Akri_InterfaceGeometry.hpp" +namespace krino { + +class SubElement; +class SubElementNode; +class ProlongationElementData; +class IntersectionPoint; +class ElementIntersection; + +class ElementObj { +public: + + ElementObj(const stk::mesh::BulkData & stkMesh, stk::mesh::Entity elemEntity); + ElementObj(const stk::topology elem_topology, const NodeVec & nodes); + + virtual ~ElementObj(); // Definition must in implementation file because SubElement is incomplete + + static bool is_less(const ElementObj * elem0,const ElementObj * elem1) { return (elem0->entityId() < elem1->entityId()); } + + static int evaluate_quad(const SubElementNode * n0, const SubElementNode * n1, const SubElementNode * n2, const SubElementNode * n3); + static int evaluate_quad(const Vector3d & x0, const Vector3d & x1, const Vector3d & x2, const Vector3d & x3); + static bool determine_diagonal_for_internal_quad_of_cut_tet_from_owner_nodes(const SubElementNode * pn0, const SubElementNode * pn1, const SubElementNode * pn2, const SubElementNode * pn3); + static bool determine_diagonal_for_internal_quad_of_cut_tet_from_edge_nodes(const Simplex_Generation_Method simplexMethod, const SubElementNode * n0, const SubElementNode * n1, const SubElementNode * n2, const SubElementNode * n3, + const bool face0, const bool face1, const bool face2, const bool face3); + + int spatial_dim() const { return my_master_elem.topology_dimension(); } + const NodeVec & get_nodes() const { return my_nodes; } + unsigned num_nodes() const { return my_nodes.size(); } + const PhaseTag & get_phase() const { return my_phase; } + void set_phase(const PhaseTag & phase) { my_phase = phase; } + stk::mesh::EntityId entityId() const { return my_entityId; } + bool check_entity(const stk::mesh::BulkData & mesh) const { return (my_entityId == 0) ? (my_entity == stk::mesh::Entity()) : (mesh.is_valid(my_entity) && my_entityId == mesh.identifier(my_entity)); } + stk::mesh::Entity entity() const { return my_entity; } + void set_entity( const stk::mesh::BulkData & mesh, stk::mesh::Entity meshEntity ) const { my_entity = meshEntity; my_entityId = mesh.identifier(my_entity); } + + const MasterElement & master_elem() const { return my_master_elem; } + stk::topology topology() const { return my_master_elem.get_topology(); } + void std_intg_pts( sierra::Array & intg_pt_locations, + sierra::Array & intg_weights, + sierra::ArrayContainer & det_J, + const sierra::ArrayContainer & coords, + const MasterElement & me ) const; + void std_intg_pts( sierra::Array & intg_pt_locations, + sierra::Array & intg_weights, + sierra::ArrayContainer & det_J, + const sierra::ArrayContainer & coords ) const { std_intg_pts(intg_pt_locations, intg_weights, det_J, coords, my_master_elem); } + + void fill_node_owner_coords(const Mesh_Element * owner, std::vector & coords) const; + Vector3d compute_local_coords_from_owner_coordinates(const Mesh_Element * owner, const Vector3d & coords) const; + + void integration_weights( + std::vector & intg_weights, // includes both gauss point weight and detJ + const MasterElement & me ) const; + void integration_weights( + std::vector & intg_weights ) const { return integration_weights(intg_weights, my_master_elem); } + static void integration_weights( + std::vector & intg_weights, // includes both gauss point weight and detJ + const int numCoordDims, + const std::vector & mesh_coords, + const MasterElement & me, + const MasterElement & mesh_me); + static void integration_weights( + std::vector & intg_weights, // includes both gauss point weight and detJ + const int numCoordDims, + const std::vector & mesh_coords, + const MasterElement & me) { integration_weights(intg_weights, numCoordDims, mesh_coords, me, me); } + static void integration_locations( + std::vector & intg_pt_locations, + const MasterElement & me); + static void gather_nodal_field(const stk::mesh::BulkData & mesh, stk::mesh::Entity element, const FieldRef field, std::vector & result, unsigned dim = 1, unsigned npe = 0); + static double volume(const stk::mesh::BulkData & mesh, stk::mesh::Entity element, const FieldRef coords_field); + static PhaseTag update_phase(const CDMesh & mesh, const PhaseTag & startPhase, const InterfaceID interface_key, const int sign); + static PhaseTag update_phase(const CDMesh & mesh, const PhaseTag & startPhase, const std::vector & interfaces, const std::vector & interfaceSigns); + + const MasterElement* get_evaluation_master_element(const FieldRef field) const; + void evaluate_prolongation_field(const CDMesh & mesh, const FieldRef field, const unsigned field_length, const Vector3d & p_coords, double * result) const; + + void add_subelement(std::unique_ptr subelem); + void get_subelements( std::vector & subelems ) const; + void get_subelements( std::vector & subelems ); + bool have_subelements() const { return !my_subelements.empty(); } + unsigned num_subelements() const { return my_subelements.size(); } + bool have_refined_edges() const; + + virtual void determine_decomposed_elem_phase(const CDMesh & mesh) { ThrowRequire(false); } + virtual void cut_interior_intersection_point(CDMesh & mesh, const Vector3d & pCoords, const std::vector & sortedDomains); + virtual void cut_face_intersection_point(const int iFace, const Vector3d & pCoords, const std::vector & sortedDomains); + bool have_edges_with_children() const; + bool captures_intersection_point_domains(const std::vector & intersectionPointDomains) const; + bool face_captures_intersection_point_domains(const int iFace, const std::vector & intersectionPointDomains) const; + + ProlongationElementData * get_prolongation_data() const { return my_prolongation_data; } + void set_prolongation_data( ProlongationElementData * data ) const { my_prolongation_data = data; } + + void prolongate_fields(const CDMesh & mesh) const; + +protected: + const MasterElement& my_master_elem; + std::vector my_nodes; + std::vector> my_subelements; + PhaseTag my_phase; + mutable stk::mesh::Entity my_entity; + mutable stk::mesh::EntityId my_entityId; + mutable ProlongationElementData * my_prolongation_data; + +private: + //: Default constructor not allowed + ElementObj(); +}; + +class Mesh_Element : public ElementObj { +public: + + Mesh_Element(CDMesh & mesh, stk::mesh::Entity elemEntity); + + virtual ~Mesh_Element(); + + static bool is_supported_topology(stk::topology elem_topology) { return determine_subelement_topology(elem_topology).first != stk::topology::INVALID_TOPOLOGY; } + static std::pair determine_subelement_topology(stk::topology elem_topology); + + Vector3d get_node_parametric_coords( const int lnn ) const; + Vector3d get_node_parametric_coords( const SubElementNode * node ) const; + + const MasterElement & coord_master_elem() const { return my_master_elem; } + + stk::topology coord_topology() const { return my_master_elem.get_topology(); } + + const stk::topology & subelement_topology() const { return my_subelement_topology; } + int subelement_order() const { return my_subelement_order; } + + std::string visualize(const CDMesh & mesh) const; + double interface_crossing_position(const InterfaceID interface, const Segment3d & edge) const; + int interface_node_sign(const InterfaceID interface, const SubElementNode * node) const; + void fill_face_interior_intersections(const NodeVec & faceNodes, const InterfaceID & interface1, const InterfaceID & interface2, std::vector & faceIntersectionPoints) const; + double interface_crossing_position(const InterfaceID interface, const SubElementNode * node1, const SubElementNode * node2) const; + std::function &)> get_diagonal_picker() const; + + const ElementCutter * get_cutter() const { return myCutter.get(); } + ElementCutter * get_cutter() { return myCutter.get(); } + + bool is_prolonged() const; + Vector3d coordinates( const Vector3d & p_coords ) const; + + PhaseTag determine_phase_at_location(const Vector3d & location) const; + bool have_interface() const { return my_have_interface; } + bool have_interface(const InterfaceID interface) const { return std::binary_search(myCuttingInterfaces.begin(), myCuttingInterfaces.end(), interface); } + int get_num_interfaces() const { return myCuttingInterfaces.size(); } + int get_interface_index(const InterfaceID interface) const; + const std::vector & get_sorted_cutting_interfaces() const { return myCuttingInterfaces; } + virtual void determine_decomposed_elem_phase(const CDMesh & mesh) override; + + bool triangulate(const CDMesh & mesh, const InterfaceGeometry & interfaceGeometry); //return value indicates if any changes were made + void create_cutter(const CDMesh & mesh, const InterfaceGeometry & interfaceGeometry); + void create_base_subelement(); + void determine_node_signs(const CDMesh & mesh, const InterfaceID interface_key); + void determine_node_scores(const CDMesh & mesh, const InterfaceID interface_key); + void decompose(CDMesh & mesh, const InterfaceID interface_key); + void handle_hanging_children(CDMesh & mesh, const InterfaceID & interface); + void build_quadratic_subelements(CDMesh & mesh); + std::vector get_interface_signs_based_on_crossings(const NodeVec & nodes) const; + void cut_interior_intersection_points(CDMesh & mesh); + + void find_child_coordinates_at_owner_coordinates(const Vector3d & ownerCoordinates, const ElementObj *& child, Vector3d & child_p_coords) const; + + double volume() const; + + bool is_single_coincident() const; + +private: + Mesh_Element() = delete; + Vector3d get_intersection_point_parametric_coordinates(const IntersectionPoint & intersectionPoint) const; + std::vector gather_nodal_coordinates() const; + + int my_subelement_order; + stk::topology my_subelement_topology; + bool my_have_interface; + + std::unique_ptr myCutter; + std::vector myCuttingInterfaces; +}; + +} // namespace krino + +#endif // Akri_Element_h diff --git a/packages/krino/krino/krino_lib/Akri_ElementCutterUtils.cpp b/packages/krino/krino/krino_lib/Akri_ElementCutterUtils.cpp new file mode 100644 index 000000000000..e640df211435 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_ElementCutterUtils.cpp @@ -0,0 +1,184 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include + +#include "../interface_geometry_interface/Akri_InterfaceGeometry.hpp" +#include + +namespace krino { + +static void fill_nodal_shape_functions(const MasterElement & masterElement, const Vector3d & elementParametricCoords, std::vector & nodalShapeFunctions) +{ + const int npe = masterElement.num_nodes(); + nodalShapeFunctions.resize(npe); + masterElement.shape_fcn(1, elementParametricCoords.data(), nodalShapeFunctions.data()); +} + +static ElementIntersectionPointFilter build_element_intersection_filter(const std::vector & nodes, + const IntersectionPointFilter & intersectionPointFilter) +{ + auto filter = + [&nodes, &intersectionPointFilter](const std::vector & intersectionPointSortedDomains) + { + return intersectionPointFilter(nodes, intersectionPointSortedDomains); + }; + return filter; +} + +static void append_intersection_points_from_interior(const MasterElement & masterElement, + const std::vector & intersectionPointNodes, + const std::vector & interiorIntersections, + std::vector & intersectionPoints) +{ + if (interiorIntersections.empty()) + return; + + const bool intersectionPointIsOwned = true; + + std::vector intersectionPointWeights; + for (auto & interiorIntersection : interiorIntersections) + { + fill_nodal_shape_functions(masterElement, interiorIntersection.parametricCoords, intersectionPointWeights); + intersectionPoints.emplace_back(intersectionPointIsOwned, intersectionPointNodes, intersectionPointWeights, interiorIntersection.sortedDomains); + } +} + +static void append_intersection_points_from_element_face(const MasterElement & elementMasterElement, + const std::vector & elementNodes, + const int iFace, + const ElementCutter & elementCutter, + const IntersectionPointFilter & intersectionPointFilter, + std::vector & intersectionPoints) +{ + std::vector interiorIntersections; + + const std::vector interfaces = elementCutter.get_sorted_cutting_interfaces(); + + std::array faceNodeOrdinals; + elementMasterElement.get_topology().face_node_ordinals(iFace, faceNodeOrdinals.data()); + const std::vector faceNodes{elementNodes[faceNodeOrdinals[0]], elementNodes[faceNodeOrdinals[1]], elementNodes[faceNodeOrdinals[2]]}; + const double * elemNodeParamCoords = elementMasterElement.nodal_parametric_coordinates(); + const std::array faceNodeCoordinates = {{Vector3d(elemNodeParamCoords+3*faceNodeOrdinals[0]), Vector3d(elemNodeParamCoords+3*faceNodeOrdinals[1]), Vector3d(elemNodeParamCoords+3*faceNodeOrdinals[2])}}; + + const MasterElement & faceMasterElement = MasterElementDeterminer::getMasterElement(elementMasterElement.get_topology().face_topology(iFace)); + const ElementIntersectionPointFilter faceIntersectionPointFilter = build_element_intersection_filter(faceNodes, intersectionPointFilter); + + std::vector sortedDomains; + for (size_t i1=0; i1 & elementNodes, + const int iFace) +{ + stk::mesh::EntityId elementId = mesh.identifier(element); + std::vector faceNodes(elementTopology.face_topology(iFace).num_nodes()); + std::vector faceElements; + elementTopology.face_nodes(elementNodes, iFace, faceNodes.data()); + stk::mesh::get_entities_through_relations(mesh, faceNodes, stk::topology::ELEMENT_RANK, faceElements); + + for (auto faceElement : faceElements) + if (mesh.identifier(faceElement) < elementId && elementSelector(mesh.bucket(faceElement))) + return false; + return true; +} + +static void append_intersection_points_from_within_element_and_owned_faces(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & parentElementSelector, + const stk::mesh::Entity element, + const InterfaceGeometry & geometry, + const std::function &)> & diagonalPicker, + const IntersectionPointFilter & intersectionPointFilter, + std::vector & intersectionPoints) +{ + stk::topology elementTopology = mesh.bucket(element).topology(); + const MasterElement & masterElement = MasterElementDeterminer::getMasterElement(elementTopology); + const std::vector elementNodes(mesh.begin_nodes(element), mesh.end_nodes(element)); + + std::unique_ptr elementCutter = geometry.build_element_cutter(mesh, element, diagonalPicker); + if (elementCutter->might_have_interior_or_face_intersections()) + { + append_intersection_points_from_element_interior(masterElement, elementNodes, *elementCutter, intersectionPointFilter, intersectionPoints); + + const int numFaces = masterElement.get_topology().num_faces(); + if (numFaces > 0) + { + for (int iFace=0; iFace &)> +temporary_build_always_true_diagonal_picker() +{ + auto diagonalPicker = + [](const std::array & faceNodes) + { + return true; + }; + return diagonalPicker; +} + +void append_intersection_points_from_within_elements_and_owned_faces(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & parentElementSelector, + const std::vector & elements, + const InterfaceGeometry & geometry, + const IntersectionPointFilter & intersectionPointFilter, + std::vector & intersectionPoints) +{ + const auto diagonalPicker = temporary_build_always_true_diagonal_picker(); + + for (auto element : elements) + { + if (parentElementSelector(mesh.bucket(element))) + { + append_intersection_points_from_within_element_and_owned_faces(mesh, + parentElementSelector, + element, + geometry, + diagonalPicker, + intersectionPointFilter, + intersectionPoints); + } + } +} + +void append_intersection_points_from_element_interior(const MasterElement & masterElement, + const std::vector & nodes, + const ElementCutter & elementCutter, + const IntersectionPointFilter & intersectionPointFilter, + std::vector & intersectionPoints) +{ + std::vector interiorIntersections; + const ElementIntersectionPointFilter elementIntersectionPointFilter = build_element_intersection_filter(nodes, intersectionPointFilter); + elementCutter.fill_interior_intersections(elementIntersectionPointFilter, interiorIntersections); + append_intersection_points_from_interior(masterElement, nodes, interiorIntersections, intersectionPoints); +} + +} diff --git a/packages/krino/krino/krino_lib/Akri_ElementCutterUtils.hpp b/packages/krino/krino/krino_lib/Akri_ElementCutterUtils.hpp new file mode 100644 index 000000000000..051f02e028fd --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_ElementCutterUtils.hpp @@ -0,0 +1,37 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_ELEMENTCUTTERUTILS_H_ +#define KRINO_INCLUDE_AKRI_ELEMENTCUTTERUTILS_H_ +#include +#include +#include +#include +#include +#include + +#include "../interface_geometry_interface/Akri_InterfaceGeometry.hpp" + +namespace krino { + +void append_intersection_points_from_element_interior(const MasterElement & masterElement, + const std::vector & nodes, + const ElementCutter & elementCutter, + const IntersectionPointFilter & intersectionPointFilter, + std::vector & intersectionPoints); + +void append_intersection_points_from_within_elements_and_owned_faces(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & parentElementSelector, + const std::vector & elements, + const InterfaceGeometry & geometry, + const IntersectionPointFilter & intersectionPointFilter, + std::vector & intersectionPoints); + +} + +#endif /* KRINO_INCLUDE_AKRI_ELEMENTCUTTERUTILS_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_Element_Cutter.cpp b/packages/krino/krino/krino_lib/Akri_Element_Cutter.cpp new file mode 100644 index 000000000000..bea70cbe6fdb --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Element_Cutter.cpp @@ -0,0 +1,1064 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Akri_MasterElementDeterminer.hpp" + +namespace krino { + +bool all_interfaces_have_single_level_set(const std::vector &interfaces) +{ + for (auto && interface : interfaces) + if (interface.first_ls() != interface.second_ls()) + return false; + return true; +} + +void fill_sorted_domains(const bool isOneLSPerPhase,const InterfaceID & interface0, const InterfaceID & interface1, std::vector & sortedDomains) +{ + sortedDomains.clear(); + if (isOneLSPerPhase) + { + + sortedDomains = {interface0.first_ls(), interface0.second_ls(), interface1.first_ls(), interface1.second_ls()}; + stk::util::sort_and_unique(sortedDomains); + } + else + { + ThrowAssert(all_interfaces_have_single_level_set({interface0, interface1})); + sortedDomains = {interface0.first_ls(), interface1.first_ls()}; + } +} + +void fill_sorted_domains(const bool isOneLSPerPhase,const InterfaceID & interface0, const InterfaceID & interface1, const InterfaceID & interface2, std::vector & sortedDomains) +{ + sortedDomains.clear(); + if (isOneLSPerPhase) + { + + sortedDomains = {interface0.first_ls(), interface0.second_ls(), interface1.first_ls(), interface1.second_ls(), interface2.first_ls(), interface2.second_ls()}; + stk::util::sort_and_unique(sortedDomains); + } + else + { + ThrowAssert(all_interfaces_have_single_level_set({interface0, interface1, interface2})); + sortedDomains = {interface0.first_ls(), interface1.first_ls(), interface2.first_ls()}; + } +} + +bool sorted_domains_form_triple_point(const bool is_one_interface_per_phase, const std::vector & sortedDomains) +{ + return !is_one_interface_per_phase || sortedDomains.size() == 3; +} + +bool sorted_domains_form_quadruple_point(const bool is_one_interface_per_phase, const std::vector & sortedDomains) +{ + return !is_one_interface_per_phase || sortedDomains.size() == 4; +} + +void fill_tetrahedron_face_surface_intersection_points(const int iFace, const Cutting_Surface * surf0, const Cutting_Surface * surf1, const std::vector & sortedDomains, std::vector & intersections) +{ + Vector3d intersectionPoint; + if (find_intersection_of_two_planes_and_side_of_tet(iFace, surf0->get_plane(), surf1->get_plane(), intersectionPoint)) + intersections.emplace_back(intersectionPoint, sortedDomains); +} + +void fill_tetrahedron_interior_surface_intersection_points(const Cutting_Surface * surf0, const Cutting_Surface * surf1, const Cutting_Surface * surf2, const std::vector & sortedDomains, std::vector & intersections) +{ + Vector3d intersectionPoint; + if (find_intersection_of_three_planes_within_tet(surf0->get_plane(), surf1->get_plane(), surf2->get_plane(), intersectionPoint)) + intersections.emplace_back(intersectionPoint, sortedDomains); +} + +std::string visualize_element(const stk::topology & topology, std::array & usedGeomIds) +{ + const auto & masterElem = MasterElementDeterminer::getMasterElement(topology); + const double * elemNodeParamCoords = masterElem.nodal_parametric_coordinates(); + + const unsigned numNodes = topology.num_nodes(); + const int dim = topology.dimension(); + + std::ostringstream os; + for (unsigned n=0; n usedGeomIds{0,0,0,0}; + std::string viz = visualize_element(topology, usedGeomIds); + for (auto && cuttingSurf : cuttingSurfaces) + { + std::ostringstream os; + os << cuttingSurf.first; + const std::string interfaceDesc = "on interface " + os.str(); + viz += cuttingSurf.second->print_visualization(usedGeomIds, interfaceDesc); + } + return viz; +} + + +void Element_Cutter::fill_triangle_interior_intersection_points(const ElementIntersectionPointFilter & intersectionPointFilter, std::vector & intersections) const +{ + std::vector interfacesWithCuttingSurface; + fill_interfaces_with_cutting_surface(interfacesWithCuttingSurface); + + std::vector sortedDomains; + std::set> alreadyFound; + Vector3d intersectionPoint; + + intersections.clear(); + for (size_t index0=0; index0get_plane(); + const Plane3d & plane1 = cutting_surfaces.at(interfacesWithCuttingSurface[index1])->get_plane(); + if (find_intersection_of_two_2D_planes_within_tri(plane0, plane1, intersectionPoint) && + intersection_point_is_real(intersectionPoint, sortedDomains)) + { + intersections.emplace_back(intersectionPoint, sortedDomains); + } + } + } + } +} + +void Element_Cutter::fill_tetrahedron_face_interior_intersections(const std::array & faceNodes, + const InterfaceID & interface1, + const InterfaceID & interface2, + const ElementIntersectionPointFilter & intersectionPointFilter, + std::vector & intersections) const +{ + const Plane3d facePlane{faceNodes[0], faceNodes[1], faceNodes[2]}; + + std::vector sortedDomains; + Vector3d intersectionPoint; + + intersections.clear(); + fill_sorted_domains(is_one_ls_per_phase(), interface1, interface2, sortedDomains); + if (sorted_domains_form_triple_point(is_one_ls_per_phase(), sortedDomains)) + { + const Plane3d & plane0 = cutting_surfaces.at(interface1)->get_plane(); + const Plane3d & plane1 = cutting_surfaces.at(interface2)->get_plane(); + if (intersectionPointFilter(sortedDomains) && + find_intersection_of_three_planes(plane0, plane1, facePlane, intersectionPoint) && + intersection_point_is_real(intersectionPoint, sortedDomains)) + { + const Vector3d faceCoords = triangle_parametric_coordinates_of_projected_point(faceNodes, intersectionPoint); + if (within_tri_bounds(faceCoords)) + intersections.emplace_back(faceCoords, sortedDomains); + } + } +} + +void Element_Cutter::fill_tetrahedron_interior_intersection_points(const ElementIntersectionPointFilter & intersectionPointFilter, std::vector & intersections) const +{ + std::vector interfacesWithCuttingSurface; + fill_interfaces_with_cutting_surface(interfacesWithCuttingSurface); + + std::vector sortedDomains; + std::set> alreadyFound; + Vector3d intersectionPoint; + + intersections.clear(); + for (size_t index0=0; index0get_plane(); + const Plane3d & plane1 = cutting_surfaces.at(interfacesWithCuttingSurface[index1])->get_plane(); + const Plane3d & plane2 = cutting_surfaces.at(interfacesWithCuttingSurface[index2])->get_plane(); + if (find_intersection_of_three_planes_within_tet(plane0, plane1, plane2, intersectionPoint) && + intersection_point_is_real(intersectionPoint, sortedDomains)) + { + intersections.emplace_back(intersectionPoint, sortedDomains); + } + } + } + } + } +} + +static ElementIntersectionPointFilter +keep_all_intersecion_points_filter() +{ + auto filter = + [](const std::vector & intersectionPointSortedDomains) + { + return true; + }; + return filter; +} + +void Element_Cutter::fill_interior_intersections(std::vector & intersections) const +{ + fill_interior_intersections(keep_all_intersecion_points_filter(), intersections); +} + +void Element_Cutter::fill_interior_intersections(const ElementIntersectionPointFilter & intersectionPointFilter, std::vector & intersections) const +{ + if (myTopology.base() == stk::topology::TETRAHEDRON_4) + fill_tetrahedron_interior_intersection_points(intersectionPointFilter, intersections); + else if (myTopology.base() == stk::topology::TRIANGLE_3_2D || myTopology.base() == stk::topology::TRIANGLE_3) + fill_triangle_interior_intersection_points(intersectionPointFilter, intersections); + else + ThrowErrorMsg("Unexepected topology " << myTopology.name()); +} + +std::unique_ptr create_element_cutter(const bool oneLSPerPhase, + const MasterElement & masterElem, + const std::vector & elementParentEdges, + const std::vector & areParentEdgesAreOrientedSameAsElementEdges, + const std::function &)> & intersectingPlanesDiagonalPicker) +{ + std::unique_ptr cutter; + if(oneLSPerPhase) + cutter.reset(new One_LS_Per_Phase_Cutter(masterElem, elementParentEdges, areParentEdgesAreOrientedSameAsElementEdges, intersectingPlanesDiagonalPicker)); + else + cutter.reset(new LS_Per_Interface_Cutter(masterElem, elementParentEdges, areParentEdgesAreOrientedSameAsElementEdges, intersectingPlanesDiagonalPicker)); + + return cutter; +} + + +int +Element_Cutter::sign_at_position(const InterfaceID interface, const Vector3d & p_coords) const +{ + const auto iter = cutting_surfaces.find(interface); + if(iter == cutting_surfaces.end()) + { + // Right now, One_LS_Per_Phase_Cutter does not build up the cutting surfaces for the single_phase case. So + // we need a "default" answer. + return +1; + } + const Cutting_Surface & interface_surface = *(iter->second); + + return interface_surface.sign_at_position(p_coords); +} + +double +Element_Cutter::interface_crossing_position(const InterfaceID interface, const Segment3d & edge) const +{ + ThrowErrorMsgIf(cutting_surfaces.find(interface) == cutting_surfaces.end(), + "No cutting surface found for interface " << interface); + const Cutting_Surface & interface_surface = *(cutting_surfaces.find(interface)->second); + return interface_surface.interface_crossing_position(edge); +} + +void +Element_Cutter::fill_interfaces_with_cutting_surface(std::vector & interfaces) const +{ + interfaces.clear(); + for (auto && entry : cutting_surfaces) + interfaces.push_back(entry.first); +} + +int +Element_Cutter::get_starting_phase_for_cutting_surfaces() const +{ + if (cutting_surfaces.empty()) + return -1; + return cutting_surfaces.begin()->first.first_ls(); +} + +bool +Element_Cutter::have_cutting_surface(const InterfaceID interface) const +{ + return cutting_surfaces.find(interface) != cutting_surfaces.end(); +} + +Cutting_Surface * +Element_Cutter::get_cutting_surface(const InterfaceID interface) const +{ + const auto iter = cutting_surfaces.find(interface); + if (iter != cutting_surfaces.end()) + return iter->second.get(); + return nullptr; +} + +static std::vector< std::array > get_element_edge_node_ordinals(const stk::topology baseTopology) +{ + const unsigned numEdges = baseTopology.num_edges(); + std::vector< std::array > elemEdgeNodeOrdinals(numEdges); + for(unsigned i=0; i < numEdges; ++i) + { + const unsigned * node_ordinals = get_edge_node_ordinals(baseTopology, i); + elemEdgeNodeOrdinals[i][0] = node_ordinals[0]; + elemEdgeNodeOrdinals[i][1] = node_ordinals[1]; + } + return elemEdgeNodeOrdinals; +} + +static std::vector +get_parametric_node_coords_on_element_nodes_and_cut_edges(const MasterElement & masterElem, + const std::vector & cutEdges, + const std::vector< std::array > elemEdgeNodeOrdinals) +{ + const stk::topology baseTopology = masterElem.get_topology().base(); + const double * elemNodeParamCoords = masterElem.nodal_parametric_coordinates(); + + const unsigned numNodes = baseTopology.num_nodes(); + const unsigned numEdges = baseTopology.num_edges(); + const int dim = baseTopology.dimension(); + + std::vector nodeParamCoords(numNodes+numEdges); + + for (unsigned n=0; n +get_node_signs_on_element_nodes(const stk::topology baseTopology, + const std::vector & cutEdges, + const std::vector< std::array > elemEdgeNodeOrdinals) +{ + const unsigned numNodes = baseTopology.num_nodes(); + std::vector nodeSigns(numNodes,-2); + + unsigned edge_error = 0; + for (auto && cut_edge : cutEdges) + { + const unsigned edge = cut_edge.edge; + const unsigned node0_ord = elemEdgeNodeOrdinals[edge][0]; + const unsigned node1_ord = elemEdgeNodeOrdinals[edge][1]; + + int node0_sign = -cut_edge.sign; + int node1_sign = cut_edge.sign; + + if (cut_edge.pos < Element_Cutter::theSnapToNodeTol) + node0_sign = 0; + else if (cut_edge.pos > 1.-Element_Cutter::theSnapToNodeTol) + node1_sign = 0; + + if ((nodeSigns[node0_ord]*node0_sign == -1) || + (nodeSigns[node1_ord]*node1_sign == -1)) + edge_error += 1< 0) + { + std::ostringstream err_msg; + err_msg << "Edge crossings are not consistent: " << std::endl; + for(auto && cut_edge : cutEdges) + { + const unsigned edge = cut_edge.edge; + const double pos = cut_edge.pos; + const int sign = cut_edge.sign; + const unsigned node0_ord = elemEdgeNodeOrdinals[edge][0]; + const unsigned node1_ord = elemEdgeNodeOrdinals[edge][1]; + err_msg << " Edge: " << edge << " error = " << ((edge_error & (1< 0) << " pos = " << pos << " sign = " << sign << " node0_ord = " << node0_ord << " node1_ord = " << node1_ord << std::endl; + } + krinolog << err_msg.str(); + throw std::runtime_error(err_msg.str()); + } + + return nodeSigns; +} + +static std::shared_ptr +build_triangle_cutting_surface(const std::vector & nodeSigns, const std::vector & nodeParamCoords) +{ + const int case_id = (nodeSigns[0]+1) + + (nodeSigns[1]+1)*3 + + (nodeSigns[2]+1)*9; + + static const unsigned case_permutations[] = + { 0, 0, 0, 2, 0, 0, 2, 1, 1, 1, // 0-9 + 1, 2, 2, 0, 2, 2, 1, 1, 1, 1, // 10-19 + 2, 0, 0, 2, 0, 0, 0 }; // 20-26 + + stk::topology topo = stk::topology::TRIANGLE_6_2D; + std::vector permute(6); + topo.permutation_node_ordinals(case_permutations[case_id], permute.begin()); + + const unsigned i0 = permute[0]; + const unsigned i1 = permute[1]; + const unsigned i2 = permute[2]; + const unsigned i3 = permute[3]; + const unsigned i5 = permute[5]; + + const int permute_case_id = + (nodeSigns[i0]+1) + + (nodeSigns[i1]+1)*3 + + (nodeSigns[i2]+1)*9; + + switch (permute_case_id) + { + case 1: // ls[0]==0 && ls[1]<0 && ls[2]<0 + case 25: // ls[0]==0 && ls[1]>0 && ls[2]>0 + { + ThrowAssert((nodeSigns[i0] == 0 && nodeSigns[i1] < 0 && nodeSigns[i2] < 0) || (nodeSigns[i0] == 0 && nodeSigns[i1] > 0 && nodeSigns[i2] > 0)); + return (permute_case_id==1) ? + (std::make_shared(nodeParamCoords[i0], (nodeParamCoords[i0] + nodeParamCoords[i1] - nodeParamCoords[i2]))) : + (std::make_shared(nodeParamCoords[i0], (nodeParamCoords[i0] + nodeParamCoords[i2] - nodeParamCoords[i1]))); + } + break; + + case 2: // ls[0]>0 && ls[1]<0 && ls[2]<0 + case 24: // ls[0]<0 && ls[1]>0 && ls[2]>0) + { + ThrowAssert((nodeSigns[i0] > 0 && nodeSigns[i1] < 0 && nodeSigns[i2] < 0) || (nodeSigns[i0] < 0 && nodeSigns[i1] > 0 && nodeSigns[i2] > 0)); + return (permute_case_id==2) ? + (std::make_shared(nodeParamCoords[i5], nodeParamCoords[i3])) : + (std::make_shared(nodeParamCoords[i3], nodeParamCoords[i5])); + } + break; + + case 5: // ls[0]>0 && ls[1]==0 && ls[2]<0 + case 21: // ls[0]<0 && ls[1]==0 && ls[2]>0 + { + ThrowAssert((nodeSigns[i0] > 0 && nodeSigns[i1] == 0 && nodeSigns[i2] < 0) || (nodeSigns[i0] < 0 && nodeSigns[i1] == 0 && nodeSigns[i2] > 0)); + return (permute_case_id==5) ? + (std::make_shared(nodeParamCoords[i5], nodeParamCoords[i1])) : + (std::make_shared(nodeParamCoords[i1], nodeParamCoords[i5])); + } + break; + + case 4: // ls[0]==0 && ls[1]==0 && ls[2]<0 + case 22: // ls[0]==0 && ls[1]==0 && ls[2]>0 + { + ThrowAssert((nodeSigns[i0] == 0 && nodeSigns[i1] == 0 && nodeSigns[i2] < 0) || (nodeSigns[i0] == 0 && nodeSigns[i1] == 0 && nodeSigns[i2] > 0)); + return (permute_case_id==4) ? + (std::make_shared(nodeParamCoords[i0], nodeParamCoords[i1])) : + (std::make_shared(nodeParamCoords[i1], nodeParamCoords[i0])); + } + break; + + default: + { + krinolog << "Case id " << case_id << " " << permute_case_id << stk::diag::dendl; + ThrowRuntimeError("Subelement decomposition error."); + } + } + + ThrowRuntimeError("Subelement decomposition error."); +} + +static std::shared_ptr +build_tetrahedral_cutting_surface(const std::vector & nodeSigns, + const std::vector & nodeParamCoords, + const std::function &)> & intersectingPlanesDiagonalPicker) +{ + const int case_id = (nodeSigns[0]+1) + + (nodeSigns[1]+1)*3 + + (nodeSigns[2]+1)*9 + + (nodeSigns[3]+1)*27; + + static const unsigned case_permutations[] = + { 0, 0, 0, 1, 0, 0, 1, 5, 0, 2, // 0-9 + 2, 6, 1, 0, 0, 1, 1,10, 2, 2, // 10-19 + 2,11, 2, 4, 1, 8, 4, 4, 3, 3, // 20-29 + 4, 3, 3, 9, 5, 7, 7, 6, 6, 9, // 30-39 + 0, 9, 9, 6, 7, 7, 7, 9,11, 3, // 40-49 + 4, 3, 3, 4, 4, 8, 3, 4, 4,11, // 50-59 + 4, 2, 2,10, 8, 1,10, 0, 1, 6, // 60-69 + 2, 2, 7, 5, 1, 0, 0, 1, 0, 0, // 70-79 + 0 }; + + stk::topology full_topology = stk::topology::TETRAHEDRON_10; + std::vector permute_node_ordinals(10); + full_topology.permutation_node_ordinals(case_permutations[case_id], permute_node_ordinals.begin()); + + const unsigned i0 = permute_node_ordinals[0]; + const unsigned i1 = permute_node_ordinals[1]; + const unsigned i2 = permute_node_ordinals[2]; + const unsigned i3 = permute_node_ordinals[3]; + const unsigned i4 = permute_node_ordinals[4]; + const unsigned i5 = permute_node_ordinals[5]; + const unsigned i6 = permute_node_ordinals[6]; + const unsigned i7 = permute_node_ordinals[7]; + const unsigned i8 = permute_node_ordinals[8]; + + const int permute_case_id = (nodeSigns[i0]+1) + + (nodeSigns[i1]+1)*3 + + (nodeSigns[i2]+1)*9 + + (nodeSigns[i3]+1)*27; + + switch (permute_case_id) + { + case 1: // ls[0]=0 && ls[1]<0 && ls[2]<0 && ls[3]<0 + case 79: // ls[0]=0 && ls[1]>0 && ls[2]>0 && ls[3]>0 + { + const Vector3d x0p13 = nodeParamCoords[i0] + nodeParamCoords[i3]-nodeParamCoords[i1]; + const Vector3d x0p12 = nodeParamCoords[i0] + nodeParamCoords[i2]-nodeParamCoords[i1]; + return (permute_case_id==1) ? + (std::make_shared(nodeParamCoords[i0],x0p13,x0p12)) : + (std::make_shared(nodeParamCoords[i0],x0p12,x0p13)); + } + break; + + case 4: // ls[0]=0 && ls[1]=0 && ls[2]<0 && ls[3]<0 + case 76: // ls[0]=0 && ls[1]=0 && ls[2]>0 && ls[3]>0 + { + const Vector3d x0p23 = nodeParamCoords[i0] + nodeParamCoords[i3]-nodeParamCoords[i2]; + return (permute_case_id==4) ? + (std::make_shared(nodeParamCoords[i0],nodeParamCoords[i1],x0p23)) : + (std::make_shared(nodeParamCoords[i1],nodeParamCoords[i0],x0p23)); + } + break; + + case 2: // ls[0]>0 && ls[1]<0 && ls[2]<0 && ls[3]<0 + case 78: // ls[0]<0 && ls[1]>0 && ls[2]>0 && ls[3]>0 + { + ThrowAssert((nodeSigns[i0] > 0 && nodeSigns[i1] < 0 && nodeSigns[i2] < 0 && nodeSigns[i3] < 0) || (nodeSigns[i0] < 0 && nodeSigns[i1] > 0 && nodeSigns[i2] > 0 && nodeSigns[i3] > 0)); + + return (permute_case_id==2) ? + (std::make_shared(nodeParamCoords[i4],nodeParamCoords[i7],nodeParamCoords[i6])) : + (std::make_shared(nodeParamCoords[i4],nodeParamCoords[i6],nodeParamCoords[i7])); + } + break; + + case 5: // ls[0]>0 && ls[1]=0 && ls[2]<0 && ls[3]<0 + case 75: // ls[0]<0 && ls[1]=0 && ls[2]>0 && ls[3]>0 + { + return (permute_case_id==5) ? + (std::make_shared(nodeParamCoords[i1],nodeParamCoords[i7],nodeParamCoords[i6])) : + (std::make_shared(nodeParamCoords[i1],nodeParamCoords[i6],nodeParamCoords[i7])); + } + break; + + case 8: // ls[0]>0 && ls[1]>0 && ls[2]<0 && ls[3]<0 + { + const bool face_diag = intersectingPlanesDiagonalPicker({{i0,i1,i2,i3}}); + return (face_diag) ? + (std::make_shared(nodeParamCoords[i8],nodeParamCoords[i7],nodeParamCoords[i6],nodeParamCoords[i5])) : + (std::make_shared(nodeParamCoords[i7],nodeParamCoords[i6],nodeParamCoords[i5],nodeParamCoords[i8])); + } + break; + + case 13: // ls[0]=0 && ls[1]=0 && ls[2]=0 && ls[3]<0 + case 67: // ls[0]=0 && ls[1]=0 && ls[2]=0 && ls[3]>0 + { + return (permute_case_id==13) ? + (std::make_shared(nodeParamCoords[i0],nodeParamCoords[i2],nodeParamCoords[i1])) : + (std::make_shared(nodeParamCoords[i0],nodeParamCoords[i1],nodeParamCoords[i2])); + } + break; + + case 14: // ls[0]>0 && ls[1]=0 && ls[2]=0 && ls[3]<0 + { + ThrowAssert(nodeSigns[i0] > 0 && nodeSigns[i1] == 0 && nodeSigns[i2] == 0 && nodeSigns[i3] < 0); + return std::make_shared(nodeParamCoords[i2],nodeParamCoords[i1],nodeParamCoords[i7]); + } + break; + + default: + { + krinolog << "Case id " << case_id << " " << permute_case_id << stk::diag::dendl; + ThrowRuntimeError("Subelement decomposition error."); + } + } + + ThrowRuntimeError("Subelement decomposition error."); +} + +static std::shared_ptr +build_cutting_surface(const MasterElement & masterElem, + const std::vector & cutEdges, + const std::function &)> & intersectingPlanesDiagonalPicker) +{ + const stk::topology baseTopology = masterElem.get_topology().base(); + const std::vector< std::array > elemEdgeNodeOrdinals = get_element_edge_node_ordinals(baseTopology); + + const auto nodeParamCoords = get_parametric_node_coords_on_element_nodes_and_cut_edges(masterElem, cutEdges, elemEdgeNodeOrdinals); + const auto nodeSigns = get_node_signs_on_element_nodes(baseTopology, cutEdges, elemEdgeNodeOrdinals); + + if (baseTopology == stk::topology::TRIANGLE_3_2D || baseTopology == stk::topology::TRIANGLE_3) + { + ThrowRequire(cutEdges.size() == 2); + return build_triangle_cutting_surface(nodeSigns, nodeParamCoords); + } + + ThrowRequireMsg(baseTopology == stk::topology::TETRAHEDRON_4, "Unsupported base topology: " << baseTopology.name()); + ThrowRequire(cutEdges.size() == 3 || cutEdges.size() == 4); + return build_tetrahedral_cutting_surface(nodeSigns, nodeParamCoords, intersectingPlanesDiagonalPicker); +} + +bool node_captures_intersection_point_domains(const std::vector & nodeDomains, const std::vector & intersectionPointDomains) +{ + return first_sorted_vector_of_domains_contains_all_domains_in_second_vector(nodeDomains, intersectionPointDomains); +} + +template +bool any_node_captures_intersection_point_domains(const NODEDOMAINS & nodesSnappedDomains, const std::vector & intersectionPointDomains) +{ + if (nodesSnappedDomains.empty()) + return false; + for (auto && nodeSnappedDomains : nodesSnappedDomains) + if (node_captures_intersection_point_domains(*nodeSnappedDomains, intersectionPointDomains)) + return true; + return false; +} + +bool intersection_is_already_captured(const std::vector*> & elementNodesSnappedDomains, const std::vector & nodes, const std::vector & intersectionPointDomains) +{ + if (elementNodesSnappedDomains.empty()) + return false; + for (auto && node : nodes) + if (node_captures_intersection_point_domains(*elementNodesSnappedDomains[node], intersectionPointDomains)) + return true; + return false; +} + +LS_Per_Interface_Cutter::LS_Per_Interface_Cutter(const MasterElement & masterElem, + const std::vector & parentEdges, + const std::vector & areParentEdgesOrientedSameAsElementEdges, + const std::function &)> & intersectingPlanesDiagonalPicker) + : Element_Cutter(masterElem) +{ + for(auto && interface : get_interfaces_present(parentEdges)) + { + const std::vector cutEdges = build_cut_edges(interface, parentEdges, areParentEdgesOrientedSameAsElementEdges); + ThrowRequire(!cutEdges.empty()); + + cutting_surfaces[interface] = build_cutting_surface(masterElem, cutEdges, intersectingPlanesDiagonalPicker); + } +} + +bool +LS_Per_Interface_Cutter::have_crossing(const InterfaceID interface, const Segment3d & edge) const +{ + const auto iter = cutting_surfaces.find(interface); + ThrowRequire(iter != cutting_surfaces.end()); + const Cutting_Surface & interface_surface = *(iter->second); + return interface_surface.sign_at_position(edge.GetNode(0)) == -interface_surface.sign_at_position(edge.GetNode(1)); +} + +int LS_Per_Interface_Cutter::get_ls_per_interface_phase_at_location(const Vector3d & pCoords) const +{ + ThrowRequireMsg(false, "Improper usage of LS_Per_Interface_Cutter."); + return -1; +} + +void LS_Per_Interface_Cutter::add_interfaces_with_uncaptured_intersection_within_element(const std::vector & elemNodesCoords, + const std::vector *> & elemNodesSnappedDomains, + std::set & interfacesWithUncapturedCrossings) const +{ + // For LS_Per_Interface_Cutter, internal intersections only happen if there are edge intersections so nothing to do here. +} + +std::vector +LS_Per_Interface_Cutter::build_cut_edges(const InterfaceID & interface, + const std::vector & parentEdges, + const std::vector & parentEdgeIsOrientedSameAsElementEdge) +{ + std::vector cutEdges; + + for(unsigned i=0; i < parentEdges.size(); ++i) + { + const CDFEM_Parent_Edge * parent_edge = parentEdges[i]; + if(parent_edge) + { + const int crossing_sign = parent_edge->get_crossing_sign(interface); + const double crossing_pos = parent_edge->get_crossing_position(interface); + + if( crossing_pos >= 0. ) + { + const double pos = parentEdgeIsOrientedSameAsElementEdge[i] ? crossing_pos : (1 -crossing_pos); + const int sign = parentEdgeIsOrientedSameAsElementEdge[i] ? crossing_sign : (-crossing_sign); + cutEdges.push_back(Edge_Crossing(i,pos,sign)); + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "Crossing of edge " << i << " at position " << pos << "\n"; + } + } + } + return cutEdges; +} + +std::vector +One_LS_Per_Phase_Cutter::build_cut_edges(const InterfaceID & interface, + const std::vector & parentEdges, + const std::vector & parentEdgeIsOrientedSameAsElementEdge) +{ + std::vector cutEdges; + + for(unsigned i=0; i < parentEdges.size(); ++i) + { + const CDFEM_Parent_Edge * parent_edge = parentEdges[i]; + if(parent_edge) + { + double crossing_pos; + int crossing_sign; + bool is_fake; + std::tie(crossing_pos, crossing_sign, is_fake) = parent_edge->get_crossing_position_and_sign(interface); + + if( crossing_pos >= 0. ) + { + const double pos = parentEdgeIsOrientedSameAsElementEdge[i] ? crossing_pos : (1 -crossing_pos); + const int sign = parentEdgeIsOrientedSameAsElementEdge[i] ? crossing_sign : (-crossing_sign); + cutEdges.push_back(Edge_Crossing(i,pos,sign)); + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "Crossing of edge " << i << " for interface " << interface << " at position " << pos << " fake = " << std::boolalpha << is_fake << "\n"; + } + } + } + return cutEdges; +} + +std::shared_ptr One_LS_Per_Phase_Cutter::attempt_to_build_cutting_surface(const MasterElement & masterElem, + const std::vector & parentEdges, + const std::vector & areParentEdgesOrientedSameAsElementEdges, + const std::function &)> & intersectingPlanesDiagonalPicker, + const InterfaceID& interface) +{ + std::shared_ptr cuttingSurface; + try + { + const std::vector cutEdges = build_cut_edges(interface, parentEdges, areParentEdgesOrientedSameAsElementEdges); + ThrowRequire(!cutEdges.empty()); + cuttingSurface = build_cutting_surface(masterElem, cutEdges, intersectingPlanesDiagonalPicker); + } + catch (const std::exception & err) + { + std::stringstream err_msg; + err_msg << "Error while cutting element with interface " << interface << std::endl; + err_msg << err.what() << std::endl; + throw std::runtime_error(err_msg.str()); + } + return cuttingSurface; +} + +static std::set determine_optimal_phases_at_location(const Vector3d & location, + const InterfaceToSurface & allSurfaces) +{ + // optimal phases are phases those that are on the "right" side of at least one surface, + // and never on the "wrong" side of an interface + std::set toPhases; + std::set fromPhases; + + for (auto && surface : allSurfaces) + { + const int sign = surface.second->sign_at_position(location); + const int toPhase = (sign < 0) ? surface.first.first_ls() : surface.first.second_ls(); + const int fromPhase = (sign < 0) ? surface.first.second_ls() : surface.first.first_ls(); + toPhases.insert(toPhase); + fromPhases.insert(fromPhase); + } + + for (int phase : fromPhases) + toPhases.erase(phase); + + return toPhases; +} + +bool intersection_point_is_real(const Vector3d & intersectionPoint, + const InterfaceToSurface & allSurfaces, + const std::vector & sortedDomains) +{ + for (auto && surface : allSurfaces) + { + if (!surface.second->on_surface(intersectionPoint, Element_Cutter::theSnapToNodeTol)) + { + const int sign = surface.second->sign_at_position(intersectionPoint); + const int toPhase = (sign < 0) ? surface.first.first_ls() : surface.first.second_ls(); + const int fromPhase = (sign < 0) ? surface.first.second_ls() : surface.first.first_ls(); + const bool toPhaseIsInDomains = std::binary_search(sortedDomains.begin(), sortedDomains.end(), toPhase); + const bool fromPhaseIsInDomains = std::binary_search(sortedDomains.begin(), sortedDomains.end(), fromPhase); + if (!toPhaseIsInDomains && fromPhaseIsInDomains) + return false; + } + } + return true; +} + +static void add_triple_point_interfaces(const bool isOneLSPerPhase, const std::vector & triplePointDomains, std::set & interfaces) +{ + ThrowRequire(triplePointDomains.size() == 3); + if (isOneLSPerPhase) + { + interfaces.insert(InterfaceID(triplePointDomains[0],triplePointDomains[1])); + interfaces.insert(InterfaceID(triplePointDomains[1],triplePointDomains[2])); + interfaces.insert(InterfaceID(triplePointDomains[0],triplePointDomains[2])); + } + else + { + interfaces.insert(InterfaceID(triplePointDomains[0],triplePointDomains[0])); + interfaces.insert(InterfaceID(triplePointDomains[1],triplePointDomains[1])); + interfaces.insert(InterfaceID(triplePointDomains[2],triplePointDomains[2])); + } +} + +static void add_interfaces_that_have_uncaptured_intersection_within_sides_of_tet(const std::vector & elemNodesCoords, + const std::vector*> & nodesSnappedDomains, + const InterfaceToSurface & allSurfaces, + std::set & interfacesWithUncapturedCrossings) +{ + Vector3d intersectionPoint; + std::vector sortedDomains; + constexpr bool oneLSPerPhase(true); + + stk::topology topology = stk::topology::TETRAHEDRON_4; + std::vector faceNodes(3); + + for (auto && surface1 : allSurfaces) + { + for (auto && surface2 : allSurfaces) + { + if (surface1.first < surface2.first && + (interfacesWithUncapturedCrossings.count(surface1.first) == 0 || interfacesWithUncapturedCrossings.count(surface2.first))) + { + fill_sorted_domains(oneLSPerPhase, surface1.first, surface2.first, sortedDomains); + + if (sorted_domains_form_triple_point(oneLSPerPhase, sortedDomains)) + { + for (int iFace=0; iFace<4; ++iFace) + { + topology.face_node_ordinals(iFace, faceNodes); + const std::array faceNodeCoords{elemNodesCoords[faceNodes[0]], elemNodesCoords[faceNodes[1]], elemNodesCoords[faceNodes[2]]}; + const Plane3d facePlane{elemNodesCoords[faceNodes[0]], elemNodesCoords[faceNodes[1]], elemNodesCoords[faceNodes[2]]}; + if (!intersection_is_already_captured(nodesSnappedDomains, faceNodes, sortedDomains) && + find_intersection_of_three_planes(facePlane, surface1.second->get_plane(), surface2.second->get_plane(), intersectionPoint) && + intersection_point_is_real(intersectionPoint, allSurfaces, sortedDomains)) + { + const Vector3d faceCoords = triangle_parametric_coordinates_of_projected_point(faceNodeCoords, intersectionPoint); + if (within_tri_bounds(faceCoords)) + add_triple_point_interfaces(oneLSPerPhase, sortedDomains, interfacesWithUncapturedCrossings); + } + } + } + } + } + } +} + +static void add_interfaces_that_have_uncaptured_intersection_within_sides_of_tet(const std::vector*> & nodesSnappedDomains, + const InterfaceToSurface & allSurfaces, + std::set & interfacesWithUncapturedCrossings) +{ + Vector3d intersectionPoint; + std::vector sortedDomains; + constexpr bool oneLSPerPhase(true); + + stk::topology topology = stk::topology::TETRAHEDRON_4; + std::vector faceNodes(3); + + for (auto && surface1 : allSurfaces) + { + for (auto && surface2 : allSurfaces) + { + if (surface1.first < surface2.first && + (interfacesWithUncapturedCrossings.count(surface1.first) == 0 || interfacesWithUncapturedCrossings.count(surface2.first))) + { + fill_sorted_domains(oneLSPerPhase, surface1.first, surface2.first, sortedDomains); + if (sorted_domains_form_triple_point(oneLSPerPhase, sortedDomains)) + { + for (int iFace=0; iFace<4; ++iFace) + { + topology.face_node_ordinals(iFace, faceNodes); + if (!intersection_is_already_captured(nodesSnappedDomains, faceNodes, sortedDomains) && + find_intersection_of_two_planes_and_side_of_tet(iFace, surface1.second->get_plane(), surface2.second->get_plane(), intersectionPoint) && + intersection_point_is_real(intersectionPoint, allSurfaces, sortedDomains)) + add_triple_point_interfaces(oneLSPerPhase, sortedDomains, interfacesWithUncapturedCrossings); + } + } + } + } + } +} + +static void add_interfaces_that_have_uncaptured_intersection_within_tri(const std::vector & elemNodesCoords, + const std::vector*> & nodesSnappedDomains, + const InterfaceToSurface & allSurfaces, + std::set & interfacesWithUncapturedCrossings) +{ + Vector3d intersectionPoint; + std::vector sortedDomains; + constexpr bool oneLSPerPhase(true); + const std::vector triNodes = {0,1,2}; + + for (auto && surface1 : allSurfaces) + { + for (auto && surface2 : allSurfaces) + { + if (surface1.first < surface2.first && + (interfacesWithUncapturedCrossings.count(surface1.first) == 0 || interfacesWithUncapturedCrossings.count(surface2.first))) + { + fill_sorted_domains(oneLSPerPhase, surface1.first, surface2.first, sortedDomains); + if (sorted_domains_form_triple_point(oneLSPerPhase, sortedDomains) && + !intersection_is_already_captured(nodesSnappedDomains, triNodes, sortedDomains) && + find_intersection_of_two_2D_planes_within_tri(surface1.second->get_plane(), surface2.second->get_plane(), intersectionPoint) && // ASSUME THAT PROVIDED TRIANGLE CONTAINED WITHIN PARENT TRIANGLE + intersection_point_is_real(intersectionPoint, allSurfaces, sortedDomains)) + { + const std::array triNodeCoords{elemNodesCoords[0], elemNodesCoords[1], elemNodesCoords[2]}; + const Vector3d triCoords = triangle_parametric_coordinates_of_projected_point(triNodeCoords, intersectionPoint); + if (within_tri_bounds(triCoords)) + add_triple_point_interfaces(oneLSPerPhase, sortedDomains, interfacesWithUncapturedCrossings); + } + } + } + } +} + +static void add_interfaces_that_have_uncaptured_intersection_within_tri(const std::vector*> & nodesSnappedDomains, + const InterfaceToSurface& allSurfaces, + std::set & interfacesWithUncapturedCrossings) +{ + Vector3d intersectionPoint; + std::vector sortedDomains; + constexpr bool oneLSPerPhase(true); + const std::vector triNodes = {0,1,2}; + + for (auto && surface1 : allSurfaces) + { + for (auto && surface2 : allSurfaces) + { + if (surface1.first < surface2.first && + (interfacesWithUncapturedCrossings.count(surface1.first) == 0 || interfacesWithUncapturedCrossings.count(surface2.first))) + { + fill_sorted_domains(oneLSPerPhase, surface1.first, surface2.first, sortedDomains); + if (sorted_domains_form_triple_point(oneLSPerPhase, sortedDomains) && + !intersection_is_already_captured(nodesSnappedDomains, triNodes, sortedDomains) && + find_intersection_of_two_2D_planes_within_tri(surface1.second->get_plane(), surface2.second->get_plane(), intersectionPoint) && + intersection_point_is_real(intersectionPoint, allSurfaces, sortedDomains)) + add_triple_point_interfaces(oneLSPerPhase, sortedDomains, interfacesWithUncapturedCrossings); + } + } + } +} + +static void add_interfaces_that_have_uncaptured_intersection_within_element(const stk::topology & baseTopology, + const std::vector*> & nodesSnappedDomains, + const InterfaceToSurface & allSurfaces, + std::set & interfacesWithUncapturedCrossings) +{ + if (baseTopology == stk::topology::TETRAHEDRON_4) + add_interfaces_that_have_uncaptured_intersection_within_sides_of_tet(nodesSnappedDomains, allSurfaces, interfacesWithUncapturedCrossings); + else if (baseTopology == stk::topology::TRIANGLE_3 || baseTopology == stk::topology::TRIANGLE_3_2D) + add_interfaces_that_have_uncaptured_intersection_within_tri(nodesSnappedDomains, allSurfaces, interfacesWithUncapturedCrossings); + else + ThrowRequireMsg(false, "Unsupported topology " << baseTopology); +} + +static void add_interfaces_that_have_uncaptured_intersection_within_element(const stk::topology & baseTopology, + const std::vector & elemNodesCoords, + const std::vector*> & nodesSnappedDomains, + const InterfaceToSurface & allSurfaces, + std::set & interfacesWithUncapturedCrossings) +{ + if (baseTopology == stk::topology::TETRAHEDRON_4) + add_interfaces_that_have_uncaptured_intersection_within_sides_of_tet(elemNodesCoords, nodesSnappedDomains, allSurfaces, interfacesWithUncapturedCrossings); + else if (baseTopology == stk::topology::TRIANGLE_3 || baseTopology == stk::topology::TRIANGLE_3_2D) + add_interfaces_that_have_uncaptured_intersection_within_tri(elemNodesCoords, nodesSnappedDomains, allSurfaces, interfacesWithUncapturedCrossings); + else + ThrowRequireMsg(false, "Unsupported topology " << baseTopology); +} + +void One_LS_Per_Phase_Cutter::add_interfaces_with_uncaptured_intersection_within_element(const std::vector & elemNodesCoords, + const std::vector *> & elemNodesSnappedDomains, + std::set & interfacesWithUncapturedCrossings) const +{ + // Does not consider uncaptured edge crossings, these should be considered already + add_interfaces_that_have_uncaptured_intersection_within_element(myTopology.base(), elemNodesCoords, elemNodesSnappedDomains, all_cutting_surfaces, interfacesWithUncapturedCrossings); +} + +bool +One_LS_Per_Phase_Cutter::have_crossing(const InterfaceID interface, const Segment3d & edge) const +{ + const auto iter = cutting_surfaces.find(interface); + ThrowRequire(iter != cutting_surfaces.end()); + const Cutting_Surface & interface_surface = *(iter->second); + if (interface_surface.sign_at_position(edge.GetNode(0)) == -interface_surface.sign_at_position(edge.GetNode(1))) + { + const double loc = interface_crossing_position(interface, edge); + const Vector3d intersectionPoint = (1.-loc)*edge.GetNode(0) + loc*edge.GetNode(1); + return krino::intersection_point_is_real(intersectionPoint, all_cutting_surfaces, {interface.first_ls(), interface.second_ls()}); + } + return false; +} + +int One_LS_Per_Phase_Cutter::get_ls_per_interface_phase_at_location(const Vector3d & pCoords) const +{ + const std::set optimalPhases = determine_optimal_phases_at_location(pCoords, all_cutting_surfaces); + ThrowRequireMsg(optimalPhases.size()==1, "Unexpected phase configuration with " << optimalPhases.size() << " optimal phases when evaluated phase at " << pCoords << "\n" << visualize()); + return *optimalPhases.begin(); +} + +bool One_LS_Per_Phase_Cutter::intersection_point_is_real(const Vector3d & intersectionPoint, + const std::vector & sortedDomains) const +{ + return krino::intersection_point_is_real(intersectionPoint, all_cutting_surfaces, sortedDomains); +} + +One_LS_Per_Phase_Cutter::One_LS_Per_Phase_Cutter(const MasterElement & masterElem, + const std::vector & parentEdges, + const std::vector & areParentEdgesOrientedSameAsElementEdges, + const std::function &)> & intersectingPlanesDiagonalPicker) + : Element_Cutter(masterElem) +{ + std::set edgePhases = get_phases_present_on_edges_and_interior(parentEdges); + for (int phase1 : edgePhases) + { + for (int phase2 : edgePhases) + { + if (phase2 > phase1) + { + const InterfaceID interface(phase1, phase2); + all_cutting_surfaces[interface] = attempt_to_build_cutting_surface(masterElem, parentEdges, areParentEdgesOrientedSameAsElementEdges, intersectingPlanesDiagonalPicker, interface); + } + } + } + + std::set interfacesWithRealCrossings; + for (auto && interface : get_interfaces_present(parentEdges)) + interfacesWithRealCrossings.insert(interface); + + const std::vector*> emptyNodesSnappedDomains; + add_interfaces_that_have_uncaptured_intersection_within_element(myTopology.base(), emptyNodesSnappedDomains, all_cutting_surfaces, interfacesWithRealCrossings); + + for (auto && interface : interfacesWithRealCrossings) + cutting_surfaces[interface] = all_cutting_surfaces.at(interface); +} + +} diff --git a/packages/krino/krino/krino_lib/Akri_Element_Cutter.hpp b/packages/krino/krino/krino_lib/Akri_Element_Cutter.hpp new file mode 100644 index 000000000000..0d5a0c2e907f --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Element_Cutter.hpp @@ -0,0 +1,132 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_ELEMENT_CUTTER_H_ +#define KRINO_INCLUDE_AKRI_ELEMENT_CUTTER_H_ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino { + +class Cutting_Surface; +class CDFEM_Parent_Edge; +class ElementIntersection; + +typedef std::function &)> ElementIntersectionPointFilter; +typedef std::map> InterfaceToSurface; + +class Element_Cutter +{ +public: + Element_Cutter(const MasterElement & masterElement) : myTopology(masterElement.get_topology()) {} + virtual ~Element_Cutter() {} + double interface_crossing_position(const InterfaceID interface, const Segment3d & edge) const; + int sign_at_position(const InterfaceID interface, const Vector3d & p_coords) const; + virtual bool have_crossing(const InterfaceID interface, const Segment3d & edge) const = 0; + virtual int get_ls_per_interface_phase_at_location(const Vector3d & pCoords) const = 0; + virtual void add_interfaces_with_uncaptured_intersection_within_element(const std::vector & elemNodesCoords, + const std::vector *> & elemNodesSnappedDomains, + std::set & interfacesWithUncapturedCrossings) const = 0; + + int get_num_cutting_surfaces() const {return cutting_surfaces.size(); } + bool have_cutting_surface(const InterfaceID interface) const; + void fill_interfaces_with_cutting_surface(std::vector & interfaces) const; + Cutting_Surface * get_cutting_surface(const InterfaceID interface) const; + + virtual bool is_one_ls_per_phase() const = 0; + void fill_interior_intersections(const ElementIntersectionPointFilter & intersectionPointFilter, std::vector & intersections) const; + void fill_interior_intersections(std::vector & intersections) const; + void fill_tetrahedron_face_interior_intersections(const std::array & faceNodes, const InterfaceID & interface1, const InterfaceID & interface2, const ElementIntersectionPointFilter & intersectionPointFilter, std::vector & intersections) const; + static std::string visualize_cutting_surfaces(const stk::topology & topology, const InterfaceToSurface & cuttingSurfaces); + virtual std::string visualize() const { return visualize_cutting_surfaces(myTopology, cutting_surfaces); } + + int get_starting_phase_for_cutting_surfaces() const; + + struct Edge_Crossing { + Edge_Crossing(unsigned e, double p, int s) : edge(e), pos(p), sign(s) {} + unsigned edge; + double pos; + int sign; + }; + + static constexpr double theSnapToNodeTol{1.e-12}; + +protected: + stk::topology myTopology; + InterfaceToSurface cutting_surfaces; + +private: + virtual bool intersection_point_is_real(const Vector3d & intersection, const std::vector & sortedDomains) const = 0; + void fill_triangle_interior_intersection_points(const ElementIntersectionPointFilter & intersectionPointFilter, std::vector & intersections) const; + void fill_tetrahedron_interior_intersection_points(const ElementIntersectionPointFilter & intersectionPointFilter, std::vector & intersections) const; +}; + +class LS_Per_Interface_Cutter : public Element_Cutter +{ +public: + LS_Per_Interface_Cutter(const MasterElement & masterElem, + const std::vector & parentEdges, + const std::vector & areParentEdgesOrientedSameAsElementEdges, + const std::function &)> & intersectingPlanesDiagonalPicker); + virtual bool is_one_ls_per_phase() const override { return false; } + virtual bool have_crossing(const InterfaceID interface, const Segment3d & edge) const override; + virtual int get_ls_per_interface_phase_at_location(const Vector3d & pCoords) const override; + virtual void add_interfaces_with_uncaptured_intersection_within_element(const std::vector & elemNodesCoords, + const std::vector *> & elemNodesSnappedDomains, + std::set & interfacesWithUncapturedCrossings) const override; +private: + virtual bool intersection_point_is_real(const Vector3d & intersection, const std::vector & sortedDomains) const override { return true; } + static std::vector build_cut_edges(const InterfaceID & interface, + const std::vector & parentEdges, + const std::vector & parentEdgeIsOrientedSameAsElementEdge); +}; + +class One_LS_Per_Phase_Cutter : public Element_Cutter +{ +public: + One_LS_Per_Phase_Cutter(const MasterElement & masterElem, + const std::vector & parentEdges, + const std::vector & areParentEdgesOrientedSameAsElementEdges, + const std::function &)> & intersectingPlanesDiagonalPicker); + virtual bool is_one_ls_per_phase() const override { return true; } + virtual bool have_crossing(const InterfaceID interface, const Segment3d & edge) const override; + virtual int get_ls_per_interface_phase_at_location(const Vector3d & pCoords) const override; + virtual void add_interfaces_with_uncaptured_intersection_within_element(const std::vector & elemNodesCoords, + const std::vector *> & elemNodesSnappedDomains, + std::set & interfacesWithUncapturedCrossings) const override; + virtual std::string visualize() const override { return visualize_cutting_surfaces(myTopology, all_cutting_surfaces); } + +private: + virtual bool intersection_point_is_real(const Vector3d & intersection, const std::vector & sortedDomains) const override; + static std::vector build_cut_edges(const InterfaceID & interface, + const std::vector & parentEdges, + const std::vector & parentEdgeIsOrientedSameAsElementEdge); + static std::shared_ptr attempt_to_build_cutting_surface(const MasterElement & masterElem, + const std::vector & parentEdges, + const std::vector & areParentEdgesOrientedSameAsElementEdges, + const std::function &)> & intersectingPlanesDiagonalPicker, + const InterfaceID& interface); + + InterfaceToSurface all_cutting_surfaces; +}; + +std::unique_ptr create_element_cutter(const bool oneLSPerPhase, + const MasterElement & masterElem, + const std::vector & elementParentEdges, + const std::vector & areParentEdgesAreOrientedSameAsElementEdges, + const std::function &)> & intersectingPlanesDiagonalPicker); + +} + +#endif /* KRINO_INCLUDE_AKRI_ELEMENT_CUTTER_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_Element_Intersections.cpp b/packages/krino/krino/krino_lib/Akri_Element_Intersections.cpp new file mode 100644 index 000000000000..04d59b4e3da8 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Element_Intersections.cpp @@ -0,0 +1,28 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include + + +namespace krino { + +std::ostream & operator<<(std::ostream & os, const ElementIntersection & elementIntersection) +{ + os << elementIntersection.parametricCoords << ", interfaces={ "; + for (int domain : elementIntersection.sortedDomains) + os << domain << " "; + os << "}"; + return os; +} + +} + diff --git a/packages/krino/krino/krino_lib/Akri_Element_Intersections.hpp b/packages/krino/krino/krino_lib/Akri_Element_Intersections.hpp new file mode 100644 index 000000000000..2931938ffc81 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Element_Intersections.hpp @@ -0,0 +1,34 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_ELEMENT_INTERSECTIONS_H_ +#define KRINO_INCLUDE_AKRI_ELEMENT_INTERSECTIONS_H_ + +#include +#include +#include + +namespace krino { + +class Element_Cutter; + +struct ElementIntersection +{ + ElementIntersection(const Vector3d & coords, const std::vector & domains) + : parametricCoords(coords), + sortedDomains(domains) {} + + Vector3d parametricCoords; + std::vector sortedDomains; +}; + +std::ostream & operator<<(std::ostream & os, const ElementIntersection& elementIntersection); + +} + +#endif /* KRINO_INCLUDE_AKRI_ELEMENT_INTERSECTIONS_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_EntityIdPool.cpp b/packages/krino/krino/krino_lib/Akri_EntityIdPool.cpp new file mode 100644 index 000000000000..6fcebc0157f7 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_EntityIdPool.cpp @@ -0,0 +1,92 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include + +namespace krino{ + +EntityIdPool::EntityIdPool(stk::mesh::MetaData & meta_data) + : my_meta_data(meta_data), + my_entity_id_pool(meta_data.entity_rank_count()) +{ +} + +stk::mesh::EntityId +EntityIdPool::get_EntityId(stk::mesh::EntityRank rank) +{ + ThrowAssert(static_cast(rank) < my_entity_id_pool.size()); + ThrowRequireMsg(!my_entity_id_pool[rank].empty(), "EntityIdPool is empty for " << rank << "."); + + stk::mesh::EntityId entity_id = my_entity_id_pool[rank].back(); + my_entity_id_pool[rank].pop_back(); + return entity_id; +} + +void +EntityIdPool::reserve(stk::mesh::EntityRank rank, size_t count, bool assert_32bit_ids, bool make_64bit_ids) +{ + my_meta_data.mesh_bulk_data().generate_new_ids( rank, count, my_entity_id_pool[rank] ); + ThrowAssert(!make_64bit_ids || !assert_32bit_ids); + if (make_64bit_ids) + { + push_ids_to_64_bit(my_entity_id_pool[rank]); + } + if (assert_32bit_ids && (rank == stk::topology::NODE_RANK || rank == stk::topology::ELEMENT_RANK)) // Only worry about node and elements since they are output + { + check_ids_are_32_bit(rank, my_entity_id_pool[rank]); + } +} + +void +EntityIdPool::generate_new_ids(stk::mesh::BulkData & mesh, stk::mesh::EntityRank rank, size_t count, std::vector & ids, bool assert_32bit_ids, bool make_64bit_ids) +{ + mesh.generate_new_ids( rank, count, ids ); + ThrowAssert(!make_64bit_ids || !assert_32bit_ids); + if (make_64bit_ids) + { + push_ids_to_64_bit(ids); + } + if (assert_32bit_ids && (rank == stk::topology::NODE_RANK || rank == stk::topology::ELEMENT_RANK)) // Only worry about node and elements since they are output + { + check_ids_are_32_bit(rank, ids); + } +} + +void +EntityIdPool::push_ids_to_64_bit(std::vector & ids) +{ + const uint64_t max_32_bit_id = std::numeric_limits::max(); + const bool ids_are_32_bit = !ids.empty() && ids[0] <= max_32_bit_id; + if (ids_are_32_bit) + { + for (auto && id : ids) + { + ThrowRequireMsg(id <= max_32_bit_id, "Mixture of ids above and below 32 bit limit not allowed in push_ids_to_64_bit."); + id += max_32_bit_id; + } + } +} + +void +EntityIdPool::check_ids_are_32_bit(stk::mesh::EntityRank rank, std::vector & ids) +{ + static const uint64_t max_allowed_id = std::numeric_limits::max(); + bool have_bad_id = false; + for (auto && id : ids) + { + if (id > max_allowed_id) + { + have_bad_id = true; + break; + } + } + ThrowRequireMsg(!have_bad_id, "Exhausted valid ids for rank " << rank << "!"); +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_EntityIdPool.hpp b/packages/krino/krino/krino_lib/Akri_EntityIdPool.hpp new file mode 100644 index 000000000000..4ecea3088548 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_EntityIdPool.hpp @@ -0,0 +1,39 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_EntityIdPool_h +#define Akri_EntityIdPool_h + +#include +#include + +namespace stk { namespace mesh { class BulkData; } } + +namespace krino { + +class EntityIdPool { +public: + EntityIdPool(stk::mesh::MetaData & meta_data); + ~EntityIdPool() {} + + static void generate_new_ids(stk::mesh::BulkData & mesh, stk::mesh::EntityRank rank, size_t count, std::vector & ids, bool assert_32bit_ids, bool make_64bit_ids); + + void reserve(stk::mesh::EntityRank rank, size_t count, bool assert_32bit_ids, bool make_64bit_ids); + stk::mesh::EntityId get_EntityId(stk::mesh::EntityRank rank); +private: + static void push_ids_to_64_bit(std::vector & ids); + static void check_ids_are_32_bit(stk::mesh::EntityRank rank, std::vector & ids); + +protected: + stk::mesh::MetaData & my_meta_data; + std::vector< std::vector > my_entity_id_pool; +}; + +} // namespace krino + +#endif // Akri_EntityIdPool_h diff --git a/packages/krino/krino/krino_lib/Akri_Facet.cpp b/packages/krino/krino/krino_lib/Akri_Facet.cpp new file mode 100644 index 000000000000..9744abd1fca0 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Facet.cpp @@ -0,0 +1,197 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include + +namespace krino{ + +// +//-------------------------------------------------------------------------------- + +std::unique_ptr +Facet::unpack_from_buffer( stk::CommBuffer & b ) +{ /* %TRACE% */ /* %TRACE% */ + std::unique_ptr facet; + + int dim = 0; + b.unpack(dim); + + switch(dim) + { + case 2: facet = Facet2d::unpack_from_buffer(b); break; + case 3: facet = Facet3d::unpack_from_buffer(b); break; + default: ThrowRuntimeError("Unrecognized facet dimension."); + } + + ThrowRequire(facet); + return facet; +} + +//-------------------------------------------------------------------------------- + +void +Facet2d::pack_into_buffer(stk::CommBuffer & b) const +{ + const int dim = 2; + b.pack(dim); + for (int n = 0; n < 2; ++n ) + { + const Vector3d & pt = facet_vertex(n); + b.pack(pt[0]); + b.pack(pt[1]); + } +} + +std::unique_ptr +Facet2d::unpack_from_buffer( stk::CommBuffer & b ) +{ /* %TRACE% */ /* %TRACE% */ + std::unique_ptr facet; + + double vx, vy; + b.unpack(vx); + b.unpack(vy); + Vector3d x0( vx, vy, 0.0 ); + b.unpack(vx); + b.unpack(vy); + Vector3d x1( vx, vy, 0.0 ); + + facet = std::make_unique( x0, x1 ); + return facet; +} + +void +Facet3d::pack_into_buffer(stk::CommBuffer & b) const +{ + const int dim = 3; + b.pack(dim); + for (int n = 0; n < 3; ++n ) + { + const Vector3d & pt = facet_vertex(n); + b.pack(pt[0]); + b.pack(pt[1]); + b.pack(pt[2]); + } +} + +std::unique_ptr +Facet3d::unpack_from_buffer( stk::CommBuffer & b ) +{ /* %TRACE% */ /* %TRACE% */ + std::unique_ptr facet; + + double vx, vy, vz; + b.unpack(vx); + b.unpack(vy); + b.unpack(vz); + Vector3d x0( vx, vy, vz ); + b.unpack(vx); + b.unpack(vy); + b.unpack(vz); + Vector3d x1( vx, vy, vz ); + b.unpack(vx); + b.unpack(vy); + b.unpack(vz); + Vector3d x2( vx, vy, vz ); + + facet = std::make_unique( x0, x1, x2 ); + return facet; +} + +std::ostream & Facet3d::put( std::ostream & os ) const +{ /* %TRACE% */ /* %TRACE% */ + // facet info + os << ": facet description: " << std::endl + << " facet point 0 = (" + << facet_vertex(0)[0] << "," + << facet_vertex(0)[1] << "," + << facet_vertex(0)[2] << ")" << std::endl + << " facet point 1 = (" + << facet_vertex(1)[0] << "," + << facet_vertex(1)[1] << "," + << facet_vertex(1)[2] << ")" << std::endl + << " facet point 2 = (" + << facet_vertex(2)[0] << "," + << facet_vertex(2)[1] << "," + << facet_vertex(2)[2] << ")" << std::endl + << " facet area = " << facet_area() << std::endl; + + return os ; +} + +void Facet3d::apply_transformation(const Transformation & transformation) +{ + Vector3d pt0 = facet_vertex(0); + Vector3d pt1 = facet_vertex(1); + Vector3d pt2 = facet_vertex(2); + + transformation.apply(pt0); + transformation.apply(pt1); + transformation.apply(pt2); + + my_facet_tri = Triangle3d( pt0, pt1 , pt2 ); + + my_bounding_box.clear(); + my_bounding_box.accommodate(pt0); + my_bounding_box.accommodate(pt1); + my_bounding_box.accommodate(pt2); +} + +void Facet2d::apply_transformation(const Transformation & transformation) +{ + Vector3d pt0 = facet_vertex(0); + Vector3d pt1 = facet_vertex(1); + + transformation.apply(pt0); + transformation.apply(pt1); + + // create facet segment + my_facet_segment = Segment3d( pt0, pt1 ); + + my_bounding_box.clear(); + my_bounding_box.accommodate(pt0); + my_bounding_box.accommodate(pt1); +} + +std::ostream & Facet2d::put( std::ostream & os ) const +{ /* %TRACE% */ /* %TRACE% */ + // facet info + os << ": facet description: " << std::endl + << " facet point 0 = (" + << facet_vertex(0)[0] << "," + << facet_vertex(0)[1] << ")" << std::endl + << " facet point 1 = (" + << facet_vertex(1)[0] << "," + << facet_vertex(1)[1] << ")" << std::endl + << " facet area = " << facet_area() << std::endl; + + return os ; +} + +Facet3d::Facet3d( const Vector3d & pt0, + const Vector3d & pt1, + const Vector3d & pt2 ) + : Facet(), + my_facet_tri( pt0, pt1, pt2 ) +{ /* %TRACE% */ /* %TRACE% */ + my_bounding_box.accommodate(pt0); + my_bounding_box.accommodate(pt1); + my_bounding_box.accommodate(pt2); +} + +Facet2d::Facet2d( const Vector3d & pt0, + const Vector3d & pt1 ) + : Facet(), + my_facet_segment(pt0, pt1) +{ /* %TRACE% */ /* %TRACE% */ + my_bounding_box.accommodate(pt0); + my_bounding_box.accommodate(pt1); +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_Facet.hpp b/packages/krino/krino/krino_lib/Akri_Facet.hpp new file mode 100644 index 000000000000..b9da02cdb513 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Facet.hpp @@ -0,0 +1,158 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Facet_h +#define Akri_Facet_h + +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace stk { class CommBuffer; } + +namespace krino { + +class Facet; +class Transformation; + +typedef std::vector< Facet * > FacetVec; +typedef std::vector< std::unique_ptr > FacetOwningVec; + +class Facet { +public: + Facet() {} + virtual ~Facet() {} + + // for debugging memory usage + virtual size_t storage_size() const = 0; + + virtual std::ostream & put( std::ostream& os ) const = 0; + friend std::ostream & operator << ( std::ostream &os , const Facet &f ) { return f.put(os); } + + // methods for off-processor communication + static std::unique_ptr unpack_from_buffer( stk::CommBuffer & b ); // static method that builds facet from data in buffer for off-processor communication + virtual void pack_into_buffer(stk::CommBuffer & b) const = 0; // pack into buffer for off-processor communication + + virtual Vector3d facet_vertex(const int i) const = 0; + virtual double facet_area() const = 0; + virtual Vector3d facet_normal() const = 0; + virtual bool degenerate() const = 0; + + virtual double point_distance_squared( const Vector3d & x ) const = 0; + virtual double point_distance_squared( const Vector3d & x, Vector2d & parametric_coords ) const = 0; + virtual int point_distance_sign( const Vector3d & x ) const = 0; + virtual Vector3d real_coordinates(const Vector2d & parametric_coords) const = 0; + virtual Vector3d weights(const Vector2d & parametric_coords) const = 0; + + virtual void apply_transformation(const Transformation & transformation) = 0; + + const BoundingBox & bounding_box() const { ThrowAssert (my_bounding_box.valid()); return my_bounding_box; } + static const BoundingBox & get_bounding_box(const Facet * facet) { return facet->bounding_box(); } + +protected: + BoundingBox my_bounding_box; +}; + +class Facet3d : public Facet { +public: + Facet3d( const Vector3d & x0, + const Vector3d & x1, + const Vector3d & x2 ); + Facet3d() = delete; + virtual ~Facet3d() {} + + virtual size_t storage_size() const { return sizeof(Facet3d); } + virtual std::ostream & put( std::ostream& os ) const; + + static std::unique_ptr unpack_from_buffer( stk::CommBuffer & b ); // static method that builds facet from data in buffer for off-processor communication + virtual void pack_into_buffer(stk::CommBuffer & b) const; + + virtual Vector3d facet_vertex(const int i) const { return my_facet_tri.GetNode(i); } + virtual double facet_area() const { return my_facet_tri.area(); } + virtual Vector3d facet_normal() const { return my_facet_tri.normal(); } + virtual bool degenerate() const { return my_facet_tri.normal_dir().zero_length(); } + virtual double point_distance_squared( const Vector3d & x ) const + { return my_facet_tri.DistanceSquared( x ); } + virtual double point_distance_squared( const Vector3d & x, Vector2d & parametric_coords ) const + { return my_facet_tri.DistanceSquared( x, parametric_coords ); } + virtual int point_distance_sign( const Vector3d & x ) const + { return (Dot(my_facet_tri.normal_dir(),x-facet_vertex(0)) < 0.0) ? -1 : 1; } + virtual Vector3d real_coordinates(const Vector2d & parametric_coords) const + { return my_facet_tri.ParametricToRealCoords(parametric_coords); } + virtual Vector3d weights(const Vector2d & parametric_coords) const + { return Vector3d(1.0-parametric_coords[0]-parametric_coords[1],parametric_coords[0],parametric_coords[1]); } + virtual void apply_transformation(const Transformation & transformation); +private: + Triangle3d my_facet_tri; +}; + +class Facet2d : public Facet { +public: + Facet2d( const Vector3d & x0, + const Vector3d & x1 ); + Facet2d() = delete; + virtual ~Facet2d() {} + + virtual size_t storage_size() const { return sizeof(Facet2d); } + virtual std::ostream & put( std::ostream& os ) const; + + static std::unique_ptr unpack_from_buffer( stk::CommBuffer & b ); // static method that builds facet from data in buffer for off-processor communication + virtual void pack_into_buffer(stk::CommBuffer & b) const; + + virtual Vector3d facet_vertex(const int i) const { return my_facet_segment.GetNode(i); } + virtual double facet_area() const { return my_facet_segment.Length(); } + virtual Vector3d facet_normal() const + { return (crossZ(facet_vertex(1)-facet_vertex(0))).unit_vector(); } + virtual bool degenerate() const { return (0.0 == my_facet_segment.Length()); } + virtual double point_distance_squared( const Vector3d & x ) const + { return my_facet_segment.DistanceSquared(x); } + virtual double point_distance_squared( const Vector3d & x, Vector2d & parametric_coords ) const + { return my_facet_segment.DistanceSquared(x, parametric_coords[0]); } + virtual int point_distance_sign( const Vector3d & x ) const + { return (Dot(crossZ(facet_vertex(1)-facet_vertex(0)), x-facet_vertex(0)) < 0.0) ? -1 : 1; } + virtual Vector3d real_coordinates(const Vector2d & parametric_coords) const + { return (1.0-parametric_coords[0])*my_facet_segment.GetNode(0) + parametric_coords[0]*my_facet_segment.GetNode(1); } + virtual Vector3d weights(const Vector2d & parametric_coords) const + { return Vector3d(1.0-parametric_coords[0],parametric_coords[0],0.0); } + virtual void apply_transformation(const Transformation & transformation); +private: + Segment3d my_facet_segment; +}; + +class FacetDistanceQuery { +public: + FacetDistanceQuery() : my_facet(nullptr), my_sqr_distance(0.0) {} + FacetDistanceQuery(const Facet & in_facet, const Vector3d & x) : my_facet(&in_facet), my_query_pt(x) + { + my_sqr_distance = my_facet->point_distance_squared(x, my_parametric_coords); + } + + bool empty() const { return my_facet == nullptr; } + const Facet & facet() const { return *my_facet; } + double distance_squared() const { return my_sqr_distance; } + Vector3d closest_point() const { return my_facet->real_coordinates(my_parametric_coords); } + double signed_distance() const { return std::sqrt(my_sqr_distance)*my_facet->point_distance_sign(my_query_pt); } + Vector3d closest_point_weights() const { return my_facet->weights(my_parametric_coords); } + +private: + const Facet* my_facet; + Vector3d my_query_pt; + double my_sqr_distance; + Vector2d my_parametric_coords; +}; + +} // namespace krino + +#endif // Akri_Facet_h diff --git a/packages/krino/krino/krino_lib/Akri_Faceted_Surface.cpp b/packages/krino/krino/krino_lib/Akri_Faceted_Surface.cpp new file mode 100644 index 000000000000..1c8eca3243f1 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Faceted_Surface.cpp @@ -0,0 +1,494 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include + +#include +#include +#include + +namespace krino{ + +Faceted_Surface::Faceted_Surface(const std::string & sn) + : SurfaceThatTakesAdvantageOfNarrowBandAndThereforeMightHaveWrongSign(), + my_name(sn) {} + +void +Faceted_Surface::parallel_distribute_facets(const size_t batch_size, const std::vector & proc_bboxes) +{ + const int num_procs = stk::EnvData::parallel_size(); + if ( num_procs == 1 ) return; + + const int me = stk::EnvData::parallel_rank(); + ThrowRequire(me != 0 || batch_size <= my_local_facets.size()); + + stk::CommSparse comm_sparse(stk::EnvData::parallel_comm()); + + std::vector dest_procs; + std::vector proc_facet_counts; + size_t start = 0; + if (me == 0) + { + dest_procs.resize(batch_size, 0); + proc_facet_counts.resize(num_procs, 0); + start = my_local_facets.size() - batch_size; + for ( size_t index = 0; index < batch_size; ++index ) + { + const Facet * facet = my_local_facets[start+index].get(); + for ( int dest_proc = 1; dest_proc < num_procs; ++dest_proc ) + { + if ( proc_bboxes[dest_proc].intersects(facet->bounding_box()) ) + { + dest_procs[index] = dest_proc; + ++(proc_facet_counts[dest_proc]); + break; + } + } + if (dest_procs[index] == 0) + { + ++(proc_facet_counts[0]); + } + } + } + + // Communication involves two steps, the first one sizes the messages, the second one actually packs and sends the messages + for ( int comm_step = 0; comm_step < 2; ++comm_step) + { + if (me == 0) + { + for ( int dest_proc = 0; dest_proc < num_procs; ++dest_proc ) + { + if (comm_step == 0 && krinolog.shouldPrint(LOG_DEBUG)) + { + krinolog << "P" << me << ":" + << " Packaging " << proc_facet_counts[dest_proc] + << " facets for proc#" << dest_proc << stk::diag::dendl; + } + + stk::CommBuffer & b = comm_sparse.send_buffer(dest_proc); + b.pack(proc_facet_counts[dest_proc]); + + for ( size_t index = 0; index < batch_size; ++index ) + { + if (dest_procs[index] == dest_proc) + { + const Facet * facet = my_local_facets[start+index].get(); + facet->pack_into_buffer(b); + } + } + } + } + if (comm_step == 0) + { //allocation step + comm_sparse.allocate_buffers(); + } + else + { //communication step + comm_sparse.communicate(); + } + } + + if (me == 0) + { + // delete facets that have been sent away (even if they are headed back to 0) + my_local_facets.erase(my_local_facets.begin()+start, my_local_facets.end()); + } + + // unload, creating locally owned copy of facet + const int recv_proc = 0; + stk::CommBuffer & b = comm_sparse.recv_buffer(recv_proc); + + size_t proc_num_facets_recvd = 0; + b.unpack(proc_num_facets_recvd); + + if(krinolog.shouldPrint(LOG_DEBUG)) + krinolog << "P" << stk::EnvData::parallel_rank() << ":" << " Receiving " << proc_num_facets_recvd << " facets from proc#" << recv_proc << stk::diag::dendl; + + for ( size_t n = 0; n < proc_num_facets_recvd; ++n ) + { + std::unique_ptr facet = Facet::unpack_from_buffer( b ); + my_local_facets.emplace_back( std::move(facet) ); + } + ThrowAssert( 0 == b.remaining() ); +} + +void +Faceted_Surface::pack_into_buffer(stk::CommBuffer & b) const +{ + ThrowRuntimeError("pack_into_buffer should not be called for a Faceted_Surface."); +} + +void +Faceted_Surface::prepare_to_compute(const double time, const BoundingBox & point_bbox, const double truncation_length) +{ /* %TRACE[ON]% */ Trace trace__("krino::Faceted_Surface::prepare_to_compute(const double time, const BoundingBox & point_bbox, const double truncation_length)"); /* %TRACE% */ + + build_local_facets(point_bbox); + + if (my_transformation != nullptr) + { + for ( auto&& facet : my_local_facets ) + { + facet->apply_transformation(*my_transformation); + } + } + + my_bounding_box.clear(); + for (auto && facet : my_local_facets) + { + my_bounding_box.accommodate(facet->bounding_box()); + } + + my_all_facets.clear(); + for ( auto&& facet : my_local_facets ) + { + my_all_facets.push_back(facet.get()); + } + + // Get all remote facets that might be closest to this processors query points + gather_nonlocal_facets(point_bbox, truncation_length); + + if(krinolog.shouldPrint(LOG_DEBUG)) + krinolog << "P" << stk::EnvData::parallel_rank() << ":" << " Building facet tree for " << my_all_facets.size() << " facets." << stk::diag::dendl; + + my_facet_tree = std::make_unique>( my_all_facets, Facet::get_bounding_box ); + + if ( krinolog.shouldPrint(LOG_DEBUG) ) + krinolog << "P" << stk::EnvData::parallel_rank() << ": After building search tree, storage size is " << storage_size()/(1024.*1024.) << " Mb." << stk::diag::dendl; +} + +void +Faceted_Surface::gather_nonlocal_facets(const BoundingBox & point_bbox, const double truncation_length) +{ /* %TRACE[ON]% */ Trace trace__("krino::Faceted_Surface::get_nonlocal_descendants(const BoundingBox & point_bbox, const double truncation_length)"); /* %TRACE% */ + + // If truncation length is specified, get all of the facets that are within + // our padded processor's bounding box. + // To do this, see if any local facets lie in the padded nodal bounding box + // of another proc. if so, send them a copy of those facets. + + my_nonlocal_facets.clear(); + + const int num_procs = stk::EnvData::parallel_size(); + if ( num_procs == 1) return; + + // We estimate the padding based on the uppoer bound of the distance from this processor's nodal bounding box + // to the other processors bounding box of facets. This calculation requires that that local + // descendants are already gathered. + + std::vector facet_bboxes; + BoundingBox::gather_bboxes( my_bounding_box, facet_bboxes ); + + double bbox_padding = std::numeric_limits::max(); + for ( int p = 0; p < num_procs; ++p ) + { + const BoundingBox & proc_facet_bbox = facet_bboxes[p]; + if (proc_facet_bbox.valid()) + { + const double upperBnd = std::sqrt(proc_facet_bbox.SqrDistUpperBnd(point_bbox)); + bbox_padding = std::min(bbox_padding,upperBnd); + } + } + if (std::numeric_limits::max() == bbox_padding) + { + bbox_padding = truncation_length; // only should happen for no facets anywhere + } + if (truncation_length > 0.0) + { + bbox_padding = std::min(bbox_padding, truncation_length); + } + + // gather the bounding box sizes for all procs + BoundingBox local_bbox = point_bbox; + std::vector bboxes; + local_bbox.pad(bbox_padding); + BoundingBox::gather_bboxes( local_bbox, bboxes ); + + // determine which facets will be sent to each processor and formulate message sizes + const int me = stk::EnvData::parallel_rank(); + size_t me_intersecting_facet_counts = 0; + std::vector intersecting_facets; + intersecting_facets.reserve(my_all_facets.size()); + + // Perform communication in stages. In the nth stage, processor, p, + // sends facets to processor p+n and receives from p-n. + // In the 0th stage, each processor count how many of its own facets are + // are within that processor's nodal bounding box. + for ( int comm_partner = 0; comm_partner < num_procs; ++comm_partner ) + { + stk::CommSparse comm_sparse(stk::EnvData::parallel_comm()); + + const int dest_proc = (me+comm_partner) % num_procs; + + // Communication involves two steps, the first one sizes the messages, the second one actually packs and sends the messages + for ( int comm_step = 0; comm_step < 2; ++comm_step) + { + if (comm_step == 0) + { + const BoundingBox & proc_bbox = bboxes[dest_proc]; + intersecting_facets.clear(); + if (proc_bbox.intersects(my_bounding_box)) + { + for ( auto&& facet : my_all_facets ) + { + if ( proc_bbox.intersects(facet->bounding_box()) ) + { + intersecting_facets.push_back(facet); + } + } + } + if (dest_proc == me) me_intersecting_facet_counts = intersecting_facets.size(); + + if(krinolog.shouldPrint(LOG_DEBUG)) + krinolog << "P" << me << ":" << " Packaging " << intersecting_facets.size() << " facets for proc#" << dest_proc << stk::diag::dendl; + } + + stk::CommBuffer & b = comm_sparse.send_buffer(dest_proc); + if (dest_proc != me) // Don't talk to yourself, it's embarrassing + { + const size_t intersecting_facets_size = intersecting_facets.size(); + b.pack(intersecting_facets_size); + for ( auto&& facet : intersecting_facets ) + { + facet->pack_into_buffer(b); + } + } + + if (comm_step == 0) + { //allocation step + comm_sparse.allocate_buffers(); + } + else + { //communication step + comm_sparse.communicate(); + } + } + + // unload, creating local copies of nonlocal facets + + const int recv_proc = (me+num_procs-comm_partner) % num_procs; + + if (recv_proc != me) + { + stk::CommBuffer & b = comm_sparse.recv_buffer(recv_proc); + + size_t proc_num_facets_recvd = 0; + b.unpack(proc_num_facets_recvd); + + if(krinolog.shouldPrint(LOG_DEBUG)) + krinolog << "P" << stk::EnvData::parallel_rank() << ":" << " Receiving " << proc_num_facets_recvd << " facets from proc#" << recv_proc << stk::diag::dendl; + + for ( size_t n = 0; n < proc_num_facets_recvd; ++n ) + { + std::unique_ptr facet = Facet::unpack_from_buffer( b ); + my_nonlocal_facets.emplace_back( std::move(facet) ); + } + ThrowAssert( 0 == b.remaining() ); + } + } + + // only retain intersecting local descendants + if (my_all_facets.size() != me_intersecting_facet_counts) + { + FacetVec local_descendants; + local_descendants.reserve(me_intersecting_facet_counts); + for ( auto&& facet : my_all_facets ) + { + if ( local_bbox.intersects(facet->bounding_box()) ) + { + local_descendants.push_back( facet ); + } + } + my_all_facets.swap(local_descendants); + } + + // copy nonlocal facets into my_descendants + my_all_facets.reserve(my_all_facets.size()+ my_nonlocal_facets.size()); + for (auto && nonlocal_descendant : my_nonlocal_facets) { my_all_facets.push_back(nonlocal_descendant.get()); } +} + +double +Faceted_Surface::point_distance(const Vector3d &x, const double narrow_band_size, const double far_field_value, const bool compute_signed_distance) const +{ /* %TRACE% */ /* %TRACE% */ + ThrowAssertMsg(my_facet_tree, "ERROR: Empty facet tree"); + + // get all facets we need to check + FacetVec facets; + my_facet_tree->find_closest_entities( x, facets, narrow_band_size ); + + if (facets.empty()) + { + ThrowRequire( 0.0 != narrow_band_size || my_facet_tree->empty() ); + return far_field_value; + } + + double dist = 0.0; + if (compute_signed_distance) + { + dist = compute_point_to_facets_distance_by_average_normal(x, facets); + if (0.0 != narrow_band_size && std::abs(dist) > narrow_band_size) + { + dist = far_field_value; + } + } + else + { + double min_sqr_dist = std::numeric_limits::max(); + for ( auto&& facet : facets ) + { + const double sqr_dist = facet->point_distance_squared(x); + if (sqr_dist < min_sqr_dist) + { + min_sqr_dist = sqr_dist; + } + } + if (0.0 != narrow_band_size && min_sqr_dist > narrow_band_size*narrow_band_size) + { + dist = far_field_value; + } + else + { + dist = std::sqrt(min_sqr_dist); + } + } + + return dist; +} + +double +Faceted_Surface::compute_point_to_facets_distance_by_average_normal(const Vector3d &x, const FacetVec & facets) const +{ /* %TRACE% */ /* %TRACE% */ + + // If the closest_point weights are all larger than this value, then the closest point + // is considered to be on the face of the closest facet rather than on the edges of the facet, and + // therefore only the closest facet is considered in the distance calculation. Otherwise, all of the + // facets are considered to compute an average normal in order to compute the distance. + const double edge_tol = 1.e-6; + + std::vector facet_queries; + facet_queries.reserve(facets.size()); + for ( auto&& facet : facets ) + { + if (facet->degenerate()) continue; // Skip zero-sized facets + facet_queries.emplace_back(*facet, x); + } + + ThrowRequireMsg(!facet_queries.empty(), "All facets are degenerate in compute_point_to_facets_distance_by_average_normal."); + + unsigned nearest = 0; + for ( unsigned index=0; index(facets[nearest]) ? 3 : 2; + + bool closest_point_on_edge = false; + for (int d=0; d 0) + { + return std::sqrt(min_sqr_dist); + } + else + { + return -std::sqrt(min_sqr_dist); + } + } +} + +Vector3d +Faceted_Surface::compute_pseudo_normal(const unsigned dim, const std::vector & facet_queries, const unsigned nearest) const +{ + const double tol = 1.e-6; + Vector3d pseudo_normal = Vector3d::ZERO; + Vector3d average_normal = Vector3d::ZERO; + + const Vector3d nearest_closest_point = facet_queries[nearest].closest_point(); + const double nearest_size2 = facet_queries[nearest].facet().bounding_box().SqrSize(); + unsigned close_count = 0; + for ( auto&& query : facet_queries ) + { + const Vector3d closest_point = query.closest_point(); + const double dist2_from_nearest = (closest_point-nearest_closest_point).length_squared(); + if (dist2_from_nearest < tol*tol*nearest_size2) + { + ++close_count; + const Facet & facet = query.facet(); + + average_normal += facet.facet_normal(); + + if (3 == dim) + { + const Vector3d closest_pt_wts = query.closest_point_weights(); + const int closest_node = (closest_pt_wts[0] > closest_pt_wts[1]) ? ((closest_pt_wts[0] > closest_pt_wts[2]) ? 0 : 2) : ((closest_pt_wts[1] > closest_pt_wts[2]) ? 1 : 2); + + const int n0 = closest_node; + const int n1 = (n0<2) ? (n0+1) : 0; + const int n2 = (n1<2) ? (n1+1) : 0; + const Vector3d edge0 = facet.facet_vertex(n1) - facet.facet_vertex(n0); + const Vector3d edge1 = facet.facet_vertex(n2) - facet.facet_vertex(n0); + const double facet_angle = std::acos(Dot(edge0, edge1)/(edge0.length()*edge1.length())); + + pseudo_normal += facet.facet_normal()*facet_angle; + } + } + } + ThrowRequireMsg(close_count>0,"Issue with tolerance in compute_pseudo_normal. No facet found within tolerance of closest point."); + + return (3 == dim && close_count > 2) ? pseudo_normal : average_normal; +} + +size_t +Faceted_Surface::storage_size() const +{ + size_t store_size = sizeof(Faceted_Surface); + if (my_facet_tree) + { + store_size += my_facet_tree->storage_size(); + } + + store_size += utility::storage_size(my_local_facets); + store_size += utility::storage_size(my_nonlocal_facets); + store_size += utility::storage_size(my_all_facets); + + return store_size; +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_Faceted_Surface.hpp b/packages/krino/krino/krino_lib/Akri_Faceted_Surface.hpp new file mode 100644 index 000000000000..9ff26e6f0142 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Faceted_Surface.hpp @@ -0,0 +1,69 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Faceted_Surface_h +#define Akri_Faceted_Surface_h + +#include +#include +#include + +namespace krino { + +class Faceted_Surface : public SurfaceThatTakesAdvantageOfNarrowBandAndThereforeMightHaveWrongSign { + +public: + Faceted_Surface(const std::string & sn); + + virtual Surface_Type type() const override { return FACETED_SURFACE; } + virtual size_t storage_size() const override; + virtual void pack_into_buffer(stk::CommBuffer & b) const; // pack into buffer for off-processor communication + virtual void prepare_to_compute(const double time, const BoundingBox & point_bbox, const double truncation_length) override; + virtual double truncated_point_signed_distance(const Vector3d &x, const double narrow_band_size, const double far_field_value) const override + { + return point_distance(x, narrow_band_size, far_field_value, true); + } + + // query/modify facets + void add( std::unique_ptr facet ) { my_local_facets.emplace_back(std::move(facet)); } + void reserve(unsigned size_) { my_local_facets.reserve(size_); } + unsigned size() const { return my_local_facets.size(); } + unsigned nonlocal_size() const { return my_nonlocal_facets.size(); } + Facet * operator()( const unsigned index ) const { return my_local_facets[index].get(); } + void clear() { my_local_facets.clear(); } + void swap(Faceted_Surface & other) { my_local_facets.swap(other.my_local_facets); } + const FacetOwningVec & get_facets() const { return my_local_facets; } + void parallel_distribute_facets(const size_t batch_size, const std::vector & proc_bboxes); + +public: + void prepare_to_compute(const BoundingBox & point_bbox, const double truncation_length) + { ThrowAssert(nullptr == my_transformation); prepare_to_compute(0.0, point_bbox, truncation_length); } + double point_unsigned_distance(const Vector3d &x, const double narrow_band_size, const double far_field_value) const + { + return point_distance(x, narrow_band_size, far_field_value, false); + } + +private: + virtual void build_local_facets(const BoundingBox & proc_bbox) {} + double point_distance(const Vector3d &x, const double narrow_band_size, const double far_field_value, const bool compute_signed_distance) const; + double compute_point_to_facets_distance_by_average_normal(const Vector3d &x, const FacetVec & facets) const; + Vector3d compute_pseudo_normal(const unsigned dim, const std::vector & facet_queries, const unsigned nearest) const; + void gather_nonlocal_facets(const BoundingBox & local_bbox, const double truncation_length); + + std::string my_name; + FacetOwningVec my_local_facets; + + mutable std::unique_ptr> my_facet_tree; + mutable FacetOwningVec my_nonlocal_facets; + mutable FacetVec my_all_facets; + BoundingBox my_bounding_box; +}; + +} // namespace krino + +#endif // Akri_Faceted_Surface_h diff --git a/packages/krino/krino/krino_lib/Akri_Fast_Marching.cpp b/packages/krino/krino/krino_lib/Akri_Fast_Marching.cpp new file mode 100644 index 000000000000..2a09003eaf2c --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Fast_Marching.cpp @@ -0,0 +1,726 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace krino{ + +Fast_Marching::Fast_Marching(LevelSet & ls, const stk::mesh::Selector & selector, stk::diag::Timer & parent_timer) + : my_ls(ls), + my_selector(selector), + my_timer("Fast Marching", parent_timer), + my_tri_timer("Update Triangle", my_timer), + my_tet_timer("Update Tetrahedron", my_timer), + my_fm_node_less(ls.mesh()), + trial_nodes(my_fm_node_less) +{ + ParallelErrorMessage err(mesh().parallel()); + stk::mesh::Selector fieldSelector(my_ls.get_distance_field()); + const stk::mesh::BucketVector& buckets = selector.get_buckets(stk::topology::ELEMENT_RANK); + for (auto && bucket : buckets) + { + if (fieldSelector(*bucket) && + bucket->topology() != stk::topology::TRIANGLE_3_2D && + bucket->topology() != stk::topology::TETRAHEDRON_4) + { + err << "Topology " << bucket->topology().name() << " is not supported in Fast_Marching.\n"; + } + } + check_error(err, "Checking topology"); +} + +void +Fast_Marching::check_error(const ParallelErrorMessage& err, const std::string & context) const +{ + auto globalError = err.gather_message(); + if (globalError.first) + { + krinolog<< "Error in " << context << ":" << stk::diag::dendl; + krinolog << globalError.second << stk::diag::dendl; + } + ThrowRequireMsg(!globalError.first, "Error in " << context << "."); +} + +void Fast_Marching::redistance() +{ + stk::diag::TimeBlock timer__(my_timer); + const FieldRef& dRef = my_ls.get_distance_field(); + const FieldRef& olddRef = my_ls.get_old_distance_field(); + + // make sure field is parallel consistent to start out + { + std::vector parallel_fields(1, &dRef.field()); + stk::mesh::copy_owned_to_shared(mesh(), parallel_fields); + } + + ParallelErrorMessage err(mesh().parallel()); + + initialize(err); + + // neighbors of initial nodes are trial nodes + for ( auto && fm_node : fm_nodes ) + { + if (fm_node.status() == STATUS_INITIAL) + { + update_neighbors(fm_node, err); + } + } + + check_error(err, "Fast Marching Initialization"); + + stk::mesh::Selector globally_shared_selector = my_selector & mesh().mesh_meta_data().globally_shared_part(); + std::vector< stk::mesh::Entity> shared_nodes; + stk::mesh::get_selected_entities( globally_shared_selector, mesh().buckets( stk::topology::NODE_RANK ), shared_nodes ); + + bool done = false; + + const size_t local_num_nodes = fm_nodes.size(); + size_t max_num_nodes = 0; + stk::all_reduce_max(mesh().parallel(), &local_num_nodes, &max_num_nodes, 1); + + const unsigned max_outer_steps = max_num_nodes; // should be overkill + unsigned num_outer_steps = 0; + while (!done) + { + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "num_trial nodes = " << trial_nodes.size() << "\n"; + + if (num_outer_steps++ > max_outer_steps) + { + err << "Error: Outer loop of Fast_Marching::redistance did not converge!\n"; + break; + } + + const unsigned max_inner_steps = 10*max_outer_steps; // should be overkill + unsigned num_inner_steps = 0; + while(!trial_nodes.empty()) + { + auto begin = trial_nodes.begin(); + Fast_Marching_Node & fm_node = **begin; + trial_nodes.erase(begin); + fm_node.set_status(STATUS_ACCEPTED); + update_neighbors(fm_node, err); + if (num_inner_steps++ > max_inner_steps) + { + err << "Error: Inner loop of Fast_Marching::redistance did not converge! Number of trial nodes = " << trial_nodes.size() << "\n"; + } + if (err.have_local_error()) break; + } + + check_error(err, "Fast Marching Iteration"); + + unsigned num_locally_updated = 0; + if (mesh().parallel_size() > 1) + { + for ( auto && shared_node : shared_nodes ) + { + Fast_Marching_Node * fm_node = get_fm_node(shared_node); + if (nullptr != fm_node && fm_node->status() != STATUS_UNUSED) + { + double & node_dist = *field_data(dRef, fm_node->node()); + node_dist = fm_node->signed_dist()*fm_node->sign(); + } + } + stk::mesh::parallel_min(mesh(), {&dRef.field()}); + + for ( auto && shared_node : shared_nodes ) + { + Fast_Marching_Node * fm_node = get_fm_node(shared_node); + if (nullptr != fm_node && fm_node->status() != STATUS_UNUSED) + { + stk::mesh::Entity node = fm_node->node(); + const double min_node_unsigned_dist = *field_data(dRef, node); + const double fm_node_unsigned_dist = fm_node->signed_dist()*fm_node->sign(); + if(min_node_unsigned_dist < fm_node_unsigned_dist) + { + ThrowAssertMsg(fm_node->status() != STATUS_INITIAL || fm_node->status() != STATUS_TRIAL, "Unexpected node to have INITIAL or TRIAL status."); + fm_node->set_signed_dist(min_node_unsigned_dist*fm_node->sign()); + add_trial_node(*fm_node); + ++num_locally_updated; + } + } + } + } + + unsigned num_globally_updated = 0; + stk::all_reduce_sum(mesh().parallel(), &num_locally_updated, &num_globally_updated, 1); + + done = (num_globally_updated == 0); + } + + for ( auto && fm_node : fm_nodes ) + { + if (fm_node.status() == STATUS_TRIAL || fm_node.status() == STATUS_FAR) + { + err << "Node " << mesh().identifier(fm_node.node()) << " with status " << fm_node.status() << " with distance " << fm_node.signed_dist() << " did not get updated!\n"; + } + if (fm_node.status() != STATUS_UNUSED) + { + double & node_dist = *field_data(dRef, fm_node.node()); + node_dist = fm_node.signed_dist(); + } + } + + check_error(err, "Fast Marching Update"); + stk::mesh::field_copy(dRef, olddRef); +} + +Fast_Marching_Node * Fast_Marching::get_fm_node(stk::mesh::Entity node) +{ + if (mesh().is_valid(node) && mesh().local_id(node) < fm_nodes.size()) + { + return &fm_nodes[mesh().local_id(node)]; + } + else + { + return nullptr; + } +} + +void Fast_Marching::initialize(ParallelErrorMessage& err) +{ + stk::mesh::BulkData& stk_mesh = mesh(); + const FieldRef xRef = my_ls.get_coordinates_field(); + const FieldRef dRef = my_ls.get_distance_field(); + + const int dim = mesh().mesh_meta_data().spatial_dimension(); + + fm_nodes.clear(); + fm_nodes.resize(stk::mesh::count_selected_entities(stk_mesh.mesh_meta_data().universal_part(), stk_mesh.buckets(stk::topology::NODE_RANK))); + + std::vector field_nodes; + stk::mesh::Selector field_not_ghost = aux_meta().active_not_ghost_selector() & my_selector & stk::mesh::selectField(dRef); + stk::mesh::get_selected_entities( field_not_ghost, stk_mesh.buckets( stk::topology::NODE_RANK ), field_nodes ); + + for ( auto&& node : field_nodes ) + { + const double * curr_node_dist = field_data(dRef, node); + ThrowAssert(nullptr != curr_node_dist); + + Vector3d coords = Vector3d::ZERO; + const double * xptr = field_data(xRef, node); + ThrowAssert(nullptr != xptr); + for ( int d = 0; d < dim; d++ ) + { + coords[d] = xptr[d]; + } + + Fast_Marching_Node * fm_node = get_fm_node(node); + ThrowAssert(nullptr != fm_node); + *fm_node = Fast_Marching_Node(node,STATUS_FAR,LevelSet::sign(*curr_node_dist) * std::numeric_limits::max(),LevelSet::sign(*curr_node_dist),coords); + } + + // To start the nodes of elements that have interfaces will be redistanced. + // I have tried a few different methods for this distance calculation with varying success. + // We can: + // 1. Use only local facets to redistance the nodes of the element. This appears to not work too + // well because the closest facet to a node might be in an element that does not support the node. + // 2. Use local facets, but use the normal distance instead of the facet distance. This seems to work + // better than (1) usually. However, it seems prone to pathalogical behavior when small facets run + // parallel to an element side, producing inaccurate distance measures on this side. + // 3. Use the standard parallel redistance calculation used in "normal" redistancing. This is susceptible + // to seeing through walls if the walls are thinner than the distance of the nodes of the cut element to + // the surface. + // 4. Start the redistancing from the subelement facets, progressing through the subelements. + // Somewhat surprisingly, this does not work too well. I think this may be due to the obtuse angles + // in the subelement decomposition. The results are very similar to that produced by local redistancing (#1). + // 5. Rescale each cut element so that it has a unit gradient (or prescribed gradient) and set the nodal + // distance to the minimum (magnitude) from all of the supporting elements. This seems to work pretty + // well in the test cases and is completely local, and is not susceptible to seeing through walls. + + { + // Initialize using method #5 (element rescaling) + std::vector field_elems; + stk::mesh::get_selected_entities( field_not_ghost, stk_mesh.buckets( stk::topology::ELEMENT_RANK ), field_elems ); + for (auto&& elem : field_elems) + { + if (have_crossing(elem)) + { + const double speed = my_ls.get_time_of_arrival_speed(elem, err); + initialize_element(elem, speed); + } + } + + if (mesh().parallel_size() > 1) + { + stk::mesh::Selector globally_shared_selector = my_selector & mesh().mesh_meta_data().globally_shared_part(); + std::vector< stk::mesh::Entity> shared_nodes; + stk::mesh::get_selected_entities( globally_shared_selector, stk_mesh.buckets( stk::topology::NODE_RANK ), shared_nodes ); + + for ( auto && shared_node : shared_nodes ) + { + Fast_Marching_Node * fm_node = get_fm_node(shared_node); + if (nullptr != fm_node && fm_node->status() != STATUS_UNUSED) + { + double & node_dist = *field_data(dRef, fm_node->node()); + node_dist = fm_node->signed_dist()*fm_node->sign(); + } + } + stk::mesh::parallel_min(mesh(), {&dRef.field()}); + + for ( auto && shared_node : shared_nodes ) + { + Fast_Marching_Node * fm_node = get_fm_node(shared_node); + if (nullptr != fm_node && fm_node->status() != STATUS_UNUSED) + { + stk::mesh::Entity node = fm_node->node(); + const double min_node_unsigned_dist = *field_data(dRef, node); + const double fm_node_unsigned_dist = fm_node->signed_dist()*fm_node->sign(); + fm_node->set_signed_dist(min_node_unsigned_dist*fm_node->sign()); + if (min_node_unsigned_dist < fm_node_unsigned_dist) + { + fm_node->set_status(STATUS_INITIAL); + } + } + } + } + } +} + +bool +Fast_Marching::have_crossing(const stk::mesh::Entity & elem) const +{ + const FieldRef dRef = my_ls.get_distance_field(); + const unsigned npe = mesh().bucket(elem).topology().num_nodes(); + ThrowAssert(npe > 0); + + const stk::mesh::Entity * elem_nodes = mesh().begin(elem, stk::topology::NODE_RANK); + const double * dist0 = field_data(dRef, elem_nodes[0]); + ThrowAssert(nullptr != dist0); + for (unsigned n=1; n(dRef, elem_nodes[n]); + ThrowAssert(nullptr != dist); + if (LevelSet::sign_change(*dist0, *dist)) + { + return true; + } + } + return false; +} + +static std::function build_get_fm_node_coordinates(Fast_Marching * fm) +{ + return [fm](stk::mesh::Entity node) -> const Vector3d & + { + Fast_Marching_Node * fm_node = fm->get_fm_node(node); + ThrowAssert(fm_node); + return fm_node->coords(); + }; +} + +static double calculate_gradient_magnitude(const int npe, + const stk::mesh::Entity * elem_nodes, + const FieldRef dRef, + const std::function & get_coordinates) +{ + double mag_grad = 1.0; + + if (3 == npe) + { + const double d0 = *field_data(dRef, elem_nodes[0]); + const double d10 = *field_data(dRef, elem_nodes[1])-d0; + const double d20 = *field_data(dRef, elem_nodes[2])-d0; + + const Vector3d x0 = get_coordinates(elem_nodes[0]); + const Vector3d x10 = get_coordinates(elem_nodes[1]) - x0; + const Vector3d x20 = get_coordinates(elem_nodes[2]) - x0; + + const double detJ = (x10[0]*x20[1]-x20[0]*x10[1]); + const Vector3d grad = d10*Vector3d(x20[1],-x20[0],0.0) + d20*Vector3d(-x10[1],x10[0],0.0); + mag_grad = grad.length()/detJ; + } + else + { + ThrowAssert(4 == npe); + + const double d0 = *field_data(dRef, elem_nodes[0]); + const double d10 = *field_data(dRef, elem_nodes[1])-d0; + const double d20 = *field_data(dRef, elem_nodes[2])-d0; + const double d30 = *field_data(dRef, elem_nodes[3])-d0; + + const Vector3d x0 = get_coordinates(elem_nodes[0]); + const Vector3d x10 = get_coordinates(elem_nodes[1]) - x0; + const Vector3d x20 = get_coordinates(elem_nodes[2]) - x0; + const Vector3d x30 = get_coordinates(elem_nodes[3]) - x0; + + const Vector3d x10_x_x20 = Cross(x10,x20); + const Vector3d x20_x_x30 = Cross(x20,x30); + const Vector3d x30_x_x10 = Cross(x30,x10); + + const double detJ = Dot(x30,x10_x_x20); + const Vector3d grad = d10*x20_x_x30 + d20*x30_x_x10 + d30*x10_x_x20; + mag_grad = grad.length()/detJ; + } + + return mag_grad; +} + +void +Fast_Marching::initialize_element(const stk::mesh::Entity & elem, const double speed) +{ + // Still another way to initialize fast marching. + // Here we go to each cut element and find the current distance gradient. + // By comparing this to the desired gradient, we rescale each element. The nodal + // distance is set to the minimum (magnitude) for each of the rescaled elements that + // support the node. + const FieldRef dRef = my_ls.get_distance_field(); + const stk::mesh::Entity * elem_nodes = mesh().begin(elem, stk::topology::NODE_RANK); + const int npe = mesh().bucket(elem).topology().num_nodes(); + + auto get_coordinates = build_get_fm_node_coordinates(this); + + const double mag_grad = calculate_gradient_magnitude(npe, elem_nodes, dRef, get_coordinates); + + for (int inode=0; inodestatus() != STATUS_UNUSED); + const double elem_node_dist = *field_data(dRef, elem_nodes[inode]) / (mag_grad * speed); + const int sign = LevelSet::sign(fm_node->signed_dist()); + fm_node->set_signed_dist(sign * std::min(std::abs(fm_node->signed_dist()), std::abs(elem_node_dist))); + fm_node->set_status(STATUS_INITIAL); + fm_node->set_sign(sign); + } +} + +void +Fast_Marching::update_neighbors(Fast_Marching_Node & accepted_node, ParallelErrorMessage& err) +{ + const FieldRef dRef = my_ls.get_distance_field(); + const stk::mesh::Selector active_field_not_aura = my_selector & aux_meta().active_not_ghost_selector() & stk::mesh::selectField(dRef); + + stk::mesh::Entity node = accepted_node.node(); + + ThrowAssertMsg(STATUS_ACCEPTED == accepted_node.status() || STATUS_INITIAL == accepted_node.status(), "Expected ACCEPTED OR INITIAL status"); + + const int dim = mesh().mesh_meta_data().spatial_dimension(); + ThrowAssert(2 == dim || 3 == dim); + const int npe_dist = (2==dim) ? 3 : 4; + std::vector elem_nodes(npe_dist); + + const unsigned num_node_elems = mesh().num_elements(node); + const stk::mesh::Entity* node_elems = mesh().begin_elements(node); + for (unsigned node_elem_index=0; node_elem_indexstatus() || STATUS_ACCEPTED == fm_nbr->status() || STATUS_FAR == fm_nbr->status() || STATUS_TRIAL == fm_nbr->status()), "Unexpected node status."); + elem_nodes[i] = fm_nbr; + bool do_add_trial_node = fm_nbr->status() == STATUS_FAR; + if (fm_nbr->status() == STATUS_ACCEPTED) + { + const double accepted_node_unsigned_dist = accepted_node.signed_dist()*accepted_node.sign(); + const double nbr_unsigned_dist = fm_nbr->signed_dist()*fm_nbr->sign(); + if(nbr_unsigned_dist > accepted_node_unsigned_dist) + { + do_add_trial_node = true; + } + } + if (do_add_trial_node) + { + add_trial_node(*fm_nbr); + } + if (fm_nbr->status() == STATUS_TRIAL) + { + ++num_trial; + node_to_update = i; + } + } + + if (1 == num_trial) + { + update_node(elem_nodes, node_to_update, speed); + } + } +} + +void +Fast_Marching::add_trial_node(Fast_Marching_Node & trial_node) +{ + ThrowAssertMsg(trial_node.status() == STATUS_ACCEPTED || trial_node.status() == STATUS_FAR, "Expected ACCEPTED or FAR when adding trial node"); + trial_nodes.insert(&trial_node); + trial_node.set_status(STATUS_TRIAL); +} + +void +Fast_Marching::update_trial_node(Fast_Marching_Node & trial_node, const double dist) +{ + ThrowAssertMsg(trial_node.status() == STATUS_TRIAL, "Unexpected node status when updating trial node"); + + auto it = trial_nodes.find(&trial_node); + ThrowAssertMsg(it != trial_nodes.end(), "Can't find trial node"); + + trial_nodes.erase(it); + + trial_node.set_signed_dist(dist); + trial_nodes.insert(&trial_node); +} + +void +Fast_Marching::update_node(std::vector & elem_nodes, int node_to_update, const double speed) +{ + // update distance + double dist = std::numeric_limits::max(); + const int npe_dist = elem_nodes.size(); + + if (3 == npe_dist) + { + dist = update_triangle(elem_nodes, node_to_update, speed); + } + else if (4 == npe_dist) + { + dist = update_tetrahedron(elem_nodes, node_to_update, speed); + } + else + { + ThrowAssertMsg(false, "Unexpected number of nodes per element: " << npe_dist); + } + + Fast_Marching_Node & fm_node = *elem_nodes[node_to_update]; + if (dist*fm_node.sign() < fm_node.signed_dist()*fm_node.sign()) + { + update_trial_node(fm_node, dist); + } +} + +double +Fast_Marching::update_triangle(std::vector & elem_nodes, int node_to_update, const double speed, int side_to_update) +{ + stk::diag::TimeBlock timer__(my_tri_timer); + const int dim = mesh().mesh_meta_data().spatial_dimension(); + ThrowAssert(2 == dim || 3 == dim); + + int lnn[3]; + if (2 == dim) + { + ThrowAssert(-1 == side_to_update); + lnn[0] = (node_to_update + 1) % 3; + lnn[1] = (node_to_update + 2) % 3; + lnn[2] = node_to_update; + } + else // (3 == dim) + { + const stk::topology tet4_topology = stk::topology::TETRAHEDRON_4; + const unsigned * side_node_ordinals = get_side_node_ordinals(tet4_topology, side_to_update); + lnn[0] = side_node_ordinals[(node_to_update + 1) % 3]; + lnn[1] = side_node_ordinals[(node_to_update + 2) % 3]; + lnn[2] = side_node_ordinals[node_to_update]; + } + + const double dist_0 = elem_nodes[lnn[0]]->signed_dist(); + const double dist_1 = elem_nodes[lnn[1]]->signed_dist(); + double dist_2 = elem_nodes[lnn[2]]->signed_dist(); + + const int sign = elem_nodes[lnn[2]]->sign(); + if (sign*(dist_2-dist_0) < 0 || sign*(dist_2-dist_1) < 0) + { + return dist_2; + } + + const Vector3d & coords_0 = elem_nodes[lnn[0]]->coords(); + const Vector3d & coords_1 = elem_nodes[lnn[1]]->coords(); + const Vector3d & coords_2 = elem_nodes[lnn[2]]->coords(); + + const double sqr_speed = speed*speed; + const double d10 = dist_1 - dist_0; + const Vector3d x10 = coords_1 - coords_0; + const Vector3d x20 = coords_2 - coords_0; + const double h10 = x10.length(); + const double h20 = x20.length(); + + double detJ = 0; + if (2 == dim) + { + detJ = (x10[0]*x20[1]-x20[0]*x10[1]); + } + else // (3 == dim) + { + detJ = Cross(x10,x20).length(); + } + ThrowAssert(detJ > 0.0); + + const double a = h10*h10; + const double b = -2.0 * sign*d10 * Dot(x10,x20); + const double c = d10*d10 * h20*h20 - detJ*detJ/sqr_speed; + + bool elem_is_defining = false; + + const double det = b*b-4.0*a*c; + if (det > 0.0) + { + // solve quadratic equation, roots are q/a and c/q + const int sign_b = ( b < 0.0 ) ? -1 : 1; + const double q = -0.5*(b + sign_b*std::sqrt(det)); + + const double d20 = sign*std::max(c/q,q/a); + + const bool causal = (sign*d20 > 0.0 && sign*(d20-d10) > 0.0); + + if (causal) + { + const double loc = (Dot(x10,x20) - sqr_speed*d10*d20) / (h10*h10 - sqr_speed*d10*d10); + elem_is_defining = (loc > 0.0 && loc < 1.0); + + if (elem_is_defining) + { + dist_2 = sign * std::min(sign*dist_2,sign*(dist_0 + d20)); + } + } + } + + if (!elem_is_defining) + { + const double h21 = (coords_2 - coords_1).length(); + dist_2 = sign * std::min(sign*dist_2,std::min(sign*dist_0+h20/speed,sign*dist_1+h21/speed)); + // Enforce causality - This is to catch the corner case (literally) where the characteristic is marching along the edges of the element + dist_2 = sign * std::max(sign*dist_2,std::max(sign*dist_0,sign*dist_1)); + } + + ThrowAssert(sign*(dist_2-dist_0)>=0 && sign*(dist_2-dist_1)>=0); + + return dist_2; +} + +double +Fast_Marching::update_tetrahedron(std::vector & elem_nodes, int node_to_update, const double speed) +{ + stk::diag::TimeBlock timer__(my_tet_timer); + static const unsigned node_permute_0[] = { 1,3,2,0 }; + static const unsigned node_permute_1[] = { 0,2,3,1 }; + static const unsigned node_permute_2[] = { 0,3,1,2 }; + static const unsigned node_permute_3[] = { 0,1,2,3 }; + static const unsigned * node_permute_table[] = { node_permute_0, node_permute_1, node_permute_2, node_permute_3 }; + + int lnn[4]; + lnn[0] = node_permute_table[node_to_update][0]; + lnn[1] = node_permute_table[node_to_update][1]; + lnn[2] = node_permute_table[node_to_update][2]; + lnn[3] = node_permute_table[node_to_update][3]; + + const double dist_0 = elem_nodes[lnn[0]]->signed_dist(); + const double dist_1 = elem_nodes[lnn[1]]->signed_dist(); + const double dist_2 = elem_nodes[lnn[2]]->signed_dist(); + double dist_3 = elem_nodes[lnn[3]]->signed_dist(); + + const int sign = elem_nodes[lnn[3]]->sign(); + if (sign*(dist_3-dist_0) < 0 || sign*(dist_3-dist_1) < 0 || sign*(dist_3-dist_2) < 0) + { + return dist_3; + } + + const Vector3d & coords_0 = elem_nodes[lnn[0]]->coords(); + const Vector3d & coords_1 = elem_nodes[lnn[1]]->coords(); + const Vector3d & coords_2 = elem_nodes[lnn[2]]->coords(); + const Vector3d & coords_3 = elem_nodes[lnn[3]]->coords(); + + const double sqr_speed = speed*speed; + const double d10 = dist_1-dist_0; + const double d20 = dist_2-dist_0; + + const Vector3d x10 = coords_1 - coords_0; + const Vector3d x20 = coords_2 - coords_0; + const Vector3d x30 = coords_3 - coords_0; + + const Vector3d x10_x_x20 = Cross(x10,x20); + const Vector3d x20_x_x30 = Cross(x20,x30); + const Vector3d x30_x_x10 = Cross(x30,x10); + + const double detJ = Dot(x30,x10_x_x20); + ThrowAssert(detJ > 0); + + const Vector3d contrib12 = sign * (d10*x20_x_x30 + d20*x30_x_x10); + + const double a = x10_x_x20.length_squared(); + const double b = 2.0 * Dot(x10_x_x20,contrib12); + const double c = contrib12.length_squared() - detJ*detJ/sqr_speed; + + bool elem_is_defining = false; + + const double det = b*b-4.0*a*c; + if (det > 0.0) + { + // solve quadratic equation, roots are q/a and c/q + const int sign_b = ( b < 0.0 ) ? -1 : 1; + const double q = -0.5*(b + sign_b*std::sqrt(det)); + + const double d30 = sign*std::max(c/q,q/a); + + const bool causal = (sign*d30 > 0.0 && sign*(d30-d10) > 0.0 && sign*(d30-d20) > 0.0); + + if (causal) + { + // Solve 2x2 system for parametric coords of intersection of gradient and 0-1-2 + // A1 r + B1 s == C1 + // A2 r + B2 s == C2 + const double A1 = x10.length_squared() - sqr_speed*d10*d10; + const double B1 = Dot(x10,x20) - sqr_speed*d10*d20; + const double C1 = Dot(x10,x30) - sqr_speed*d10*d30; + const double A2 = B1; + const double B2 = x20.length_squared() - sqr_speed*d20*d20; + const double C2 = Dot(x20,x30) - sqr_speed*d20*d30; + const double denom = A2*B1 - A1*B2; + const double loc_r = (C2*B1 - C1*B2)/denom; + const double loc_s = (A2*C1 - A1*C2)/denom; + const double loc_t = 1.0 - loc_r - loc_s; + elem_is_defining = (loc_r > 0.0 && loc_s > 0.0 && loc_t > 0.0); + + if (elem_is_defining) + { + dist_3 = sign * std::min(sign*dist_3,sign*(dist_0 + d30)); + } + } + } + + if (!elem_is_defining) + { + const stk::topology tet4_topology = stk::topology::TETRAHEDRON_4; + for (int iside=0; iside<4; ++iside) + { + const unsigned * side_node_lnn = get_side_node_ordinals(tet4_topology, iside); + for (int inode=0; inode<3; ++inode) + { + if (node_to_update == (int)side_node_lnn[inode]) + { + dist_3 = sign * std::min(sign*dist_3, sign*update_triangle(elem_nodes,inode,speed,iside)); + } + } + } + // Enforce causality - This is to catch the corner case (literally) where the characteristic is marching along the edges of the element + // This is not completely covered by the 2d check because 1 face might still predict a value that is less than the neighbors. + dist_3 = sign * std::max(sign*dist_3,std::max(sign*dist_2,std::max(sign*dist_1,sign*dist_0))); + } + + ThrowAssert(sign*(dist_3-dist_0)>=0 && sign*(dist_3-dist_1)>=0 && sign*(dist_3-dist_2)>=0); + + return dist_3; +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_Fast_Marching.hpp b/packages/krino/krino/krino_lib/Akri_Fast_Marching.hpp new file mode 100644 index 000000000000..233bec1d38a7 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Fast_Marching.hpp @@ -0,0 +1,104 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Fast_Marching_h +#define Akri_Fast_Marching_h + +#include +#include +#include + +namespace krino { + +class SubElement; +class Mesh_Element; +class AuxMetaData; +class ParallelErrorMessage; + +enum Enum_Fast_Marching_Node_Status{STATUS_UNUSED=0, STATUS_INITIAL, STATUS_ACCEPTED, STATUS_TRIAL, STATUS_FAR}; + +class Fast_Marching_Node +{ +public: + Fast_Marching_Node() + : my_node(), my_status(STATUS_UNUSED), my_signed_dist(std::numeric_limits::max()), my_on_neg_side(false) {} + Fast_Marching_Node(stk::mesh::Entity in_node, Enum_Fast_Marching_Node_Status in_status, double in_dist, int in_sign, Vector3d in_coords) + : my_node(in_node), my_status(in_status), my_signed_dist(in_dist), my_on_neg_side(in_sign<0), my_coords(in_coords) {} + + int sign() const { return (my_on_neg_side ? (-1) : 1); } + void set_sign(const int in_sign) { my_on_neg_side = (in_sign<0); } + stk::mesh::Entity node() const { return my_node; } + Enum_Fast_Marching_Node_Status status() const { return my_status; } + const Vector3d & coords() const { return my_coords; } + double signed_dist() const { return my_signed_dist; } + void set_signed_dist(double dist) { my_signed_dist = dist; } + void set_status(Enum_Fast_Marching_Node_Status status) { my_status = status; } + +private: + stk::mesh::Entity my_node; + Enum_Fast_Marching_Node_Status my_status; + double my_signed_dist; + bool my_on_neg_side; + Vector3d my_coords; +}; + +class Fast_Marching_Node_Less +{ +public: + Fast_Marching_Node_Less(const stk::mesh::BulkData & mesh) : mMesh(mesh) {} + bool operator() (const Fast_Marching_Node *a, const Fast_Marching_Node *b) const + { + const double unsignedDistA = a->signed_dist()*a->sign(); + const double unsignedDistB = b->signed_dist()*b->sign(); + if (unsignedDistA < unsignedDistB) return true; + if (unsignedDistB < unsignedDistA) return false; + return mMesh.identifier(a->node()) < mMesh.identifier(b->node()); + } +private: + const stk::mesh::BulkData & mMesh; +}; + +class Fast_Marching { +public: + Fast_Marching(LevelSet & ls, const stk::mesh::Selector & selector, stk::diag::Timer & parent_timer); + + void redistance(); + void initialize(ParallelErrorMessage& err); + void update_neighbors(Fast_Marching_Node & accepted_node, ParallelErrorMessage & err); + void update_node(std::vector & elem_nodes, int node_to_update, const double speed); + + bool have_crossing(const stk::mesh::Entity & elem) const; + void initialize_subelement(const SubElement & subelem, const int side, const double speed); + void initialize_element(const stk::mesh::Entity & elem, const double speed); + double update_triangle(std::vector & elem_nodes, int node_to_update, const double speed, int side_to_update=-1); + double update_tetrahedron(std::vector & elem_nodes, int node_to_update, const double speed); + + void add_trial_node(Fast_Marching_Node & add_trial_node); + void update_trial_node(Fast_Marching_Node & add_trial_node, const double dist); + Fast_Marching_Node * get_fm_node(stk::mesh::Entity node); + + const AuxMetaData& aux_meta() const { return my_ls.aux_meta(); } + AuxMetaData& aux_meta() { return my_ls.aux_meta(); } + const stk::mesh::BulkData& mesh() const { return my_ls.mesh(); } + stk::mesh::BulkData& mesh() { return my_ls.mesh(); } +private: + LevelSet & my_ls; + stk::mesh::Selector my_selector; + stk::diag::Timer my_timer; + stk::diag::Timer my_tri_timer; + stk::diag::Timer my_tet_timer; + std::vector fm_nodes; + Fast_Marching_Node_Less my_fm_node_less; + std::set trial_nodes; //set sorted by distance, then by id to break "ties" + + void check_error(const ParallelErrorMessage& err, const std::string & context) const; +}; + +} // namespace krino + +#endif // Akri_Fast_Marching_h diff --git a/packages/krino/krino/krino_lib/Akri_FieldRef.cpp b/packages/krino/krino/krino_lib/Akri_FieldRef.cpp new file mode 100644 index 000000000000..c10c1dd8a22f --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_FieldRef.cpp @@ -0,0 +1,54 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include // for operator<<, stringstream, etc + +namespace krino{ + +bool FieldRef::operator < ( const FieldRef & rhs ) const +{ + if ( my_field == rhs.my_field || rhs.my_field == NULL ) + { + return false; // identical + } + else if (my_field == NULL) + { + return true; + } + else if ( my_field->field_state(stk::mesh::StateNone) == rhs.field().field_state(stk::mesh::StateNone) ) + { + return my_field->state() < rhs.state(); + } + return name() < rhs.name(); +} + +std::string +state_string(stk::mesh::FieldState state) +{ + static const char * const local_state_name[] = { + "STATE_NONE/NEW/NP1", + "STATE_OLD/N", + "STATE_NM1", + "STATE_NM2", + "STATE_NM3", + "STATE_NM4" + }; + + if ((unsigned) state < (unsigned)sizeof(local_state_name)/(unsigned)sizeof(local_state_name[0])) + return local_state_name[state]; + else { + std::stringstream strout; + strout << "(" << (unsigned) state << ")"; + return strout.str().c_str(); + } +} + +//---------------------------------------------------------------------- + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_FieldRef.hpp b/packages/krino/krino/krino_lib/Akri_FieldRef.hpp new file mode 100644 index 000000000000..b64fcdf37f36 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_FieldRef.hpp @@ -0,0 +1,168 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_FieldRef_h +#define Akri_FieldRef_h + +#include // for NULL +#include // for string +#include "stk_mesh/base/DataTraits.hpp" // for DataTraits +#include "stk_mesh/base/Entity.hpp" // for Entity +#include "stk_mesh/base/Field.hpp" // for Field +#include "stk_mesh/base/FieldBase.hpp" // for FieldBase, etc +#include "stk_mesh/base/FieldState.hpp" // for FieldState, etc +#include "stk_mesh/base/Types.hpp" // for EntityRank +#include "stk_util/util/ReportHandler.hpp" // for ThrowAssertMsg, etc +namespace stk { namespace mesh { class Bucket; } } + +namespace krino{ + +std::string +state_string(stk::mesh::FieldState state); + +class FieldRef +{ +public: + // Only default constructed FieldRef (or copies of a default constructed FieldRef) should ever have my_field==NULL + FieldRef() : my_field(NULL) {} + + FieldRef(const FieldRef &) = default; + FieldRef & operator= (const FieldRef &) = default; + + FieldRef(const stk::mesh::FieldBase * in_field) + : my_field(in_field) + { + ThrowAssertMsg(NULL != in_field, "Cannot set FieldRef with NULL field."); + } + FieldRef(const stk::mesh::FieldBase * in_field, const stk::mesh::FieldState in_state) + : my_field(in_field) + { + ThrowAssertMsg(NULL != in_field, "Cannot set FieldRef with NULL field."); + my_field = in_field->field_state(in_state); + ThrowAssertMsg(NULL != my_field, "Invalid state."); + } + FieldRef(const stk::mesh::FieldBase & in_fieldref) + { + my_field = &in_fieldref; + } + FieldRef(const FieldRef in_fieldref, const stk::mesh::FieldState in_state) + { + ThrowAssertMsg(NULL != in_fieldref.my_field, "Cannot set FieldRef with NULL field."); + my_field = in_fieldref.my_field->field_state(in_state); + } + + FieldRef field_state(stk::mesh::FieldState in_state) const + { + return FieldRef(my_field, in_state); + } + + // assignment operators + FieldRef& operator=( const stk::mesh::FieldBase * in_field ) + { + ThrowAssertMsg(NULL != in_field, "Cannot set FieldRef with NULL field."); + my_field = in_field; + return *this; + } + FieldRef& operator=( const stk::mesh::FieldBase & in_field ) + { + my_field = &in_field; + return *this; + } + + bool valid() const { return NULL != my_field; } + bool valid_restriction_size() const { return NULL != my_field && 0 != my_field->restrictions().size(); } + void assert_valid() const { ThrowAssertMsg(NULL != my_field, "Attempt to access field of uninitialized FieldRef."); } + + // stk access + const stk::mesh::FieldBase & field() const { assert_valid(); return *my_field; } + stk::mesh::FieldBase & field() { assert_valid(); return const_cast(*my_field); } + operator const stk::mesh::FieldBase &() const { assert_valid(); return *my_field; } + + // pass through methods + unsigned number_of_states() const { assert_valid(); return my_field->number_of_states(); } + stk::mesh::FieldState state() const { assert_valid(); return my_field->state(); } + stk::mesh::EntityRank entity_rank() const { assert_valid(); return my_field->entity_rank(); } + + template bool type_is() const + { return my_field->type_is(); } + + // name_with_state() includes any suffix indicating the state, ie "_STKFS_OLD" + const std::string & name_with_state() const { assert_valid(); return my_field->name(); } + // name() is the basic field name without any suffix for state, regardless of the state of the field + std::string name() const { assert_valid(); return my_field->field_state(stk::mesh::StateNone)->name(); } + + unsigned length(const stk::mesh::Bucket& b) const + { + assert_valid(); + const unsigned type_stride = my_field->data_traits().stride_of; + const unsigned fieldSize = stk::mesh::field_bytes_per_entity(*my_field, b) / type_stride; + return fieldSize; + } + + unsigned length(stk::mesh::Entity e) const + { + assert_valid(); + const unsigned type_stride = my_field->data_traits().stride_of; + const unsigned fieldSize = stk::mesh::field_bytes_per_entity(*my_field, e) / type_stride; + return fieldSize; + } + + // Only safe if field has same length on all entities + unsigned length() const + { + ThrowAssert(valid()); + return my_field->max_size(my_field->entity_rank()); + } + + // testing + bool operator ==(const FieldRef & rhs) const { return my_field == rhs.my_field; } + bool operator !=(const FieldRef & rhs) const { return my_field != rhs.my_field; } + bool operator < (const FieldRef & rhs) const; + +private: + const stk::mesh::FieldBase * my_field; +}; + +typedef std::set FieldSet; + +inline std::ostream & operator << (std::ostream & os, const FieldRef & field_ref) +{ + const bool valid = field_ref.valid(); + os << "FieldRef valid = " << std::boolalpha << valid; + if(valid) + { + os << " field = " << field_ref.name_with_state(); + } + return os; +} + +template< class pod_type > +pod_type * field_data( const stk::mesh::FieldBase & field, stk::mesh::Entity entity ) +{ + return stk::mesh::field_data(static_cast< const stk::mesh::Field& >(field), entity); +} + +template< class pod_type > +pod_type * field_data( const stk::mesh::FieldBase & field, const stk::mesh::Bucket & bucket ) +{ + return stk::mesh::field_data(static_cast< const stk::mesh::Field& >(field), bucket); +} + +inline bool has_field_data( const FieldRef fieldref, stk::mesh::Entity entity ) +{ + return stk::mesh::field_is_allocated_for_bucket(fieldref.field(), fieldref.field().get_mesh().bucket(entity)); +} + +inline bool has_field_data( const FieldRef fieldref, const stk::mesh::Bucket & bucket ) +{ + return stk::mesh::field_is_allocated_for_bucket(fieldref.field(), bucket); +} + +} // namespace krino + +#endif // Akri_FieldRef_h diff --git a/packages/krino/krino/krino_lib/Akri_IC_Alg.cpp b/packages/krino/krino/krino_lib/Akri_IC_Alg.cpp new file mode 100644 index 000000000000..0050bcc786fc --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_IC_Alg.cpp @@ -0,0 +1,225 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace krino{ + +namespace { +double relative_crossing_position(const double ls0, const double ls1) +{ + return LevelSet::sign_change(ls0, ls1) ? ls0 / ( ls0 - ls1 ) : -10.; +} +} + +//---------------------------------------------------------------- +void IC_Alg::execute(const double time, const bool requires_additional_initialization) +{ /* %TRACE[ON]% */ Trace trace__("krino::IC_Analytic_Alg::execute()"); /* %TRACE% */ + + if (surface_list.size() == 0 && my_calculators.empty()) + { + return; + } + + const stk::mesh::BulkData& mesh = levelSet.mesh(); + const stk::mesh::MetaData& meta = mesh.mesh_meta_data(); + const FieldRef xField = levelSet.get_coordinates_field(); + const FieldRef dField = levelSet.get_distance_field(); + const int spatial_dim = meta.spatial_dimension(); + + BoundingBox node_bbox; + levelSet.compute_nodal_bbox( mesh.mesh_meta_data().universal_part(), node_bbox ); + surface_list.prepare_to_compute(time, node_bbox, levelSet.narrow_band_size()); + + stk::mesh::BucketVector const& buckets = mesh.get_buckets( stk::topology::NODE_RANK, stk::mesh::selectField(dField) ); + + for ( auto && bucket_ptr : buckets ) + { + const stk::mesh::Bucket & b = *bucket_ptr; + const int length = b.size(); + double *dist = field_data(dField, b); + double * coord = field_data(xField, b); + + for (int n = 0; n < length; ++n) + { + ThrowAssert(&(dist[n]) != NULL); + + const Vector3d x(&coord[spatial_dim*n], spatial_dim); + + dist[n] = surface_list.point_signed_distance_with_narrow_band(x, levelSet.narrow_band_size()); + } + } + + stk::mesh::communicate_field_data(mesh, {&dField.field()}); + + if (levelSet.narrow_band_size() > 0. && surface_list.truncated_distance_may_have_wrong_sign()) + { + DistanceSweeper::fix_sign_by_sweeping(mesh, dField, surface_list.get_signed_narrow_band_size(levelSet.narrow_band_size())); + } + + if(CDFEM_Support::get(meta).get_nonconformal_adapt_target_count() > 0) + { + compute_IC_error_indicator(); + } + + for (auto && calc : my_calculators) + { + calc->compute_signed_distance(levelSet); + } + + if (!levelSet.get_keep_IC_surfaces() && !requires_additional_initialization) + { + clear(); + } +} + +void IC_Alg::compute_IC_error_indicator() +{ + const stk::mesh::BulkData& mesh = levelSet.mesh(); + const stk::mesh::MetaData& meta = mesh.mesh_meta_data(); + const FieldRef xField = levelSet.get_coordinates_field(); + const FieldRef dField = levelSet.get_distance_field(); + stk::mesh::Selector fieldSelector(dField); + const int spatial_dim = meta.spatial_dimension(); + const auto & cdfem_support = CDFEM_Support::get(meta); + + const auto & aux_meta = AuxMetaData::get(meta); + const auto & indicator_field_name = cdfem_support.get_nonconformal_adapt_indicator_name(); + auto indicator_field = + aux_meta.get_field(stk::topology::ELEMENT_RANK, indicator_field_name); + const auto & elem_buckets = + mesh.get_buckets(stk::topology::ELEMENT_RANK, meta.locally_owned_part() & fieldSelector); + + std::vector nodal_signed_distances; + std::vector nodal_coordinates; + std::vector edge_midpoints; + std::vector midpoint_signed_distances, midpoint_interp_signed_distances; + int edge_nodes[] = {0, 0}; + + for(auto && b_ptr : elem_buckets) + { + const auto & b = *b_ptr; + + const int size = b.size(); + double * indicator_data = field_data(indicator_field, b); + + const stk::topology & topo = b.topology(); + + const auto & master_elem = MasterElementDeterminer::getMasterElement(*b_ptr, xField); + const int num_nodes = master_elem.num_nodes(); + + nodal_signed_distances.resize(num_nodes); + nodal_coordinates.resize(num_nodes); + + for(int el=0; el < size; ++el) + { + const auto * nodes = mesh.begin_nodes(b[el]); + for(int n=0; n < num_nodes; ++n) + { + auto node = nodes[n]; + nodal_signed_distances[n] = *field_data(dField, node); + const double * coords_data = field_data(xField, node); + nodal_coordinates[n] = Vector3d(coords_data, spatial_dim); + } + + // Iterate edges, find location of crossing on the edge and compare to location of crossing + // if we add a node at the midpoint with the exact signed distance there. + const int num_edges = topo.num_edges(); + edge_midpoints.resize(num_edges); + midpoint_signed_distances.resize(num_edges); + midpoint_interp_signed_distances.resize(num_edges); + double err = 0.; + for (int e=0; e < num_edges; ++e) + { + ThrowAssert(topo.edge_topology(e).num_nodes() == 2); + topo.edge_node_ordinals(e, edge_nodes); + + const Vector3d & x0 = nodal_coordinates[edge_nodes[0]]; + const Vector3d & x1 = nodal_coordinates[edge_nodes[1]]; + edge_midpoints[e] = 0.5*(x0+x1); + const double edge_length_sqr = (x1-x0).length_squared(); + + double midpoint_signed_distance = + surface_list.point_signed_distance_with_narrow_band(edge_midpoints[e], levelSet.narrow_band_size()); + midpoint_signed_distances[e] = midpoint_signed_distance; + + const double ls0 = nodal_signed_distances[edge_nodes[0]]; + const double ls1 = nodal_signed_distances[edge_nodes[1]]; + midpoint_interp_signed_distances[e] = 0.5 * (ls0 + ls1); + const double orig_crossing_pos = relative_crossing_position(ls0, ls1); + const double left_half_crossing_pos = + 0.5 * relative_crossing_position(ls0, midpoint_signed_distance); + const double right_half_crossing_pos = + 0.5 * (1. + relative_crossing_position(midpoint_signed_distance, ls1)); + + if(orig_crossing_pos >= 0.) + { + if(left_half_crossing_pos >= 0.) + { + const double delta = orig_crossing_pos - left_half_crossing_pos; + err += edge_length_sqr * delta * delta; + } + if(right_half_crossing_pos >= 0.) + { + const double delta = orig_crossing_pos - right_half_crossing_pos; + err += edge_length_sqr * delta * delta; + } + } + else + { + if(left_half_crossing_pos >= 0. || right_half_crossing_pos >= 0.) + { + err += edge_length_sqr; + } + } + } + + for(int ei=0; ei < num_edges; ++ei) + { + for(int ej=ei+1; ej < num_edges; ++ej) + { + const double edge_length_sqr = (edge_midpoints[ei] - edge_midpoints[ej]).length_squared(); + const double interp_crossing_pos = + relative_crossing_position(midpoint_interp_signed_distances[ei], + midpoint_interp_signed_distances[ej]); + const double crossing_pos = + relative_crossing_position(midpoint_signed_distances[ei], + midpoint_signed_distances[ej]); + + const bool has_interp_crossing = interp_crossing_pos >= 0.; + const bool has_crossing = crossing_pos >= 0.; + if(has_interp_crossing != has_crossing) err += edge_length_sqr; + if(has_crossing && has_interp_crossing) + { + const double delta = crossing_pos - interp_crossing_pos; + err += edge_length_sqr * delta * delta; + } + } + } + indicator_data[el] += err; + } + } +} + +//---------------------------------------------------------------- +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_IC_Alg.hpp b/packages/krino/krino/krino_lib/Akri_IC_Alg.hpp new file mode 100644 index 000000000000..b74bb2ebf174 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_IC_Alg.hpp @@ -0,0 +1,55 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_IC_Alg_h +#define Akri_IC_Alg_h + +#include +#include +#include + +namespace krino { class LevelSet; } + +namespace krino { + +//---------------------------------------------------------------- +class IC_Alg { + +public: + IC_Alg( LevelSet & ls ) : levelSet( ls ), surface_list("IC Surface List") {} + ~IC_Alg() {} + + void set_composition_method(Composite_Surface::CompositionMethod composition_method) { + surface_list.set_composition_method(composition_method); + } + + // query number of surfaces + unsigned numberSurfaces() { return surface_list.size();} + + // push a surface onto our container + void addSurface(Surface * surf) { surface_list.add(surf); } + + // remove all surfaces and calculators + void clear() { surface_list.clear(); my_calculators.clear(); } + + void addCalculator(std::unique_ptr calc) { my_calculators.emplace_back(std::move(calc)); } + + BoundingBox get_surface_bounding_box() { return surface_list.get_bounding_box(); } + + void execute(const double time, const bool requires_additional_initialization); +private: + void compute_IC_error_indicator(); +private: + LevelSet & levelSet; + Composite_Surface surface_list; + std::vector> my_calculators; +}; +//---------------------------------------------------------------- +} // namespace krino + +#endif // Akri_IC_Alg_h diff --git a/packages/krino/krino/krino_lib/Akri_IC_Calculator.cpp b/packages/krino/krino/krino_lib/Akri_IC_Calculator.cpp new file mode 100644 index 000000000000..6ff101371964 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_IC_Calculator.cpp @@ -0,0 +1,100 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +namespace krino{ + +void IC_Binder::compute_signed_distance(const LevelSet &ls) const +{ + const stk::mesh::MetaData & meta = ls.meta(); + const stk::mesh::BulkData & mesh = ls.mesh(); + const FieldRef distRef = ls.get_distance_field(); + + const stk::mesh::Selector selector = stk::mesh::selectField(distRef); + + const LevelSetManager & region_ls = LevelSetManager::get(meta); + const unsigned num_ls = region_ls.numberLevelSets(); + + std::vector otherDistRefs; + for (unsigned i=0; i otherDist(num_other_dist, nullptr); + for ( auto && bucket : mesh.get_buckets( stk::topology::NODE_RANK, selector) ) + { + const stk::mesh::Bucket & b = *bucket; + const size_t length = b.size(); + + double * dist = field_data(distRef, b); + for (unsigned i=0; i(otherDistRefs[i], b); + } + + for ( size_t n = 0; n < length; ++n ) + { + dist[n] = std::numeric_limits::max(); + for (unsigned i=0; i<(num_other_dist-my_binder_type); ++i) + { + for (unsigned j=i+1; j<(num_other_dist-my_binder_type); ++j) + { + //ensure binder separates particles by a distance of my_interface_size + if (my_binder_type==0) + { + dist[n] = std::min(dist[n], std::max(otherDist[i][n]-my_interface_size, otherDist[j][n]-my_interface_size)); + } + + //create smooth connection between particles if bridge size specified + //take the product of two offset particle level sets and move the zero crossing by bridge_size + if (my_binder_type==1) + { + if (my_smooth_bridge_size > 0.0) + { + double bridge = (otherDist[i][n]+my_smooth_bridge_offset)*(otherDist[j][n]+my_smooth_bridge_offset)-my_smooth_bridge_size; + if (my_root_smooth_bridge) + { + //root the level set product to bring it back to distance function magnitudes + const int sign = (bridge >= 0.0) ? 1 : -1; + bridge = sign*std::sqrt(std::abs(bridge)); + } + dist[n] = std::min(dist[n], bridge); + } + } + } + } + for (unsigned i=0; i + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace krino{ + +Block_Surface_Connectivity::Block_Surface_Connectivity(const stk::mesh::MetaData & meta) +{ + /* %TRACE[ON]% */ + Trace trace__("Block_Surface_Connectivity::Block_Surface_Connectivity(const stk::mesh::MetaData & meta)"); /* %TRACE% */ + + std::vector surfacesInMap = meta.get_surfaces_in_surface_to_block_map(); + for(auto && surfaceInMap : surfacesInMap) + { + std::set touching_block_ordinals; + for (auto && touching_part : meta.get_blocks_touching_surface(surfaceInMap)) + { + touching_block_ordinals.insert(touching_part->mesh_meta_data_ordinal()); + } + add_surface(surfaceInMap->mesh_meta_data_ordinal(), touching_block_ordinals); + } +} + +Block_Surface_Connectivity::Block_Surface_Connectivity(const stk::mesh::MetaData & meta, const Ioss::Region & io_region) +{ + /* %TRACE[ON]% */ + Trace trace__("Block_Surface_Connectivity::Block_Surface_Connectivity(const Ioss::Region & reg)"); /* %TRACE% */ + + std::vector side_block_names; + std::vector side_block_ordinals; + + for(auto sideset : io_region.get_sidesets()) + { + side_block_names.clear(); + sideset->block_membership(side_block_names); + side_block_ordinals.clear(); + for (auto && block_name : side_block_names) + { + const stk::mesh::Part * side_block_part = meta.get_part(block_name); + ThrowRequire(nullptr != side_block_part); + side_block_ordinals.push_back(side_block_part->mesh_meta_data_ordinal()); + } + const stk::mesh::Part * side_part = meta.get_part(sideset->name()); + ThrowRequire(nullptr != side_part); + add_surface(side_part->mesh_meta_data_ordinal(), std::set(side_block_ordinals.begin(), side_block_ordinals.end())); + + if (!sideset->get_side_blocks().empty()) + { + for (auto&& side_subset : sideset->get_side_blocks()) + { + // Fmwk only creates subset if more than 1 sideblock, but stk always creates them, so just check. + const stk::mesh::Part * side_subset_part = meta.get_part(side_subset->name()); + if (nullptr == side_subset_part) continue; + side_block_names.clear(); + side_subset->block_membership(side_block_names); + side_block_ordinals.clear(); + for (auto && block_name : side_block_names) + { + const stk::mesh::Part * side_block_part = meta.get_part(block_name); + ThrowRequire(nullptr != side_block_part); + side_block_ordinals.push_back(side_block_part->mesh_meta_data_ordinal()); + } + add_surface(side_subset_part->mesh_meta_data_ordinal(), std::set(side_block_ordinals.begin(), side_block_ordinals.end())); + } + } + } +} + +std::vector +get_input_mesh_block_names( const Ioss::Region & io_region ) +{ + std::vector mesh_elem_blocks; + for(auto && elem_block : io_region.get_element_blocks()) + { + if (stk::io::include_entity(elem_block)) + { + mesh_elem_blocks.push_back(elem_block->name()); + } + } + return mesh_elem_blocks; +} + // namespace krino +} diff --git a/packages/krino/krino/krino_lib/Akri_IO_Helpers.hpp b/packages/krino/krino/krino_lib/Akri_IO_Helpers.hpp new file mode 100644 index 000000000000..2ec5005ffd08 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_IO_Helpers.hpp @@ -0,0 +1,66 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_IO_Helpers_h +#define Akri_IO_Helpers_h +// +#include +#include + +#include + +#include + +namespace stk { namespace mesh { class BulkData; } } +namespace stk { namespace mesh { class MetaData; } } +namespace stk { namespace diag { class Timer; } } +namespace Ioss { class Region; } + +namespace krino { + +class AuxMetaData; + +class Block_Surface_Connectivity +{ +public: + Block_Surface_Connectivity() {} + Block_Surface_Connectivity(const stk::mesh::MetaData & meta); + Block_Surface_Connectivity(const stk::mesh::MetaData & meta, const Ioss::Region & io_region); + void get_surfaces_touching_block(const stk::mesh::PartOrdinal & block_ordinal, + std::set & surface_ordinals) const + { + auto it = block_to_surface_map.find(block_ordinal); + if(it == block_to_surface_map.end()) return; + surface_ordinals.insert(it->second.begin(), it->second.end()); + } + void get_blocks_touching_surface(const stk::mesh::PartOrdinal & surface_ordinal, + std::set & block_ordinals) const + { + block_ordinals.clear(); + auto it = surface_to_block_map.find(surface_ordinal); + if(it == surface_to_block_map.end()) return; + block_ordinals.insert(it->second.begin(), it->second.end()); + } + + void add_surface(const stk::mesh::PartOrdinal & surf_ordinal, const std::set touching_blocks) + { + surface_to_block_map[surf_ordinal].insert(touching_blocks.begin(), touching_blocks.end()); + for(auto && block : touching_blocks) + { + block_to_surface_map[block].insert(surf_ordinal); + } + } +private: + std::map< stk::mesh::PartOrdinal, std::set > block_to_surface_map; + std::map< stk::mesh::PartOrdinal, std::set > surface_to_block_map; +}; + +std::vector get_input_mesh_block_names( const Ioss::Region & io_region ); +} // namespace krino + +#endif // Akri_IO_Helpers_h diff --git a/packages/krino/krino/krino_lib/Akri_InterfaceID.hpp b/packages/krino/krino/krino_lib/Akri_InterfaceID.hpp new file mode 100644 index 000000000000..a72328ac6b8b --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_InterfaceID.hpp @@ -0,0 +1,98 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef AKRI_INTERFACEID_H_ +#define AKRI_INTERFACEID_H_ + +#include +#include +#include +#include + +#include + +namespace krino { + +class InterfaceID; +typedef std::map CrossingMap; +typedef std::map CrossingSignMap; + +class InterfaceID +{ +public: + InterfaceID() : ls_1(-1), ls_2(-1) {} + InterfaceID(const int first, const int second) : ls_1(std::min(first, second)), + ls_2(std::max(first, second)) {} + + bool is_single_ls() const { return ls_1 == ls_2; } + int first_ls() const { return ls_1; } + int second_ls() const {return ls_2; } + void fill_sorted_domains(std::vector & sortedDomains) const; + + static std::vector all_phase_pairs(const std::set & phases); +private: + int ls_1; + int ls_2; +}; + +inline bool operator<(const InterfaceID lhs, const InterfaceID rhs) +{ + if(lhs.first_ls() < rhs.first_ls()) return true; + if(rhs.first_ls() < lhs.first_ls()) return false; + if(lhs.second_ls() < rhs.second_ls()) return true; + return false; +} + +inline bool operator==(const InterfaceID lhs, const InterfaceID rhs) +{ + return (lhs.first_ls() == rhs.first_ls()) && (lhs.second_ls() == rhs.second_ls()); +} + +inline bool operator!=(const InterfaceID lhs, const InterfaceID rhs) +{ + return !(lhs == rhs); +} + +inline std::ostream & operator<<(std::ostream &os, const InterfaceID lhs) +{ + os << "InterfaceID(" << lhs.first_ls() << ", " << lhs.second_ls() << ")"; + return os; +} + +inline std::vector InterfaceID::all_phase_pairs(const std::set & phases) +{ + std::vector result; + std::set::const_iterator end_phase = phases.end(); + for(std::set::const_iterator it = phases.begin(); it != end_phase; ++it) + { + std::set::const_iterator it2 = it; + for(++it2; it2 != end_phase; ++it2) + { + result.push_back(InterfaceID(*it, *it2)); + } + } + return result; +} + +inline void InterfaceID::fill_sorted_domains(std::vector & sortedDomains) const +{ + sortedDomains.clear(); + if (first_ls() == second_ls()) + { + sortedDomains.push_back(first_ls()); + } + else + { + sortedDomains.push_back(first_ls()); + sortedDomains.push_back(second_ls()); + } +} + +} // namespace krino + +#endif /* AKRI_INTERFACEID_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_Interface_Name_Generator.hpp b/packages/krino/krino/krino_lib/Akri_Interface_Name_Generator.hpp new file mode 100644 index 000000000000..98e9bde5c0e7 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Interface_Name_Generator.hpp @@ -0,0 +1,71 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Interface_Name_Generator_h +#define Akri_Interface_Name_Generator_h +// + +#include + +namespace krino { + +// Interface_Name_Generator are used in Phase_Support::decompose_blocks() to determine the name of the +// interface sideset within a block so that cdfem death and LS-like problems can have different naming conventions +// that are more user-friendly for each case. +class Interface_Name_Generator +{ +public: + virtual ~Interface_Name_Generator() {} + virtual std::string interface_name(const std::string & io_part_name, const std::string & phase_name) const = 0; + virtual std::string interface_superset_name(const std::string & phase_name) const = 0; + virtual std::string interface_subset_name(const std::string & io_part_name, const std::string & phase_name) const = 0; +}; + +class LS_Name_Generator : public Interface_Name_Generator +{ +public: + virtual ~LS_Name_Generator() {} + virtual std::string interface_name(const std::string & io_part_name, const std::string & phase_name) const + { + return "surface_"+io_part_name+"_"+phase_name; + } + virtual std::string interface_superset_name(const std::string & phase_name) const + { + return "surface_"+phase_name; + } + virtual std::string interface_subset_name(const std::string & io_part_name, const std::string & phase_name) const + { + return "surface_"+io_part_name+"_"+phase_name; + } +}; + +class Death_Name_Generator : public Interface_Name_Generator +{ +public: + Death_Name_Generator(std::string spec_name) : death_spec_name(spec_name) {} + virtual ~Death_Name_Generator() {} + virtual std::string interface_name(const std::string & io_part_name, const std::string & phase_name) const + { + return "surface_"+io_part_name+"_"+death_spec_name; + } + virtual std::string interface_superset_name(const std::string & phase_name) const + { + return "surface_"+death_spec_name; + } + virtual std::string interface_subset_name(const std::string & io_part_name, const std::string & phase_name) const + { + return "surface_"+io_part_name+"_"+death_spec_name; + } +private: + std::string death_spec_name; +}; + + +} // namespace krino + +#endif // Akri_Interface_Name_Generator_h diff --git a/packages/krino/krino/krino_lib/Akri_Intersection_Points.cpp b/packages/krino/krino/krino_lib/Akri_Intersection_Points.cpp new file mode 100644 index 000000000000..f75cfc998e3c --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Intersection_Points.cpp @@ -0,0 +1,267 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include + +#include "../interface_geometry_interface/Akri_InterfaceGeometry.hpp" + +namespace krino { + +EdgeIntersection::EdgeIntersection(const IntersectionPoint & intersectionPt) +{ + const std::vector & intPtNodes = intersectionPt.get_nodes(); + ThrowAssert(intPtNodes.size() == 2); + nodes = {intPtNodes[0], intPtNodes[1]}; + crossingLocation = intersectionPt.get_weights()[1]; + const auto & domains = intersectionPt.get_sorted_domains(); + ThrowAssert(domains.size() == 1 || domains.size() == 2); + interface = (domains.size() == 1) ? InterfaceID(domains[0],domains[0]) : InterfaceID(domains[0], domains[1]); +} + +std::string debug_output(const stk::mesh::BulkData & mesh, const IntersectionPoint & intersectionPoint) +{ + const std::vector & nodes = intersectionPoint.get_nodes(); + const std::vector & weights = intersectionPoint.get_weights(); + const std::vector & domains = intersectionPoint.get_sorted_domains(); + std::ostringstream os; + os << "intersection point domains={ "; + for (int domain : domains) + os << domain << " "; + os << "} stencil={ "; + for (size_t i=0; i & firstVec, const std::vector & secondVec) +{ + for (int domain : secondVec) + if (!std::binary_search(firstVec.begin(), firstVec.end(), domain)) + return false; + return true; +} + +bool any_node_already_captures_intersection_point(const std::vector & nodes, + const std::vector & intersectionPointSortedDomains, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) +{ + for (auto node : nodes) + { + auto iter = nodesToCapturedDomains.find(node); + if (iter != nodesToCapturedDomains.end()) + { + const std::vector & nodeSortedDomains = iter->second; + if (first_sorted_vector_of_domains_contains_all_domains_in_second_vector(nodeSortedDomains, intersectionPointSortedDomains)) + return true; + } + } + return false; +} + +bool domains_already_snapped_to_node_are_also_at_intersection_point(const NodeToCapturedDomainsMap & nodesToCapturedDomains, stk::mesh::Entity node, const std::vector & intersectionPointDomains) +{ + const auto iter = nodesToCapturedDomains.find(node); + if (iter == nodesToCapturedDomains.end()) + return true; + return first_sorted_vector_of_domains_contains_all_domains_in_second_vector(intersectionPointDomains, iter->second); +} + +static bool any_entity_in_first_vector_is_contained_in_second_sorted_vector(const std::vector & firstVec, const std::vector & secondVec) +{ + for (auto && first : firstVec) + if (std::binary_search(secondVec.begin(), secondVec.end(), first)) + return true; + return false; +} + +IntersectionPointFilter +keep_all_intersection_points_filter() +{ + auto filter = + [](const std::vector & intersectionPointNodes, const std::vector & intersectionPointSortedDomains) + { + return true; + }; + return filter; +} + +static +void pack_intersection_points_for_owners_of_nodes(const stk::mesh::BulkData & mesh, + const std::vector & intersectionPoints, + stk::CommSparse &commSparse) +{ + std::vector intersectionPointNodeOwners; + stk::pack_and_communicate(commSparse,[&]() + { + for (auto && intersectionPoint : intersectionPoints) + { + intersectionPointNodeOwners.clear(); + for (auto && node : intersectionPoint.get_nodes()) + { + const int procId = mesh.parallel_owner_rank(node); + if (procId != commSparse.parallel_rank()) + intersectionPointNodeOwners.push_back(procId); + } + stk::util::sort_and_unique(intersectionPointNodeOwners); + for (int procId : intersectionPointNodeOwners) + { + commSparse.send_buffer(procId).pack(intersectionPoint.get_nodes().size()); + for (auto && node : intersectionPoint.get_nodes()) + commSparse.send_buffer(procId).pack(mesh.identifier(node)); + for (auto && weight : intersectionPoint.get_weights()) + commSparse.send_buffer(procId).pack(weight); + commSparse.send_buffer(procId).pack(intersectionPoint.get_sorted_domains().size()); + for (auto && domain : intersectionPoint.get_sorted_domains()) + commSparse.send_buffer(procId).pack(domain); + } + } + }); +} + +static +void unpack_intersection_points(const stk::mesh::BulkData & mesh, + std::vector & intersectionPoints, + stk::CommSparse &commSparse) +{ + std::vector intersectionPointNodes; + std::vector intersectionPointWeights; + std::vector intersectionPointDomains; + stk::mesh::EntityId nodeId; + const bool intersectionPointIsOwned = false; + + stk::unpack_communications(commSparse, [&](int procId) + { + stk::CommBuffer & buffer = commSparse.recv_buffer(procId); + + while ( buffer.remaining() ) + { + size_t numNodes; + commSparse.recv_buffer(procId).unpack(numNodes); + intersectionPointNodes.resize(numNodes); + for (auto && node : intersectionPointNodes) + { + commSparse.recv_buffer(procId).unpack(nodeId); + node = mesh.get_entity(stk::topology::NODE_RANK, nodeId); + } + intersectionPointWeights.resize(numNodes); + for (auto && weight : intersectionPointWeights) + commSparse.recv_buffer(procId).unpack(weight); + size_t numDomains; + commSparse.recv_buffer(procId).unpack(numDomains); + intersectionPointDomains.resize(numDomains); + for (auto && domain : intersectionPointDomains) + { + commSparse.recv_buffer(procId).unpack(domain); + } + intersectionPoints.emplace_back(intersectionPointIsOwned, intersectionPointNodes, intersectionPointWeights, intersectionPointDomains); + } + }); +} + +void communicate_intersection_points(const stk::mesh::BulkData & mesh, std::vector & intersectionPoints) +{ + stk::CommSparse commSparse(mesh.parallel()); + pack_intersection_points_for_owners_of_nodes(mesh, intersectionPoints, commSparse); + unpack_intersection_points(mesh, intersectionPoints, commSparse); +} + +static std::vector build_intersection_points(const stk::mesh::BulkData & mesh, + const InterfaceGeometry & geometry, + const NodeToCapturedDomainsMap & nodesToCapturedDomains, + const IntersectionPointFilter & intersectionPointFilter) +{ + std::vector elementsToIntersect; + stk::mesh::get_entities( mesh, stk::topology::ELEMENT_RANK, mesh.mesh_meta_data().locally_owned_part(), elementsToIntersect, false); + + std::vector intersectionPoints; + geometry.append_element_intersection_points(mesh, nodesToCapturedDomains, elementsToIntersect, intersectionPointFilter, intersectionPoints); + communicate_intersection_points(mesh, intersectionPoints); + + return intersectionPoints; +} + +std::vector build_all_intersection_points(const stk::mesh::BulkData & mesh, + const InterfaceGeometry & geometry, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) +{ + const IntersectionPointFilter intersectionPointFilter = keep_all_intersection_points_filter(); + return build_intersection_points(mesh, geometry, nodesToCapturedDomains, intersectionPointFilter); +} + +static IntersectionPointFilter +filter_intersection_points_to_those_not_already_handled(const NodeToCapturedDomainsMap & nodesToCapturedDomains) +{ + auto filter = + [&nodesToCapturedDomains](const std::vector & intersectionPointNodes, const std::vector & intersectionPointSortedDomains) + { + return !any_node_already_captures_intersection_point(intersectionPointNodes, intersectionPointSortedDomains, nodesToCapturedDomains); + }; + return filter; +} + +std::vector build_uncaptured_intersection_points(const stk::mesh::BulkData & mesh, + const InterfaceGeometry & geometry, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) +{ + IntersectionPointFilter intersectionPointFilter = filter_intersection_points_to_those_not_already_handled(nodesToCapturedDomains); + + return build_intersection_points(mesh, geometry, nodesToCapturedDomains, intersectionPointFilter); +} + +static IntersectionPointFilter +filter_intersection_points_to_those_using_previous_iteration_snap_nodes_but_not_already_handled(const NodeToCapturedDomainsMap & nodesToCapturedDomains, const std::vector & iterationSortedSnapNodes) +{ + auto filter = + [&nodesToCapturedDomains, &iterationSortedSnapNodes](const std::vector & intersectionPointNodes, const std::vector & intersectionPointSortedDomains) + { + return any_entity_in_first_vector_is_contained_in_second_sorted_vector(intersectionPointNodes, iterationSortedSnapNodes) && + !any_node_already_captures_intersection_point(intersectionPointNodes, intersectionPointSortedDomains, nodesToCapturedDomains); + }; + return filter; +} + +std::vector get_owned_elements_using_nodes_knowing_that_nodes_dont_have_common_elements(const stk::mesh::BulkData & mesh, + const std::vector & nodes) +{ + std::vector nodeElements; + for (auto node : nodes) + for (auto element : StkMeshEntities{mesh.begin_elements(node), mesh.end_elements(node)}) + if (mesh.bucket(element).owned()) + nodeElements.push_back(element); + return nodeElements; +} + +void update_intersection_points_after_snap_iteration(const stk::mesh::BulkData & mesh, + const InterfaceGeometry & geometry, + const std::vector & iterationSortedSnapNodes, + const NodeToCapturedDomainsMap & nodesToCapturedDomains, + std::vector & intersectionPoints) +{ + const auto intersectionPointFilter = filter_intersection_points_to_those_using_previous_iteration_snap_nodes_but_not_already_handled(nodesToCapturedDomains, iterationSortedSnapNodes); + + size_t newSize=0; + for (auto && intersectionPoint : intersectionPoints) + { + if (intersectionPoint.is_owned() && !any_entity_in_first_vector_is_contained_in_second_sorted_vector(intersectionPoint.get_nodes(), iterationSortedSnapNodes)) + intersectionPoints[newSize++] = intersectionPoint; // FIXME use custom swap? + } + intersectionPoints.erase(intersectionPoints.begin()+newSize, intersectionPoints.end()); + + const std::vector updateElements = get_owned_elements_using_nodes_knowing_that_nodes_dont_have_common_elements(mesh, iterationSortedSnapNodes); + geometry.append_element_intersection_points(mesh, nodesToCapturedDomains, updateElements, intersectionPointFilter, intersectionPoints); + communicate_intersection_points(mesh, intersectionPoints); +} + +} diff --git a/packages/krino/krino/krino_lib/Akri_Intersection_Points.hpp b/packages/krino/krino/krino_lib/Akri_Intersection_Points.hpp new file mode 100644 index 000000000000..0f7c07da5832 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Intersection_Points.hpp @@ -0,0 +1,75 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_INTERSECTION_POINTS_H_ +#define KRINO_INCLUDE_AKRI_INTERSECTION_POINTS_H_ + +#include +#include +#include +#include +#include + +namespace krino { + +class CDFEM_Support; +class Phase_Support; +class CDFEM_Parent_Edge; +class InterfaceGeometry; + +class IntersectionPoint +{ +public: + IntersectionPoint(const bool owned, const std::vector & nodes, const std::vector & weights, const std::vector & sortedDomains) + : mOwned(owned), mNodes(nodes), mWeights(weights), mSortedDomains(sortedDomains) {ThrowAssert(mNodes.size() == mWeights.size());} + bool is_owned() const { return mOwned; } + const std::vector & get_nodes() const { return mNodes; } + const std::vector & get_weights() const { return mWeights; } + const std::vector & get_sorted_domains() const { return mSortedDomains; } +private: + bool mOwned; + std::vector mNodes; + std::vector mWeights; + std::vector mSortedDomains; +}; + +struct EdgeIntersection +{ + EdgeIntersection(const IntersectionPoint & intersectionPt); + std::array nodes; + double crossingLocation; + InterfaceID interface; +}; + +void communicate_intersection_points(const stk::mesh::BulkData & mesh, std::vector & intersectionPoints); + +typedef std::function &, const std::vector &)> IntersectionPointFilter; + +bool first_sorted_vector_of_domains_contains_all_domains_in_second_vector(const std::vector & firstVec, const std::vector & secondVec); +bool domains_already_snapped_to_node_are_also_at_intersection_point(const NodeToCapturedDomainsMap & nodesToCapturedDomains, stk::mesh::Entity node, const std::vector & intersectionPointDomains); +std::string debug_output(const stk::mesh::BulkData & mesh, const IntersectionPoint & intersectionPoint); + +IntersectionPointFilter keep_all_intersection_points_filter(); + +std::vector build_all_intersection_points(const stk::mesh::BulkData & mesh, + const InterfaceGeometry & geometry, + const NodeToCapturedDomainsMap & nodesToCapturedDomains); + +std::vector build_uncaptured_intersection_points(const stk::mesh::BulkData & mesh, + const InterfaceGeometry & geometry, + const NodeToCapturedDomainsMap & nodesToCapturedDomains); + +void update_intersection_points_after_snap_iteration(const stk::mesh::BulkData & mesh, + const InterfaceGeometry & geometry, + const std::vector & iterationSortedSnapNodes, + const NodeToCapturedDomainsMap & nodesToCapturedDomains, + std::vector & intersectionPoints); + +} + +#endif /* KRINO_INCLUDE_AKRI_INTERSECTION_POINTS_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_LevelSet.cpp b/packages/krino/krino/krino_lib/Akri_LevelSet.cpp new file mode 100644 index 000000000000..64dc628a7d55 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_LevelSet.cpp @@ -0,0 +1,1822 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace krino { + +bool all_nodes_have_field_data(const stk::mesh::BulkData& stk_bulk, stk::mesh::Entity entity, const stk::mesh::FieldBase& field) +{ + const unsigned nnodes = stk_bulk.num_nodes(entity); + const stk::mesh::Entity* nodes = stk_bulk.begin_nodes(entity); + for (unsigned i = 0; i < nnodes; ++i ) + { + if (field_bytes_per_entity(field, nodes[i]) == 0) + { + return false; + } + } + return true; +} + +std::vector LevelSet::the_levelSet_names; + +LevelSet_Identifier LevelSet::get_identifier(const std::string & name) +{ + std::string upper_name = name; + std::transform(upper_name.begin(), upper_name.end(), upper_name.begin(), ::toupper); + for (unsigned i=0; i 0; +} + +BoundingBox LevelSet::get_IC_surface_bounding_box() +{ + IC_Alg& ic_alg = get_IC_alg(); + return ic_alg.get_surface_bounding_box(); +} + +IC_Alg& LevelSet::get_IC_alg() +{ + if (!my_IC_alg) + { + my_IC_alg = std::make_unique(*this); + } + return *my_IC_alg; +} + +void LevelSet::set_current_coordinates(stk::mesh::MetaData & meta, const FieldRef ref) +{ + LevelSetManager & ls_manager = LevelSetManager::get(meta); + ls_manager.set_current_coordinates(ref); +} + +FieldRef LevelSet::get_current_coordinates(stk::mesh::MetaData & meta) +{ + LevelSetManager & ls_manager = LevelSetManager::get(meta); + FieldRef current_coords = ls_manager.get_current_coordinates(); + if (!current_coords.valid()) + { + const stk::mesh::FieldBase * meta_coords = meta.coordinate_field(); + ThrowRequireMsg(nullptr != meta_coords, "Coordinates must be defined before calling LevelSet::get_current_coordinates()."); + ls_manager.set_current_coordinates(meta_coords); + current_coords = ls_manager.get_current_coordinates(); + } + return current_coords; +} + +void LevelSet::setup(stk::mesh::MetaData & meta) +{ + const LevelSetManager & region_ls = LevelSetManager::get(meta); + for (auto&& ls : region_ls) + { + ls->setup(); + } +} + +void LevelSet::post_commit_setup(stk::mesh::MetaData & meta) +{ + const double max_elem_size = compute_maximum_element_size(meta.mesh_bulk_data()); + + const LevelSetManager & region_ls = LevelSetManager::get(meta); + for (auto&& ls : region_ls) + { + ls->setup(); + if (ls->my_narrow_band_multiplier > 0.) + { + ls->narrow_band_size(ls->my_narrow_band_multiplier * max_elem_size); + } + else if (ls->narrow_band_size() > 0.) + { + const double narrow_band = ls->narrow_band_size(); + ThrowErrorMsgIf(!(narrow_band > max_elem_size), + "Currently, narrow_band_size must be greater than the maximum element size of " << max_elem_size << std::endl + << "in order to avoid unintentional accuracy degradation. If this feature is needed, please contact krino developers."); + } + } +} + + +void LevelSet::setup(void) +{ + register_fields(); + + facets.reset(new Faceted_Surface("current_facets")); + facets_old.reset(new Faceted_Surface("old_facets")); + + // initializes CDFEM_Support + if (!meta().is_commit()) + { + CDFEM_Support::get(my_meta); + } +} +//-------------------------------------------------------------------------------- +void LevelSet::register_fields(void) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::register_fields(void)"); /* %TRACE% */ + + if (trackIsoSurface || aux_meta().has_field(stk::topology::NODE_RANK, my_distance_name)) + { + // non-krino region + if (trackIsoSurface) + { + if (aux_meta().has_field(stk::topology::NODE_RANK, my_isovar_name)) + { + set_isovar_field( aux_meta().get_field(stk::topology::NODE_RANK, my_isovar_name) ); + } + else if (aux_meta().has_field(stk::topology::ELEMENT_RANK, my_isovar_name)) + { + set_isovar_field( aux_meta().get_field(stk::topology::ELEMENT_RANK, my_isovar_name) ); + } + else + { + ThrowErrorMsgIf( + true, "Isosurface variable '" << my_isovar_name << "' should already be registered."); + } + } + else + { + const FieldRef distance_ref = aux_meta().get_field(stk::topology::NODE_RANK, my_distance_name); + if ( distance_ref.number_of_states() == 1 ) + { + set_distance_field( distance_ref ); + set_old_distance_field( distance_ref ); + } + else + { + set_distance_field( distance_ref.field_state(stk::mesh::StateNew) ); + set_old_distance_field( distance_ref.field_state(stk::mesh::StateOld) ); + } + + set_isovar_field( my_distance_field ); + } + } + else + { + if (aux_meta().using_fmwk()) + { + ThrowRuntimeError("ERROR: field " << my_distance_name << " is not registered for level set " << name() << ". " + << "This can be caused by an incorrect Distance Variable. " + << "Or, in aria, there could be a conflict between the specified subindex and the named species. " + << "If so, try using a subindex greater than the number of species for your level set."); + } + + // krino region + const FieldType & type_double = FieldType::REAL; + + if(krinolog.shouldPrint(LOG_DEBUG)) + krinolog << "KRINO: Registering distance variable with name '" << my_distance_name << "'." << stk::diag::dendl; + + const bool cdfem_is_active = krino::CDFEM_Support::is_active(meta()); + if (cdfem_is_active) + { + Phase_Support phase_support = Phase_Support::get(meta()); + for (auto partPtr : meta().get_mesh_parts()) + { + if (partPtr->primary_entity_rank() == stk::topology::ELEMENT_RANK && + phase_support.level_set_is_used_by_nonconformal_part(this, phase_support.find_nonconformal_part(*partPtr))) + { + FieldRef distance_ref = aux_meta().register_field( my_distance_name, type_double, stk::topology::NODE_RANK, 1, 1, *partPtr ); + set_old_distance_field( distance_ref ); + set_distance_field( distance_ref ); + } + } + } + else + { + FieldRef distance_ref = aux_meta().register_field( my_distance_name, type_double, stk::topology::NODE_RANK, 2, 1, meta().universal_part() ); + set_old_distance_field( FieldRef( distance_ref, stk::mesh::StateOld ) ); + set_distance_field( FieldRef( distance_ref, stk::mesh::StateNew ) ); + } + + set_isovar_field( my_distance_field ); + } + + if (!myTimeOfArrivalBlockSpeedsByName.empty()) + { + myTimeOfArrivalBlockSpeeds.resize(my_meta.get_parts().size()); + for (auto entry : myTimeOfArrivalBlockSpeedsByName) + { + if (my_aux_meta.has_part(entry.first)) + { + myTimeOfArrivalBlockSpeeds[my_aux_meta.get_part(entry.first).mesh_meta_data_ordinal()] = entry.second; + } + else + { + ThrowErrorMsgIf(true, "Could not find block " << entry.first << " when setting speed for computing time-of-arrival."); + } + } + } + + if (!my_time_of_arrival_element_speed_field_name.empty()) + { + const bool hasSpeedField = aux_meta().has_field(stk::topology::ELEMENT_RANK, my_time_of_arrival_element_speed_field_name); + ThrowErrorMsgIf(!hasSpeedField, "Could not find element speed field " << my_time_of_arrival_element_speed_field_name << " for computing time-of-arrival."); + myTimeOfArrivalElementSpeedField = aux_meta().get_field(stk::topology::ELEMENT_RANK, my_time_of_arrival_element_speed_field_name); + ThrowRequireMsg(myTimeOfArrivalBlockSpeeds.empty(), "Speed for time-of-arrival calculation should be specified via element speed or block speed (not both)."); + } +} + +//----------------------------------------------------------------------------------- + +void +LevelSet::set_time_of_arrival_block_speed(const std::string & blockName, const double blockSpeed) +{ + std::string lowerBlockName = blockName; + std::transform(lowerBlockName.begin(), lowerBlockName.end(), lowerBlockName.begin(), ::tolower); + auto entry = myTimeOfArrivalBlockSpeedsByName.find(lowerBlockName); + ThrowRequireMsg(entry == myTimeOfArrivalBlockSpeedsByName.end(), "Speed for block " << blockName << " specified more than once."); + myTimeOfArrivalBlockSpeedsByName[lowerBlockName] = blockSpeed; +} + +//----------------------------------------------------------------------------------- +void +LevelSet::facets_exoii(void) +{ + /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::facets_exoii(void)"); /* %TRACE% */ + Faceted_Surface & f = *facets; + facets_exoii(f); +} + + +void +LevelSet::facets_exoii(Faceted_Surface & cs) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::facets_exoii(Faceted_Surface & cs)"); /* %TRACE% */ + + int nfaces = cs.size(); + const int nodes_per_elem = spatial_dimension; + const int nnodes = nfaces * nodes_per_elem; + const int nelems = nfaces * 1; //writing out faces as elements + + // construct file name + ThrowAssert(my_facetFileIndex <= 99999); + char counterChar[6]; + std::sprintf(counterChar, "%.5d", my_facetFileIndex); + std::string fn = std::string("facets.") + name() + ".e-s" + std::string(counterChar); + + // Type (ExodusII) is hard-wired at this time. + Ioss::DatabaseIO *db = Ioss::IOFactory::create("exodusII", fn, Ioss::WRITE_RESULTS); + Ioss::Region io(db, "FacetRegion"); + + // find offsets for consistent global numbering + int elem_start = 0; + if ( mesh().parallel_size() > 1 ) { + std::vector< int > num_faces; + num_faces.resize( mesh().parallel_size() ); + + // Gather everyone's facet list sizes + // I don't think there is a framework call for this... + MPI_Allgather( &nfaces, 1, MPI_INT, + &(num_faces[0]), 1, MPI_INT, mesh().parallel() ); + for (int i = 0; i< mesh().parallel_rank(); ++i) { + elem_start += num_faces[i]; + } + } + + const std::string description = "level set interface facets"; + io.property_add(Ioss::Property("title", description)); + io.begin_mode(Ioss::STATE_DEFINE_MODEL); + + // if we have no elements bail now + if ( 0 == nelems ) { + io.end_mode(Ioss::STATE_DEFINE_MODEL); + my_facetFileIndex++; + return; + } + + Ioss::NodeBlock *nb = new Ioss::NodeBlock(db, "nodeblock_1", nnodes, spatial_dimension); + io.add(nb); + + std::string el_type; + if (spatial_dimension == 3) + el_type = "trishell3"; + else + el_type = "shellline2d2"; + + Ioss::ElementBlock *eb = new Ioss::ElementBlock(db, "block_1", el_type, nelems); + io.add(eb); + + io.end_mode(Ioss::STATE_DEFINE_MODEL); + io.begin_mode(Ioss::STATE_MODEL); + + const FacetOwningVec & cs_surfaces = cs.get_facets(); + + // loop over elements and node to create maps + { + std::vector< int > nmap, emap; + nmap.reserve(nnodes); + emap.reserve(nelems); + + for ( unsigned n=0, e=0; eput_field_data("ids", nmap); + eb->put_field_data("ids", emap); + } + + // generate coordinates + { + std::vector< double > xyz; + xyz.reserve(spatial_dimension*nnodes); + + for ( auto&& cs_surface : cs_surfaces ) { + for ( int j = 0; j < nodes_per_elem; ++j ) { + const Vector3d & vert = cs_surface->facet_vertex(j); + xyz.push_back(vert[0]); + xyz.push_back(vert[1]); + if (3 == spatial_dimension) xyz.push_back(vert[2]); + } + } + nb->put_field_data("mesh_model_coordinates", xyz); + } + + // generate connectivity + { + std::vector< int > conn; + conn.reserve(nnodes); + for ( int n = 0; n < nnodes; ++n ) { + conn.push_back(nodes_per_elem*elem_start + n+1); + } + eb->put_field_data("connectivity", conn); + } + + io.end_mode(Ioss::STATE_MODEL); + my_facetFileIndex++; +} + +//----------------------------------------------------------------------------------- +void +LevelSet::compute_surface_distance(const double narrowBandSize, const double farFieldValue) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::compute_surface_distance(void)"); /* %TRACE% */ + + stk::mesh::Selector surface_selector = selectUnion(my_compute_surface_distance_parts); + + if (narrowBandSize > 0.0) + { + my_narrow_band_size = narrowBandSize; + } + + const stk::mesh::Field& coords = reinterpret_cast&>(get_coordinates_field().field()); + const stk::mesh::Field& dist = reinterpret_cast&>(get_distance_field().field()); + + Compute_Surface_Distance::calculate( + mesh(), + get_timer(), + coords, + dist, + surface_selector, + my_narrow_band_size, + farFieldValue); + + // output for time 0 is from old + if (!(my_distance_field == my_old_distance_field)) + { + stk::mesh::field_copy(my_distance_field, my_old_distance_field); + } + +} + +//----------------------------------------------------------------------------------- +void +LevelSet::set_surface_distance(std::vector surfaces, const double in_distance) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::compute_surface_distance(void)"); /* %TRACE% */ + + const FieldRef dField = get_distance_field(); + + const stk::mesh::Selector selector = stk::mesh::selectField(dField) & selectUnion(surfaces); + stk::mesh::BucketVector const& buckets = mesh().get_buckets( stk::topology::NODE_RANK, selector); + + for ( auto && bucket : buckets ) + { + const stk::mesh::Bucket & b = *bucket; + + const size_t length = b.size(); + double *dist = field_data(dField , b); + + for ( size_t n = 0; n < length; ++n ) + { + dist[n] = in_distance; + } + } +} + +//----------------------------------------------------------------------------------- +void +LevelSet::advance_semilagrangian(const double deltaTime) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::advance_semilagrangian(const double deltaTime)"); /* %TRACE% */ + + if(trackIsoSurface) + { + redistance(); + } + else + { + stk::mesh::Selector selector(my_meta.universal_part()); + + // store existing facets in facets_old + facets->swap( *facets_old ); + + // get non-local facets such that we have copies of all old facets + // within the range of this proc's nodes + prepare_to_compute_distance( deltaTime, selector ); + + // compute nodal distances with semi-lagrangian step + stk::mesh::field_copy(my_old_distance_field, my_distance_field); + compute_distance_semilagrangian( deltaTime, selector ); + + // build local facet list + build_facets_locally(my_meta.universal_part()); + + // debugging + if (krinolog.shouldPrint(LOG_FACETS)) + { + facets_exoii(); + } + } +} + +//----------------------------------------------------------------------------------- +void +LevelSet::initialize(stk::mesh::MetaData & meta, const bool requires_additional_initialization) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::initialize(void)"); /* %TRACE% */ + + const LevelSetManager & region_ls = LevelSetManager::get(meta); + for (auto&& ls : region_ls) + { + ls->initialize(0., requires_additional_initialization); + } +} + +//----------------------------------------------------------------------------------- +void +LevelSet::initialize(const double time, const bool requires_additional_initialization) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::initialize(void)"); /* %TRACE% */ + + if (trackIsoSurface) + { + return; + } + + if(get_isovar_field().valid()) get_isovar_field().field().sync_to_host(); + if(get_distance_field().valid()) get_distance_field().field().sync_to_host(); + if(get_old_distance_field().valid()) get_old_distance_field().field().sync_to_host(); + + if(get_isovar_field().valid()) get_isovar_field().field().modify_on_host(); + if(get_distance_field().valid()) get_distance_field().field().modify_on_host(); + if(get_old_distance_field().valid()) get_old_distance_field().field().modify_on_host(); + + if (!my_compute_surface_distance_parts.empty()) + { + compute_surface_distance(); + return; + } + + krinolog << "Initializing levelset " << name() << "..." << stk::diag::dendl; + + /* process analytic surfaces */ + if (my_IC_alg) + { + my_IC_alg->execute(time, requires_additional_initialization); + } + + if (compute_time_of_arrival()) + { + fast_marching_redistance(my_meta.universal_part(), true); + } + else if (my_perform_initial_redistance) + { + constrained_redistance(); + } + + // Offset initialized LS if requested + if (my_ic_offset != 0.0) increment_distance(my_ic_offset, false); + + // Scale initialized LS if requested + if (my_ic_scale != 1.0) scale_distance(my_ic_scale); + + stk::mesh::field_copy(get_distance_field(), get_old_distance_field()); +} + +double LevelSet::constrained_redistance(const bool use_initial_vol) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::constrained_redistance(const bool use_initial_vol)"); /* %TRACE% */ + + // Steps: + // 1. measure current volume + // 2. perform regular redistance + // 3. find offset needed to conserve volume + // 4. increment nodal distance by this amount + + if (get_isovar_field().valid()) get_isovar_field().field().sync_to_host(); + if (get_distance_field().valid()) get_distance_field().field().sync_to_host(); + if (get_old_distance_field().valid()) get_old_distance_field().field().sync_to_host(); + + if (get_isovar_field().valid()) get_isovar_field().field().modify_on_host(); + if (get_distance_field().valid()) get_distance_field().field().modify_on_host(); + if (get_old_distance_field().valid()) get_old_distance_field().field().modify_on_host(); + + // measure current area and volumes + double start_area, start_neg_vol, start_pos_vol; + compute_sizes( start_area,start_neg_vol, start_pos_vol, 0. ); + if ( 0. == start_neg_vol || 0. == start_area ) + { + krinolog << "Skipping redistancing operation. Volume and/or area have zero value. Area: " + << start_area << " Volume: " << start_neg_vol << stk::diag::dendl; + return 0.; + } + + if (use_initial_vol) + { + krinolog << "Performing conserved redistancing..." << stk::diag::dendl; + + if (my_initial_neg_vol <= 0.0) + { + my_initial_neg_vol = start_neg_vol; + } + start_pos_vol = start_neg_vol + start_pos_vol - my_initial_neg_vol; + start_neg_vol = my_initial_neg_vol; + } + else + { + krinolog << "Performing constrained redistancing..." << stk::diag::dendl; + } + + // perform regular redistance + redistance(); + + krinolog << "Correcting for volume change:" << stk::diag::dendl; + + // find correction needed to conserve volume + const double correction = find_redistance_correction( start_area, start_neg_vol, start_pos_vol ); + + // update nodal distance field + increment_distance( -correction, true ); + + return my_initial_neg_vol; +} + +//-------------------------------------------------------------------------------- + +static std::function(const double)> build_volume_error_function_with_derivative(LevelSet & ls, const double startingNegVol, const double startingPosVol) +{ + auto volume_error_function_with_derivative = [&ls, startingNegVol, startingPosVol](const double x) + { + double area, negVol, posVol; + ls.compute_sizes( area, negVol, posVol, x ); + const double totVol = startingNegVol+startingPosVol; + const double relativeError = (negVol - startingNegVol) / totVol; + krinolog << " Correction = " << x + << ", Current volume = " << negVol + << ", Target volume = " << startingNegVol + << ", Relative Error = " << std::abs(relativeError) + << stk::diag::dendl; + const double derivative = area/totVol; + return std::make_pair(relativeError, derivative); + }; + return volume_error_function_with_derivative; +} + +double +LevelSet::find_redistance_correction( const double start_area, + const double start_neg_vol, + const double start_pos_vol, + const int max_iterations, + const double tol ) +{ /* %TRACE% */ /* %TRACE% */ + auto volume_error_function_with_derivative = build_volume_error_function_with_derivative(*this, start_neg_vol, start_pos_vol); + const auto result = find_root_newton_raphson(volume_error_function_with_derivative, 0., max_iterations, tol); + + if (!result.first) + { + stk::RuntimeWarningAdHoc() << "\nConstrained renormalization failed to converge to root within " + << max_iterations << " iterations. Continuing with correction " << result.second << "\n"; + } + return result.second; +} + +void +LevelSet::redistance() +{ + redistance(my_meta.universal_part()); +} + +void +LevelSet::redistance(const stk::mesh::Selector & selector) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::redistance(void)"); /* %TRACE% */ + + ThrowErrorMsgIf(!my_time_of_arrival_element_speed_field_name.empty(), "Redistancing a time-of-arrival field will corrupt it."); + + if (get_isovar_field().valid()) get_isovar_field().field().sync_to_host(); + if (get_distance_field().valid()) get_distance_field().field().sync_to_host(); + if (get_old_distance_field().valid()) get_old_distance_field().field().sync_to_host(); + + if (get_isovar_field().valid()) get_isovar_field().field().modify_on_host(); + if (get_distance_field().valid()) get_distance_field().field().modify_on_host(); + if (get_old_distance_field().valid()) get_old_distance_field().field().modify_on_host(); + + if (FAST_MARCHING == my_redistance_method) + { + fast_marching_redistance(selector); + return; + } + ThrowRequire(CLOSEST_POINT == my_redistance_method); + + krinolog << "Redistancing the level set field..." << stk::diag::dendl; + + // our starting point is a nodal variable (like distance or temperature) + // that needs to be contoured to form the surface + // after forming the surface, the nodal distance needs to be calculated + // the newly formed surface should be remain in the vector facets + build_facets_locally(selector); + + // debugging + if (krinolog.shouldPrint(LOG_FACETS)) + { + facets_exoii(); + } + + // swap these facets into facet_old to take advantage of routines + // that are expecting the facets there + facets->swap( *facets_old ); + + // get non-local facets such that we have copies of all "old" facets + // within the range of this proc's nodes + prepare_to_compute_distance( 0., selector ); + + // compute nodal distances with semi-lagrangian step + compute_distance_semilagrangian( 0., selector ); + + // swap so that the facets that were formed remain in the vector facets + facets->swap( *facets_old ); + +} + +double +LevelSet::get_time_of_arrival_speed(stk::mesh::Entity elem, ParallelErrorMessage& err) const +{ + double speed = 1.0; + if (myTimeOfArrivalBlockSpeeds.empty()) + { + if (myTimeOfArrivalElementSpeedField.valid()) + { + const double * speedFieldData = field_data(myTimeOfArrivalElementSpeedField, elem); + if (!speedFieldData) err << "Missing element speed on element " << mesh().identifier(elem) << "\n"; + else speed = *speedFieldData; + + if (speed <= 0.0) + { + err << "Non positive-definite speed " << speed << " found on element " << mesh().identifier(elem) << "\n"; + } + } + } + else + { + const stk::mesh::Part & elemPart = find_element_part(mesh(), elem); + speed = myTimeOfArrivalBlockSpeeds[elemPart.mesh_meta_data_ordinal()]; + ThrowAssert(speed >= 0.0); // Negative speeds should have already been caught and generated error. + if (speed == 0.0) + err << "Speed not specified for block " << elemPart.name() << "\n"; + } + + return speed; +} + +void +LevelSet::fast_marching_redistance(const stk::mesh::Selector & selector, const bool compute_time_of_arrival) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::fast_marching_redistance(const stk::mesh::Selector & selector)"); /* %TRACE% */ + + // Unlike redistance() this method provides an approximate (not exact) distance to the isosurface. + // On the other hand, this method provide solve the Eikonal equation on the mesh. This is slightly + // different than a pure distance function because it provides the distance through domain. It can't see + // through walls like the redistance() method does. + + if (compute_time_of_arrival) krinolog << "Initializing the level set field to be the time-of-arrival using a fast marching method..." << stk::diag::dendl; + else krinolog << "Redistancing the level set field using a fast marching method..." << stk::diag::dendl; + Fast_Marching fm(*this, selector, get_timer()); + fm.redistance(); +} + +//-------------------------------------------------------------------------------- + +void +LevelSet::set_distance( const double & in_distance ) const +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::set_distance( const double & in_distance ) const"); /* %TRACE% */ + + stk::mesh::field_fill(in_distance, get_distance_field()); +} + +void +LevelSet::increment_distance( const double increment, const bool enforce_sign ) const +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::increment_distance( const double & increment ) const"); /* %TRACE% */ + // + // increment the distance everywhere by the given value + // + + const FieldRef dField = get_distance_field(); + + const stk::mesh::Selector active_field_selector = aux_meta().active_not_ghost_selector() & stk::mesh::selectField(dField); + stk::mesh::BucketVector const& buckets = mesh().get_buckets( stk::topology::NODE_RANK, active_field_selector); + + for ( auto && bucket : buckets ) + { + const stk::mesh::Bucket & b = *bucket; + + const size_t length = b.size(); + + double *d = field_data(dField , b); + + for (size_t i = 0; i < length; ++i) + { + const double change = (enforce_sign && sign_change(d[i],d[i]+increment)) ? -0.5*d[i] : increment; + d[i] += change; + } + } // end bucket loop +} + +void +LevelSet::scale_distance( const double scale) const +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::scale_distance( const double & scale ) const"); /* %TRACE% */ + // + // increment the distance everywhere by the given value + // + + const FieldRef dField = get_distance_field(); + + const stk::mesh::Selector active_field_selector = aux_meta().active_not_ghost_selector() & stk::mesh::selectField(dField); + stk::mesh::BucketVector const& buckets = mesh().get_buckets( stk::topology::NODE_RANK, active_field_selector); + + stk::mesh::BucketVector::const_iterator ib = buckets.begin(); + stk::mesh::BucketVector::const_iterator ib_end = buckets.end(); + + // iterate nodes, by buckets, and set distance + for ( ; ib != ib_end ; ++ib ) + { + const stk::mesh::Bucket & b = **ib; + + const size_t length = b.size(); + + double *d = field_data(dField , b); + + for (size_t i = 0; i < length; ++i) + { + d[i] *= scale; + } + } // end bucket loop +} + +void +LevelSet::negate_distance() const +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::increment_distance( const double & increment ) const"); /* %TRACE% */ + + const FieldRef dRef = get_distance_field(); + stk::mesh::field_scale(-1.0, dRef); +} + +//----------------------------------------------------------------------------------- + +void +LevelSet::compute_continuous_gradient() const +{ /* %TRACE[ON]% */ /* %TRACE% */ + // + // Compute a mass-lumped continuous distance gradient + // + + const int dim = mesh().mesh_meta_data().spatial_dimension(); + + const FieldRef contGradRef = aux_meta().get_field(stk::topology::NODE_RANK, "CONT_GRAD"); + const FieldRef nodeAreaRef = aux_meta().get_field(stk::topology::NODE_RANK, "NODE_AREA"); + + + // initialize + stk::mesh::field_fill(0.0, contGradRef); + stk::mesh::field_fill(0.0, nodeAreaRef); + + // intg_pt_locations and intg_weights are just wrappers for framework data + // determinants and grad_distance are arrays containing data that are resized + // only as needed + sierra::Array intg_pt_locations; + sierra::Array intg_weights; + sierra::ArrayContainer determinants; + sierra::ArrayContainer grad_distance; + + // ************************** + // Not-covered-in-nightly RWH + // ************************** + + const FieldRef xField = get_coordinates_field(); + const FieldRef isoField = get_isovar_field(); + + stk::mesh::Selector active_field_selector = stk::mesh::selectField(contGradRef) & aux_meta().active_not_ghost_selector(); + std::vector< stk::mesh::Entity> objs; + stk::mesh::get_selected_entities( active_field_selector, mesh().buckets( stk::topology::ELEMENT_RANK ), objs ); + + for ( auto && elem : objs ) + { + // create element + ContourElement ls_elem( mesh(), elem, xField, isoField ); + + ls_elem.std_intg_pts( intg_pt_locations, intg_weights, determinants, + ls_elem.dist_master_elem() ); + ls_elem.compute_distance_gradient( intg_pt_locations, grad_distance ); + + const unsigned num_intg_pts = intg_pt_locations.dimension(); + const int npe = ls_elem.dist_topology().num_nodes(); + + const double * shape_fcn_ptr = ls_elem.dist_master_elem().shape_fcn(); + const sierra::Array shape_fcn(shape_fcn_ptr,npe,num_intg_pts); + + const stk::mesh::Entity* elem_nodes = mesh().begin_nodes(elem); + + for ( int i = 0; i < npe; ++i ) + { + double * cont_grad = field_data(contGradRef, elem_nodes[i]); + double * area_ptr = field_data(nodeAreaRef, elem_nodes[i]); + double & area = *area_ptr; + + for (unsigned ip = 0; ip < num_intg_pts; ++ip ) + { + const double NdV = shape_fcn(i,ip) * intg_weights(ip) * determinants(ip); + area += NdV; + + for ( int d = 0; d < dim; d++ ) + { + cont_grad[d] += grad_distance(d,ip) * NdV; + //krinolog << "grad_distance(" << d << "," << ip << ") = " << grad_distance(d,ip) << stk::diag::dendl; + } + } + } + } + + // iterate nodes, by buckets, and calculate average gradient + + stk::mesh::BucketVector const& buckets = mesh().get_buckets(stk::topology::NODE_RANK, active_field_selector); + + for ( auto && bucket : buckets ) + { + const stk::mesh::Bucket & b = *bucket; + + const size_t length = b.size(); + + double * cont_grad = field_data(contGradRef, b); + double * area = field_data(nodeAreaRef, b); + + for (size_t i = 0; i < length; ++i) + { + for ( int d = 0; d < dim; d++ ) + { + cont_grad[dim*i+d] = cont_grad[dim*i+d] / area[i]; + } + } + } +} + +//----------------------------------------------------------------------------------- + +void +LevelSet::compute_nodal_bbox( const stk::mesh::Selector & selector, + BoundingBox & node_bbox, + const Vector3d & displacement ) const +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::compute_nodal_bbox( BoundingBox & node_bboxes, const double & deltaTime ) const"); /* %TRACE% */ + + // find the local nodal bounding box + + const FieldRef xField = get_coordinates_field(); + const FieldRef dField = get_distance_field(); + + const stk::mesh::Selector active_field_selector = aux_meta().active_not_ghost_selector() & selector & stk::mesh::selectField(dField); + stk::mesh::BucketVector const& buckets = mesh().get_buckets( stk::topology::NODE_RANK, active_field_selector); + + for ( auto && bucket : buckets ) + { + const stk::mesh::Bucket & b = *bucket; + + const size_t length = b.size(); + + double *x = field_data(xField, b); + + for (size_t i = 0; i < length; ++i) + { + + Vector3d x_bw(Vector3d::ZERO); + for ( unsigned dim = 0; dim < spatial_dimension; ++dim ) + { + int index = i*spatial_dimension+dim; + x_bw[dim] = x[index] - displacement[dim]; + } + + // incrementally size bounding box + node_bbox.accommodate( x_bw ); + } + } // end bucket loop +} + +//----------------------------------------------------------------------------------- + +void +LevelSet::prepare_to_compute_distance( const double & deltaTime, const stk::mesh::Selector & selector ) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::get_nonlocal_facets( const double & deltaTime )"); /* %TRACE% */ + + // Get all of the facets that are within our processor's bounding box + // To do this, see if any local facets lie in the nodal bounding box + // of another proc. if so, send them a copy of those facets + + // First, find the bounding box for each proc that contains all of the + // nodes on that proc plus the narrow_band size + + BoundingBox node_bbox; + const Vector3d displacement = deltaTime * get_extension_velocity(); + compute_nodal_bbox( selector, node_bbox, displacement ); + + facets_old->prepare_to_compute(node_bbox, my_narrow_band_size); +} + +//----------------------------------------------------------------------------------- + +void +LevelSet::compute_distance_semilagrangian( const double & deltaTime, const stk::mesh::Selector & selector ) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::compute_distance_semilagrangian( const double & deltaTime ) const"); /* %TRACE% */ + + const double h_avg = compute_average_edge_length(); + + const FieldRef xField = get_coordinates_field(); + const FieldRef dField = get_distance_field(); + const Vector3d extv = get_extension_velocity(); + + const stk::mesh::Selector active_field_selector = aux_meta().active_not_ghost_selector() & selector & stk::mesh::selectField(dField); + stk::mesh::BucketVector const& buckets = mesh().get_buckets(stk::topology::NODE_RANK, active_field_selector); + + for ( auto && bucket : buckets ) + { + const stk::mesh::Bucket & b = *bucket; + + const size_t length = b.size(); + + double *d = field_data( dField , b); + double *x = field_data( xField , b); + + // Handle special case of ( deltaTime == 0. ) so that we + // can handle situation where velocity is not defined at all + // nodes where distance is defined. (This is currently a requirement + // for regular semilagrangian advancement). + if ( deltaTime == 0. ) + { + for (size_t i = 0; i < length; ++i) + { + Vector3d x_node(Vector3d::ZERO); + for ( unsigned dim = 0; dim < spatial_dimension; ++dim ) + { + int index = i*spatial_dimension+dim; + x_node[dim] = x[index]; + } + + int previous_sign = LevelSet::sign(d[i]); + // If this is too large, then sharp edges can propagate incorrect signs + // (even through walls, etc). + // If this is too small, then a phase can't disappear because the sign + // preservation will prevent it even if the subelement contouring process + // neglects it. So this should be slightly larger than the tolerance in + // compute_subelement_decomposition. + bool enforce_sign = (std::abs(d[i]) > 5.e-4*h_avg); + + d[i] = distance( x_node, previous_sign, enforce_sign ); + } + } + else + { + for (size_t i = 0; i < length; ++i) + { + Vector3d x_bw(Vector3d::ZERO); + for ( unsigned dim = 0; dim < spatial_dimension; ++dim ) + { + int index = i*spatial_dimension+dim; + x_bw[dim] = x[index] - extv[dim] * deltaTime; + } + + int previous_sign = LevelSet::sign(d[i]); + + d[i] = distance( x_bw, previous_sign, false ); + } + } + } // end bucket loop +} + +//----------------------------------------------------------------------------------- + +void +LevelSet::compute_distance( stk::mesh::Entity n, + const double & deltaTime ) const +{ /* %TRACE% */ /* %TRACE% */ + + // use the facet cell array to compute the distance to a node n + + const FieldRef xField = get_coordinates_field(); + const FieldRef dField = get_distance_field(); + const Vector3d extv = get_extension_velocity(); + + double *x = field_data( xField , n); + double *d = field_data( dField , n); + + // Handle special case of ( deltaTime == 0. ) so that we + // can handle situation where velocity is not defined at all + // nodes where distance is defined. (This is currently a requirement + // for regular semilagrangian advancement). + if ( deltaTime == 0. ) + { + Vector3d x_node(Vector3d::ZERO); + for ( unsigned dim = 0; dim < spatial_dimension; ++dim ) + { + x_node[dim] = x[dim]; + } + + int previous_sign = LevelSet::sign(*d); + *d = distance( x_node, previous_sign, true ); + } + else + { + Vector3d x_bw(Vector3d::ZERO); + for ( unsigned dim = 0; dim < spatial_dimension; ++dim ) + { + x_bw[dim] = x[dim] - extv[dim] * deltaTime; + } + + int previous_sign = LevelSet::sign(*d); + *d = distance( x_bw, previous_sign, false ); + } +} + +//----------------------------------------------------------------------------------- + +double +LevelSet::distance( const Vector3d & x, + const int previous_sign, + const bool enforce_sign ) const +{ /* %TRACE% */ /* %TRACE% */ + + if (enforce_sign) + { + return previous_sign * facets_old->point_unsigned_distance(x, my_narrow_band_size, my_narrow_band_size); + } + return facets_old->truncated_point_signed_distance(x, my_narrow_band_size, previous_sign*my_narrow_band_size); +} + +//----------------------------------------------------------------------------------- + +void +LevelSet::snap_to_mesh() const +{ /* %TRACE[ON]% */ Trace trace__("LevelSet::snap_to_mesh()"); /* %TRACE% */ + const double tol = 1.0e-2; + + // Remove sliver subelements by setting nodal values near zero to zero exactly. + // This should probably be an edge-based check. But this poses a problem for higher order + // elements, which we are going to decompose into lower order elements. So we make it + // simpler by compare ALL pairs of nodes within the element. If the crossing between any + // pair of nodes is is within a relative distance of tol, the distance at the nearest node + // is set to zero. + + // This seems like a great way to consistently handle degeneracies. + // One problem, however, is this only handles the near zero's on the original elements. + // We will generate others as we decompose into non-conformal subelements. This won't fix + // those degenerate situations. + + std::vector dist; + + const FieldRef dField = get_distance_field(); + + const stk::mesh::Selector active_field_selector = stk::mesh::selectField(dField) & aux_meta().active_locally_owned_selector(); + stk::mesh::BucketVector const& buckets = mesh().get_buckets(stk::topology::ELEMENT_RANK, active_field_selector); + + for ( auto && bucket : buckets ) + { + const stk::mesh::Bucket & b = *bucket; + + const stk::topology dist_topology = MasterElementDeterminer::get_field_topology(b, dField); + const int npe_dist = dist_topology.num_nodes(); + dist.resize( npe_dist ); + + const size_t length = b.size(); + for (size_t i = 0; i < length; ++i) + { + stk::mesh::Entity elem = b[i]; + + if (!elem_on_interface(elem)) continue; + + const stk::mesh::Entity* elem_nodes = mesh().begin_nodes(elem); + + for ( int n = 0; n < npe_dist; ++n ) + { + dist[n] = *field_data(dField, elem_nodes[n]); + } + + for ( int n = 0; n < npe_dist; ++n ) + { + for ( int np = n+1; np < npe_dist; ++np ) + { + if (sign_change(dist[n],dist[np])) + { + const double d0 = std::fabs(dist[n]); + const double d1 = std::fabs(dist[np]); + if (d0 < d1) + { + if (d0 / (d0+d1) < tol) + { + *field_data(dField, elem_nodes[n]) = 0.0; + } + } + else + { + if (d1 / (d1+d0) < tol) + { + *field_data(dField, elem_nodes[np]) = 0.0; + } + } + } + } + } + } + } +} + +//----------------------------------------------------------------------------------- + +bool +LevelSet::remove_wall_features() const +{ /* %TRACE[ON]% */ Trace trace__("LevelSet::remove_wall_features()"); /* %TRACE% */ + + std::vector dist; + std::vector coords; + int small_feature_removed = false; + + const FieldRef dField = get_distance_field(); + const FieldRef coordinates_field = get_coordinates_field(); + + stk::mesh::Selector surface_selector = selectUnion(my_surface_parts); + + const stk::mesh::Selector active_field_selector = stk::mesh::selectField(dField) & aux_meta().active_locally_owned_selector() & surface_selector; + stk::mesh::BucketVector const& buckets = mesh().get_buckets(meta().side_rank(), active_field_selector); + + for ( auto && bucket : buckets ) + { + const stk::mesh::Bucket & b = *bucket; + + const stk::topology dist_topology = MasterElementDeterminer::get_field_topology(b, dField); + const int npe_dist = dist_topology.num_nodes(); + dist.resize( npe_dist ); + coords.resize( npe_dist ); + + const size_t length = b.size(); + for (size_t i = 0; i < length; ++i) + { + stk::mesh::Entity side = b[i]; + + const stk::mesh::Entity* side_nodes = mesh().begin_nodes(side); + + for ( int n = 0; n < npe_dist; ++n ) + { + dist[n] = *field_data(dField, side_nodes[n]); + + double *coord_data = field_data(coordinates_field,side_nodes[n]); + coords[n] = coord_data; + } + + for ( int n = 0; n < npe_dist; ++n ) + { + const stk::mesh::Entity *elems = mesh().begin_elements(side); //get the element + const int num_elems = mesh().num_elements(side); + + for (int l = 0; l < num_elems; ++l ) + { + const stk::mesh::Entity elem = elems[l]; + + //make sure we have the right element (active) and has the distance function + //at every point in the element (to take gradient) + + if(!aux_meta().active_locally_owned_selector()(mesh().bucket(elem))) continue; + if(!elem_has_field_data(dField, elem)) continue; + + //const double elem_len = calc_elem_len_normal(elem, side, coordinates_field); + + if(std::fabs(dist[n]) > my_max_feature_size) continue; + + ContourElement ls_elem( mesh(), elem, coordinates_field, dField ); + const Vector3d p_coords(1/3., 1/3., 1/3.); + const Vector3d grad_dist_vec = ls_elem.distance_gradient(p_coords); + + Vector3d face_normal; + + //assume linear tet or tri elements! + if(spatial_dimension == 2) + { + face_normal = {-coords[1][1]+coords[0][1], coords[1][0]-coords[0][0], 0}; //simple 90 deg rotation + const stk::mesh::Entity* elem_nodes = mesh().begin_nodes(elem); + + int num_elem_nodes = mesh().num_nodes(elem); + + for (int j = 0; j < num_elem_nodes; ++j) //find node in elem not part of side part, confirm normal points into elem + { + if(elem_nodes[j] != side_nodes[0] && elem_nodes[j] != side_nodes[spatial_dimension-1]) + { + double *coord = field_data(coordinates_field,elem_nodes[j]); + const Vector3d vec_check(coord[0]-coords[0][0], coord[1]-coords[0][1], 0); + + if(Dot(face_normal, vec_check) < 0) + { + face_normal *= -1; + break; + } + } + } + } + else + { + const Vector3d x1 (coords[1][0]-coords[0][0], coords[1][1]-coords[0][1],coords[1][2]-coords[0][2]); + const Vector3d x2 (coords[2][0]-coords[0][0], coords[2][1]-coords[0][1],coords[2][2]-coords[0][2]); + face_normal = -1.0*Cross(x1,x2); + } + + face_normal.unitize(); + const double eta = -dist[n]/(Dot(grad_dist_vec, face_normal)); + + if(eta < my_max_feature_size && eta > 0) + { + *field_data(dField, side_nodes[n]) = -1.0 * dist[n]; + small_feature_removed = true; + } + } + } + } + } + + int global_small_feature_removed = false; + stk::all_reduce_sum(mesh().parallel(), &small_feature_removed, &global_small_feature_removed, 1); + + if (global_small_feature_removed) + { + stk::mesh::communicate_field_data(mesh(), {&dField.field()}); + return true; + } + + return false; +} +//-------------------------------------------------------------------------------- + +bool +LevelSet::simple_remove_wall_features() const +{ /* %TRACE[ON]% */ Trace trace__("LevelSet::remove_wall_features()"); /* %TRACE% */ + + int small_feature_removed = false; + const FieldRef dField = get_distance_field(); + + stk::mesh::Selector surface_selector = selectUnion(my_surface_parts); + + const stk::mesh::Selector active_field_selector = stk::mesh::selectField(dField) & aux_meta().active_part() & surface_selector; + stk::mesh::BucketVector const& buckets = mesh().get_buckets(stk::topology::NODE_RANK, active_field_selector); + + for ( auto && bucket : buckets ) + { + const stk::mesh::Bucket & b = *bucket; + const size_t length = b.size(); + for (size_t i = 0; i < length; ++i) + { + stk::mesh::Entity node = b[i]; + double & dist = *field_data(dField, node); + + if(std::fabs(dist) < my_max_feature_size) + { + dist*=-1; + small_feature_removed = true; + } + } + } + + return small_feature_removed; +} +//-------------------------------------------------------------------------------- + +void +LevelSet::set_surface_parts_vector() +{ + Phase_Support my_phase_support = Phase_Support::get(meta()); + + std::vector conformal_parts = my_phase_support.get_conformal_parts(); + + krinolog << "Removing small features less than " << my_max_feature_size << " on surfaces: "; + for (auto && mypart : conformal_parts) + { + if(mypart->primary_entity_rank() == meta().side_rank() && !my_phase_support.is_interface(mypart)) + { + my_surface_parts.push_back(mypart); + krinolog << mypart->name() << " "; + } + } + + krinolog << stk::diag::dendl; +} + +//-------------------------------------------------------------------------------- + +bool +LevelSet::elem_has_field_data(const FieldRef &myField, const stk::mesh::Entity &elem) const +{ + const stk::mesh::Entity* elem_nodes = mesh().begin_nodes(elem); + int num_nodes = mesh().num_nodes(elem); + + for (int k = 0; k < num_nodes; ++k) + { + if(!has_field_data(myField, elem_nodes[k])) return false; + } + + return true; +} +//-------------------------------------------------------------------------------- + +bool +LevelSet::elem_on_interface(stk::mesh::Entity e) const +{ /* %TRACE% */ /* %TRACE% */ + + const FieldRef isoField = get_isovar_field(); + + const unsigned nnodes = mesh().num_nodes(e); + ThrowAssert( 0 < nnodes ); + const stk::mesh::Entity* nodes = mesh().begin_nodes(e); + + // guilty till proven innocent here + bool on_interface = false; + bool have_crossing = false; + + double *d = field_data(isoField, nodes[0]); + double first_value = *d - my_threshold; + bool inside_narrow_band = fabs(first_value) < 0.5*my_narrow_band_size; + + for (unsigned i = 1; i < nnodes; ++i ) + { + d = field_data(isoField, nodes[i]); + if ( NULL == d ) continue; // account for lower order interpolation + double value = *d - my_threshold; + + have_crossing |= sign_change(value, first_value); + inside_narrow_band |= (fabs(value) < 0.5*my_narrow_band_size); + } + + // It is the user's job to make sure that the narrow band is sufficiently + // large that we don't to test if this crossing is within the narrow band + if ( have_crossing ) + on_interface = true; + + return on_interface; +} + +//-------------------------------------------------------------------------------- +void +LevelSet::compute_sizes( double & area, double & neg_vol, double & pos_vol, const double iso_val ) +{ /* %TRACE[ON]% */ /* %TRACE% */ + + // initialize + area = 0.0; + neg_vol = 0.0; + pos_vol = 0.0; + + const double h_avg = compute_average_edge_length(); + + sierra::ArrayContainer intg_pt_locations; + sierra::ArrayContainer intg_weights; + sierra::ArrayContainer determinants; + + const FieldRef xField = get_coordinates_field(); + const FieldRef isoField = get_isovar_field(); + //const Vector3d extv = get_extension_velocity(); + + stk::mesh::Selector active_field_selector = stk::mesh::selectField(isoField) & aux_meta().active_locally_owned_selector(); + std::vector< stk::mesh::Entity> objs; + stk::mesh::get_selected_entities(active_field_selector, mesh().buckets( stk::topology::ELEMENT_RANK ), objs); + + for ( auto && elem : objs ) + { + // create element that is decomposed into subelements + ContourElement ls_elem( mesh(), elem, xField, isoField, iso_val ); + + ls_elem.compute_subelement_decomposition(h_avg); + + // get integration point locations, weights, and determinants for surface + int num_intg_pts = ls_elem.gather_intg_pts( 0, // interface + intg_pt_locations,// gauss pt locations + intg_weights, // integration wts at gauss pts + determinants, // determinant at gauss pts + true ); // map_to_real_coords + for ( int ip = 0; ip < num_intg_pts; ++ip ) + { + area += intg_weights(ip) * determinants(ip); + } + + // get integration point locations, weights, and determinants for negative volume + num_intg_pts = ls_elem.gather_intg_pts( -1, // negative side of interface + intg_pt_locations, // gauss pt locations + intg_weights, // integration wts at gauss pts + determinants, // determinant at gauss pts + true ); // map_to_real_coords + for ( int ip = 0; ip < num_intg_pts; ++ip ) + { + neg_vol += intg_weights(ip) * determinants(ip); + } + + // get integration point locations, weights, and determinants for positive volume + num_intg_pts = ls_elem.gather_intg_pts( 1, // positive side of interface + intg_pt_locations, // gauss pt locations + intg_weights, // integration wts at gauss pts + determinants, // determinant at gauss pts + true ); // map_to_real_coords + for ( int ip = 0; ip < num_intg_pts; ++ip ) + { + pos_vol += intg_weights(ip) * determinants(ip); + } + } + + // communicate global sums + const int vec_length = 3; + std::vector local_sum( vec_length ); + std::vector global_sum( vec_length ); + local_sum[0] = area; + local_sum[1] = neg_vol; + local_sum[2] = pos_vol; + + stk::all_reduce_sum(mesh().parallel(), &local_sum[0], &global_sum[0], vec_length); + + area = global_sum[0]; + neg_vol = global_sum[1]; + pos_vol = global_sum[2]; + +} +//-------------------------------------------------------------------------------- +double +LevelSet::gradient_magnitude_error(void) +{ /* %TRACE[ON]% */ /* %TRACE% */ + + double area = 0., sum_L2 = 0., global_L2 = 0., local_Loo = 0., global_Loo = 0.; + + sierra::ArrayContainer intg_pt_locations; + sierra::ArrayContainer intg_weights; + sierra::ArrayContainer determinants; + sierra::ArrayContainer grad_dist; + + const double h_avg = compute_average_edge_length(); + + const FieldRef xField = get_coordinates_field(); + const FieldRef isoField = get_isovar_field(); + //const Vector3d extv = get_extension_velocity(); + + stk::mesh::Selector active_field_selector = stk::mesh::selectField(isoField) & aux_meta().active_locally_owned_selector(); + std::vector< stk::mesh::Entity> objs; + stk::mesh::get_selected_entities( active_field_selector, mesh().buckets( stk::topology::ELEMENT_RANK ), objs ); + + for ( auto && elem : objs ) + { + // create element that is decomposed into subelements + ContourElement ls_elem( mesh(), elem, xField, isoField ); + + ls_elem.compute_subelement_decomposition(h_avg); + + // get integration point locations, weights, and determinants for surface + int num_intg_pts = ls_elem.gather_intg_pts( 0, // interface + intg_pt_locations,// gauss pt locations + intg_weights, // integration wts at gauss pts + determinants, // determinant at gauss pts + true ); // map_to_real_coords + + ls_elem.compute_distance_gradient( intg_pt_locations, grad_dist ); + for ( int ip = 0; ip < num_intg_pts; ++ip ) + { + double mag_grad_phi = 0.; + for ( unsigned dim = 0; dim < spatial_dimension; ++dim ) mag_grad_phi += grad_dist(dim,ip) * grad_dist(dim,ip); + mag_grad_phi = sqrt(mag_grad_phi); + + sum_L2 += (mag_grad_phi - 1.) * (mag_grad_phi - 1.) * intg_weights(ip) * determinants(ip); + area += intg_weights(ip) * determinants(ip); + + if ( fabs(mag_grad_phi - 1.) > local_Loo ) local_Loo = fabs(mag_grad_phi - 1.); + } + } + + // communicate global norms + const int vec_length = 2; + std::vector local_sum( vec_length ); + std::vector global_sum( vec_length ); + local_sum[0] = sum_L2; + local_sum[1] = area; + + stk::all_reduce_sum(mesh().parallel(), &local_sum[0], &global_sum[0], vec_length); + stk::all_reduce_max(mesh().parallel(), &local_Loo, &global_Loo, 1); + + if ( global_sum[1] > 0. ) + { + global_L2 = global_sum[0] / global_sum[1]; + } + + krinolog << "Gradient norm error for " << name() << ": L2 = " << global_L2 << ", Loo = " << global_Loo << stk::diag::dendl; + + // L2 is the standard now, maybe Loo would be better? + return global_L2; +} +//-------------------------------------------------------------------------------- +double +LevelSet::compute_average_edge_length() const +{ /* %TRACE[ON]% */ /* %TRACE% */ + + double sum_avg_edge_lengths = 0.0; + int num_elems = 0; + + const FieldRef xField = get_coordinates_field(); + const FieldRef isoField = get_isovar_field(); + + stk::mesh::Selector active_field_selector = stk::mesh::selectField(isoField) & aux_meta().active_locally_owned_selector(); + std::vector< stk::mesh::Entity> objs; + stk::mesh::get_selected_entities( active_field_selector, mesh().buckets( stk::topology::ELEMENT_RANK ), objs ); + + for ( auto && elem : objs ) + { + ++num_elems; + + // create element + ContourElement ls_elem( mesh(), elem, xField, isoField ); + + sum_avg_edge_lengths += ls_elem.average_edge_length(); + } + + // communicate global sums + const int vec_length = 2; + std::vector local_sum( vec_length ); + std::vector global_sum( vec_length ); + local_sum[0] = sum_avg_edge_lengths; + local_sum[1] = 1.0*num_elems; + + stk::all_reduce_sum(mesh().parallel(), &local_sum[0], &global_sum[0], vec_length); + + const double h_avg = ( global_sum[1] != 0.0 ) ? global_sum[0]/global_sum[1] : 0.0; + + return h_avg; +} + +//-------------------------------------------------------------------------------- + +void +LevelSet::build_facets_locally(const stk::mesh::Selector & selector) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::build_facets_locally(void)"); /* %TRACE% */ + + // clear vector of facets + facets->clear(); + + const double h_avg = compute_average_edge_length(); + + const FieldRef xField = get_coordinates_field(); + const FieldRef isoField = get_isovar_field(); + + stk::mesh::Selector active_field_selector = selector & stk::mesh::selectField(isoField) & aux_meta().active_locally_owned_selector(); + std::vector< stk::mesh::Entity> objs; + stk::mesh::get_selected_entities( active_field_selector, mesh().buckets( stk::topology::ELEMENT_RANK ), objs ); + + for ( auto && elem : objs ) + { + // create element that is decomposed into subelements + ContourElement ls_elem( mesh(), elem, xField, isoField ); + ls_elem.compute_subelement_decomposition(h_avg); + + ls_elem.build_subelement_facets( *facets ); + } +} + +LevelSet & +LevelSet::build( + stk::mesh::MetaData & in_meta, + const std::string & ls_name, + stk::diag::Timer & parent_timer ) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::build(stk::mesh::MetaData & in_meta, const std::string & ls_name, stk::diag::Timer & parent_timer)"); /* %TRACE% */ + LevelSetManager & region_ls = LevelSetManager::get(in_meta); + + ThrowRequire(!region_ls.has_levelSet(ls_name)); + LevelSet * ls = new LevelSet(in_meta, ls_name, parent_timer); + region_ls.add(ls); + return *ls; +} + +//-------------------------------------------------------------------------------- +LevelSet::LevelSet( + stk::mesh::MetaData & in_meta, + const std::string & in_name, + stk::diag::Timer & parent_timer ) : + my_meta(in_meta), + my_aux_meta(AuxMetaData::get(in_meta)), + my_identifier(LevelSet::get_identifier(in_name)), + my_name(LevelSet::get_name(my_identifier)), + my_parent_timer(parent_timer), + my_timer("LevelSet", parent_timer), + spatial_dimension(in_meta.spatial_dimension()), + my_narrow_band_multiplier(0.0), + my_narrow_band_size(0.0), + my_max_feature_size(-1.0), + my_ic_offset(0.0), + my_ic_scale(1.0), + my_perform_initial_redistance(false), + my_keep_IC_surfaces(false), + my_threshold(0.0), + my_redistance_method(CLOSEST_POINT), + epsilon(1.0e-16), + trackIsoSurface(false), + my_facetFileIndex(1), + my_initial_neg_vol(0.0), + my_needs_reinitialize_every_step(false) +{ /* %TRACE[ON]% */ Trace trace__("krino::LevelSet::LevelSet(stk::mesh::MetaData & in_meta, const std::string & ls_name, stk::diag::Timer & parent_timer)"); /* %TRACE% */ + my_coordinates_field = get_current_coordinates(in_meta); + + // default names for distance and velocity + // line commands are available for overriding these names + const std::string distName = "D_" + name(); + set_distance_name(distName); +} + +LevelSet::~LevelSet() +{ +} + +//----------------------------------------------------------------------------------- +void +LevelSet::gather_nodal_field( + const stk::mesh::BulkData& stk_mesh, + stk::mesh::Entity obj, + const FieldRef & field, + double * gathered_field_data ) +{ /* %TRACE% */ /* %TRACE% */ + + int ncomp_field = field.length(); + + // Gather obj's nodal field into a single flat array + // of dimension (ncomp_field,num_nodes) + + int j = 0; + const unsigned num_nodes = stk_mesh.num_nodes(obj); + const stk::mesh::Entity* nodes = stk_mesh.begin_nodes(obj); + for (unsigned node_index=0; node_index(field, node); + for ( int i = 0; i < ncomp_field; ++i ) + { + gathered_field_data[j++] = var[i]; + } + } + + ThrowAssert( (unsigned)j == ncomp_field * stk_mesh.num_nodes(obj)); +} +//-------------------------------------------------------------------------------- +LevelSetManager & +LevelSetManager::get(stk::mesh::MetaData & meta) +{ + LevelSetManager * mgr = const_cast(meta.get_attribute()); + if (nullptr == mgr) + { + mgr = new LevelSetManager; + meta.declare_attribute_with_delete(mgr); + } + return *mgr; +} +//-------------------------------------------------------------------------------- +LevelSetManager & +LevelSetManager::get(const stk::mesh::MetaData & meta) +{ + LevelSetManager * mgr = const_cast(meta.get_attribute()); + ThrowRequireMsg(nullptr != mgr, "No LevelSetManager found for MetaData."); + return *mgr; +} +//-------------------------------------------------------------------------------- +bool LevelSetManager::has_levelSet(const std::string & ls_name) const +{ + for (auto&& ls : my_level_sets) + { + if (ls->name() == ls_name || ls->get_composite_name() == ls_name) + { + return true; + } + } + return false; +} +//-------------------------------------------------------------------------------- +std::string +print_sizes(const LevelSet & ls) +{ /* %TRACE[ON]% */ Trace trace__("krino::print_sizes(const LevelSet & levelSet)"); /* %TRACE% */ + + // + // find sizes of facets stored in vector of facet descriptions + // + + const auto & ls_surfaces = ls.get_facets().get_facets(); + unsigned local_facet_num = ls_surfaces.size(); + unsigned global_facet_num = 0; + stk::all_reduce_sum(ls.mesh().parallel(), &local_facet_num , &global_facet_num, 1); + + // iterate local facets and calc area + + double local_facets_totalArea = 0.0; // total surface area of interface from facets + double local_facets_maxArea = -1.0; // area of largest facet on interface + double local_facets_minArea = std::numeric_limits::max(); // area of smallest facet on interface + + // loop over facets + for ( auto&& surface : ls_surfaces ) + { + double area = surface->facet_area(); + local_facets_totalArea += area; + + local_facets_maxArea = std::min(area, local_facets_maxArea); + local_facets_minArea = std::max(area, local_facets_maxArea); + } + + double global_facets_totalArea = 0.0; + double global_facets_maxArea = 0.0; + double global_facets_minArea = 0.0; + + stk::all_reduce_min(ls.mesh().parallel(), &local_facets_minArea, &global_facets_minArea, 1); + stk::all_reduce_max(ls.mesh().parallel(), &local_facets_maxArea, &global_facets_maxArea, 1); + stk::all_reduce_sum(ls.mesh().parallel(), &local_facets_totalArea, &global_facets_totalArea, 1); + + std::ostringstream out; + + // facet info + out << "P" << ls.mesh().parallel_rank() << ": facets for level set '" << ls.name() << "' " << std::endl ; + out << "\t " << "Global sizes: { " << global_facet_num << " facets on }" << std::endl; + out << "\t " << "Local sizes: { " << local_facet_num << " facets on }" << std::endl; + out << "\t Global areas: { Min = " << global_facets_minArea << ", Max = " << global_facets_maxArea + << ", Total = " << global_facets_totalArea << " }" << std::endl; + return out.str(); +} +//-------------------------------------------------------------------------------- + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_LevelSet.hpp b/packages/krino/krino/krino_lib/Akri_LevelSet.hpp new file mode 100644 index 000000000000..609e13728396 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_LevelSet.hpp @@ -0,0 +1,339 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_LevelSet_h +#define Akri_LevelSet_h + +/** + + Create one of these for every interface you want to capture. + + */ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace stk { namespace mesh { class BulkData; } } +namespace stk { namespace mesh { class MetaData; } } +namespace sierra { namespace Sctl { class Event; } } +namespace krino { class AuxMetaData; } +namespace krino { class IC_Alg; } +namespace krino { class ParallelErrorMessage; } + +namespace krino { + +enum Redistance_Method +{ + CLOSEST_POINT=0, + FAST_MARCHING, + MAX_REDISTANCE_METHOD_TYPE +}; + +/// Return true if field-data exists for the specified meshobj and field. +bool all_nodes_have_field_data(const stk::mesh::BulkData& stk_bulk, stk::mesh::Entity entity, const stk::mesh::FieldBase& field); + +class LevelSet { +friend class LevelSet_Size; +public: + stk::mesh::MetaData & meta(); + const stk::mesh::MetaData & meta() const; + stk::mesh::BulkData & mesh(); + const stk::mesh::BulkData & mesh() const; + AuxMetaData & aux_meta(); + const AuxMetaData & aux_meta() const; + + const std::string & name() const { return my_name; } + LevelSet_Identifier get_identifier() const {return my_identifier; } + stk::diag::Timer & get_timer() const { return my_timer; } + stk::diag::Timer & get_parent_timer() const { return my_parent_timer; } + + // finalize setup + static void setup(stk::mesh::MetaData & meta); + static void post_commit_setup(stk::mesh::MetaData & meta); + virtual void setup(); + + static void set_current_coordinates(stk::mesh::MetaData & meta, const FieldRef ref); + static FieldRef get_current_coordinates(stk::mesh::MetaData & meta); + + void register_fields(); + + void advance_semilagrangian(const double deltaTime); + + static void gather_nodal_field( + const stk::mesh::BulkData& stk_mesh, + stk::mesh::Entity obj, + const FieldRef & field_ref, + double * field); + + double compute_average_edge_length() const; + + void build_facets_locally(const stk::mesh::Selector & selector); + + void compute_sizes( double & area, double & neg_vol, double & pos_vol, const double distance = 0.0 ); + double gradient_magnitude_error(); + void compute_continuous_gradient() const; + + void compute_distance( stk::mesh::Entity n, + const double & deltaTime ) const; + void estimate_error(); + + // hack to dump facet list to exoii databse. + void facets_exoii(); + void facets_exoii(Faceted_Surface & cs); + + bool elem_on_interface(stk::mesh::Entity e) const; + + void snap_to_mesh() const; + bool remove_wall_features() const; + bool simple_remove_wall_features() const; + bool elem_has_field_data(const FieldRef &myField, const stk::mesh::Entity &elem) const; + double calc_elem_len_normal(const stk::mesh::Entity &elem, const stk::mesh::Entity &side, const FieldRef &coordinates_field) const; + + + //-------------------------------------------------------------------------------- + // queries + //-------------------------------------------------------------------------------- + + const std::string & get_distance_name() const { return my_distance_name; } + void set_distance_name( const std::string & distance_name ) { my_distance_name = distance_name; } + + const FieldRef & get_distance_field() const { return my_distance_field; } + void set_distance_field( const FieldRef & ref ) { my_distance_field = ref; } + + const FieldRef & get_old_distance_field() const { return my_old_distance_field; } + void set_old_distance_field( const FieldRef & ref ) { my_old_distance_field = ref; } + + void set_extension_velocity( const Vector3d & extension_velocity ) { my_extension_velocity = extension_velocity; } + const Vector3d & get_extension_velocity() const { return my_extension_velocity; } + + const FieldRef & get_coordinates_field() const { return my_coordinates_field; } + + const std::string & get_isovar_name() const { return my_isovar_name; } + + const std::string & get_composite_name() const { return my_composite_name; } + void set_composite_name( const std::string & composite_name ) { + my_composite_name = composite_name; + std::transform(my_composite_name.begin(), my_composite_name.end(), my_composite_name.begin(), ::toupper); + } + + const FieldRef & get_isovar_field() const { return my_isovar_field; } + void set_isovar_field( const FieldRef & ref ) { my_isovar_field = ref; } + + double get_time_of_arrival_speed(stk::mesh::Entity elem, ParallelErrorMessage& err) const; + + void set_isovar(const std::string & isovar_name, const double isoval) { my_isovar_name = isovar_name; my_threshold = isoval; trackIsoSurface = true; } + const double & get_isoval() const { return my_threshold; } + + bool get_reinitialize_every_step() const { return my_needs_reinitialize_every_step; } + void set_reinitialize_every_step(const bool reinit) { set_keep_IC_surfaces(); my_needs_reinitialize_every_step = true; } + + Redistance_Method get_redistance_method() const { return my_redistance_method; } + void set_redistance_method( const Redistance_Method type ) { my_redistance_method = type; } + void set_time_of_arrival_element_speed_field_name( const std::string & time_of_arrival_speed_field_name) { my_time_of_arrival_element_speed_field_name = time_of_arrival_speed_field_name; } + void set_time_of_arrival_block_speed(const std::string & blockName, const double blockSpeed); + Faceted_Surface & get_facets() { return *facets; } + const Faceted_Surface & get_facets() const { return *facets; } + + static std::vector the_levelSet_names; + static LevelSet_Identifier get_identifier(const std::string & name); + static std::string & get_name(const LevelSet_Identifier identifier) { ThrowAssert(identifier.get() < the_levelSet_names.size()); return the_levelSet_names[identifier.get()]; } + + void narrow_band_multiplier( double multiplier ) { my_narrow_band_multiplier = multiplier; } + const double & narrow_band_size() const { return my_narrow_band_size; } + void narrow_band_size( double size ) { my_narrow_band_size = size; } // publicly deprecated, make private + + void max_feature_size( double size ) { my_max_feature_size= size; } + void use_simple_remove_feature( bool is_simple ) {my_use_simple_remove_feature = is_simple; } + + double max_feature_size() { return my_max_feature_size; } + bool use_simple_remove_feature() { return my_use_simple_remove_feature; } + + void set_surface_parts_vector(); + + void set_ic_offset (const double offset) { + my_ic_offset = offset; + } + void set_ic_scale (const double scale) { + my_ic_scale = scale; + } + void perform_initial_redistance(const bool flag) { + my_perform_initial_redistance = flag; + } + + void set_keep_IC_surfaces() { my_keep_IC_surfaces = true; } + bool get_keep_IC_surfaces() const { return my_keep_IC_surfaces; } + + static bool sign_change( double f1, double f2 ) { + return ( (f1 < 0.) ? (f2 >= 0.) : (f2 < 0.) ); // GOMA sign convention + //return ( (f1 > 0.) ? (f2 <= 0.) : (f2 > 0.) ); // Marching cubes sign convention + } + + static int sign( double f ) { + return ( (f < 0.) ? -1 : 1 ); // GOMA sign convention + //return ( (f > 0.) ? 1 : -1 ); // Marching cubes sign convention + } + + const std::vector & get_compute_surface_distance_parts() const { return my_compute_surface_distance_parts; } + std::vector & get_compute_surface_distance_parts() { return my_compute_surface_distance_parts; } + void set_surface_distance(std::vector surfaces, const double in_distance); + void compute_surface_distance(const double narrowBandSize=0.0, const double farFieldValue=0.0); + static void initialize(stk::mesh::MetaData & meta, const bool requires_additional_initialization); + void initialize(const double time = 0.0, const bool requires_additional_initialization = false); + void redistance(); + void redistance(const stk::mesh::Selector & selector); + void fast_marching_redistance(const stk::mesh::Selector & selector, const bool compute_time_of_arrival = false); + + void set_initial_volume(const double v) { my_initial_neg_vol = v; } + double constrained_redistance(const bool use_initial_vol = false); + + void compute_nodal_bbox( const stk::mesh::Selector & selector, + BoundingBox & node_bbox, + const Vector3d & displacement = Vector3d::ZERO ) const; + + double find_redistance_correction( const double start_area, + const double start_neg_vol, + const double start_pos_vol, + const int max_iterations = 100, + const double tol = 1.e-6 ); + + bool has_IC_surfaces(); + BoundingBox get_IC_surface_bounding_box(); + IC_Alg& get_IC_alg(); + + static LevelSet & build(stk::mesh::MetaData & in_meta, const std::string & ls_name, stk::diag::Timer & parent_timer); + + virtual ~LevelSet(); + +private: + LevelSet(stk::mesh::MetaData & in_meta, const std::string & in_name, stk::diag::Timer & parent_timer); + +private: + stk::mesh::MetaData & my_meta; + AuxMetaData & my_aux_meta; + const LevelSet_Identifier my_identifier; + const std::string my_name; + mutable stk::diag::Timer my_parent_timer; + mutable stk::diag::Timer my_timer; + +public: + const unsigned spatial_dimension; + +private: + + FieldRef my_coordinates_field; + FieldRef my_distance_field; + FieldRef my_old_distance_field; + FieldRef my_isovar_field; + FieldRef myTimeOfArrivalElementSpeedField; + + std::string my_distance_name; + std::string my_isovar_name; + + std::string my_composite_name; + + double my_narrow_band_multiplier; + double my_narrow_band_size; + + double my_max_feature_size; + bool my_use_simple_remove_feature = false; + + + double my_ic_offset; + double my_ic_scale; + bool my_perform_initial_redistance; + bool my_keep_IC_surfaces; + + double my_threshold; + Redistance_Method my_redistance_method; + std::string my_time_of_arrival_element_speed_field_name; + std::map myTimeOfArrivalBlockSpeedsByName; + std::vector myTimeOfArrivalBlockSpeeds; + + std::unique_ptr my_IC_alg; + + // vector of previous facets + std::unique_ptr facets_old; + + // vector of current facets + std::unique_ptr facets; + + Vector3d my_extension_velocity; + const double epsilon; + + bool trackIsoSurface; + + // used to increment file name for facet exoii database hack + int my_facetFileIndex; + + double my_initial_neg_vol; + + bool my_needs_reinitialize_every_step; + + std::vector my_compute_surface_distance_parts; + std::vector my_surface_parts; + + + void set_distance(const double & distance) const; + void increment_distance(const double increment, const bool enforce_sign = false) const; + void scale_distance(const double scale) const; + void negate_distance() const; + + void time_integrate(const double deltaTime); + + void prepare_to_compute_distance( const double & deltaTime, const stk::mesh::Selector & selector ); + + void compute_distance_semilagrangian(const double & deltaTime, const stk::mesh::Selector & selector ); + + double distance( const Vector3d & x, + const int previous_sign, + const bool enforce_sign ) const; + + bool compute_time_of_arrival() const { return !my_time_of_arrival_element_speed_field_name.empty() || !myTimeOfArrivalBlockSpeeds.empty(); } +}; + +class LevelSetManager { +public: + LevelSetManager() {} + LevelSetManager(LevelSetManager const&) = delete; + LevelSetManager& operator=(LevelSetManager const&) = delete; + + static LevelSetManager & get(stk::mesh::MetaData & meta); + static LevelSetManager & get(const stk::mesh::MetaData & meta); + + int numberLevelSets() const { return my_level_sets.size(); } + LevelSet & levelSet(const unsigned ordinal) const { ThrowAssert(ordinal < my_level_sets.size()); return *my_level_sets[ordinal]; } + bool has_levelSet(const std::string & ls_name) const; + void add(LevelSet * ls) { my_level_sets.emplace_back(ls); } + + std::vector< std::unique_ptr >::const_iterator begin() const { return my_level_sets.begin(); } + std::vector< std::unique_ptr >::const_iterator end() const { return my_level_sets.end(); } + + void set_current_coordinates(FieldRef current_coords) { my_current_coordinates = current_coords; } + FieldRef get_current_coordinates() const { return my_current_coordinates; } +private: + std::vector< std::unique_ptr > my_level_sets; + FieldRef my_current_coordinates; +}; + +std::string print_sizes(const LevelSet & ls); + +} // namespace krino + +#endif // Akri_LevelSet_h diff --git a/packages/krino/krino/krino_lib/Akri_LevelSetInterfaceGeometry.cpp b/packages/krino/krino/krino_lib/Akri_LevelSetInterfaceGeometry.cpp new file mode 100644 index 000000000000..9f2cfdda6005 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_LevelSetInterfaceGeometry.cpp @@ -0,0 +1,931 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino { + +std::unique_ptr create_element_cutter_with_error_handling(const stk::mesh::BulkData & mesh, + stk::mesh::Entity element, + const std::vector & elementParentEdges, + const std::vector & areParentEdgesAreOrientedSameAsElementEdges, + const Phase_Support & phaseSupport, + const std::function &)> & intersectingPlanesDiagonalPicker) +{ + if (krinolog.shouldPrint(LOG_DEBUG)) + krinolog << "Building cutting planes for element global_id=" << mesh.identifier(element) << "\n"; + + stk::topology elementTopology = mesh.bucket(element).topology(); + const MasterElement & masterElement = MasterElementDeterminer::getMasterElement(elementTopology); + const bool oneLSPerPhase = phaseSupport.has_one_levelset_per_phase(); + + std::unique_ptr cutter; + + try + { + cutter = create_element_cutter(oneLSPerPhase, masterElement, elementParentEdges, areParentEdgesAreOrientedSameAsElementEdges, intersectingPlanesDiagonalPicker); + } + catch(const std::exception & err) + { + krinolog << "Error constructing cutting surfaces for Mesh_Element " << mesh.identifier(element) << stk::diag::push << stk::diag::dendl; + for(unsigned i=0; i < elementParentEdges.size(); ++i) + { + krinolog << "Edge " << i; + if(elementParentEdges[i]) { + krinolog << " node ids = "; + for(auto && node : elementParentEdges[i]->get_nodes()) krinolog << mesh.identifier(node) << " "; + krinolog << stk::diag::push << stk::diag::dendl; + krinolog << *elementParentEdges[i] << stk::diag::pop << stk::diag::dendl; + } + else + { + krinolog << " No parent edge." << stk::diag::dendl; + } + } + krinolog << stk::diag::pop << stk::diag::dendl; + krinolog << err.what() << stk::diag::dendl; + throw err; + } + + return cutter; +} + +LevelSetElementCutter::LevelSetElementCutter(const stk::mesh::BulkData & mesh, + stk::mesh::Entity element, + const ParentEdgeMap & parentEdges, + const Phase_Support & phaseSupport, + const std::function &)> & intersectingPlanesDiagonalPicker) +{ + fill_element_parent_edges(mesh, element, parentEdges, myParentEdges, myParentEdgesAreOrientedSameAsElementEdges); + myElementInterfaceCutter = create_element_cutter_with_error_handling(mesh, element, myParentEdges, myParentEdgesAreOrientedSameAsElementEdges, phaseSupport, intersectingPlanesDiagonalPicker); +} + +std::string LevelSetElementCutter::visualize(const stk::mesh::BulkData & mesh) const +{ + std::ostringstream os; + + std::vector interiorIntersections; + myElementInterfaceCutter->fill_interior_intersections(interiorIntersections); + + os << "Interior intersections " << interiorIntersections.size() << ": \n"; + for (auto && intersection : interiorIntersections) + { + os << "Interior intersection at " << intersection.parametricCoords << " with domains { "; + for (int domain : intersection.sortedDomains) os<< domain << " "; + os << "}\n"; + } + + os << "Parent edges: \n"; + for (auto && edge : myParentEdges) + { + os << "Edge with nodes " << mesh.identifier(edge->get_parent_nodes().first) << " and " << mesh.identifier(edge->get_parent_nodes().second) << ":\n"; + if (edge) os << *edge; + } + + os << myElementInterfaceCutter->visualize() << "\n"; + + return os.str(); +} + +std::unique_ptr LevelSetInterfaceGeometry::build_element_cutter(const stk::mesh::BulkData & mesh, + stk::mesh::Entity element, + const std::function &)> & intersectingPlanesDiagonalPicker) const +{ + std::unique_ptr cutter; + cutter.reset( new LevelSetElementCutter(mesh, element, myParentEdges, myPhaseSupport, intersectingPlanesDiagonalPicker) ); + return cutter; +} + +PhaseTag LevelSetInterfaceGeometry::get_starting_phase(const ElementCutter * cutter) const +{ + const LevelSetElementCutter * LSCutter = dynamic_cast(cutter); + ThrowRequire(LSCutter); + return LSCutter->get_starting_phase(myCdfemSupport, myPhaseSupport); +} + +PhaseTag LevelSetElementCutter::get_starting_phase(const CDFEM_Support & cdfemSupport, const Phase_Support & phaseSupport) const +{ + PhaseTag phase; + + // For elements with no interfaces, this will be the uncrossed phase of the mesh element. + // For elements with interface, this is the starting phase that will be inherited by the + // subelements and then incrementally updated as we process the interfaces. + if (cdfemSupport.num_ls_fields() > 1 && Phase_Support::has_one_levelset_per_phase()) + { + int LSphase = myElementInterfaceCutter->get_starting_phase_for_cutting_surfaces(); + if (LSphase == -1) + { + const auto & phasesPresent = get_phases_present_on_edges(myParentEdges); + ThrowRequire(!phasesPresent.empty() && (myElementInterfaceCutter->get_num_cutting_surfaces() > 0 || 1 == phasesPresent.size())); + LSphase = *phasesPresent.begin(); + } + phase.add(cdfemSupport.ls_field(LSphase).identifier, -1); + } + else + { + const int num_ls = cdfemSupport.num_ls_fields(); + for(int ls_index=0; ls_index < num_ls; ++ls_index) + { + const InterfaceID interface(ls_index,ls_index); + int sign = 0; + bool shouldSetPhase = false; + for(auto && edge : myParentEdges) + { + if (edge) + { + shouldSetPhase = true; + if (edge->have_crossing(InterfaceID(ls_index,ls_index))) + { + shouldSetPhase = false; + break; + } + else + sign = edge->get_crossing_sign(interface); + } + } + if (shouldSetPhase) + phase.add(cdfemSupport.ls_field(ls_index).identifier, sign); + } + } + + return phase; +} + +static std::vector find_next_phase_candidates(const std::vector & interfaces, + const std::vector & pathSoFar) +{ + const int currentPhase = pathSoFar.back(); + std::vector nextPhaseCandidates; + for (auto && interface : interfaces) + { + if (interface.first_ls() == currentPhase) + { + if (std::find(pathSoFar.begin(), pathSoFar.end(), interface.second_ls()) == pathSoFar.end()) + nextPhaseCandidates.push_back(interface.second_ls()); + } + else if (interface.second_ls() == currentPhase) + { + if (std::find(pathSoFar.begin(), pathSoFar.end(), interface.first_ls()) == pathSoFar.end()) + nextPhaseCandidates.push_back(interface.first_ls()); + } + } + return nextPhaseCandidates; +} + +static std::vector shortest_path_to_end(const std::vector & pathSoFar, + const std::vector & interfaces, + const std::set & endPhases) +{ + if (endPhases.count(pathSoFar.back()) > 0) + return pathSoFar; + + const std::vector nextPhaseCandidates = find_next_phase_candidates(interfaces, pathSoFar); + if (nextPhaseCandidates.empty()) + { + return {}; + } + + std::vector shortestPath; + size_t shortestPathSize = std::numeric_limits::max(); + for (int nextPhase : nextPhaseCandidates) + { + std::vector path = pathSoFar; + path.push_back(nextPhase); + const auto fullPath = shortest_path_to_end(path, interfaces, endPhases); + if (!fullPath.empty() && fullPath.size() < shortestPathSize) + { + shortestPath = fullPath; + shortestPathSize = fullPath.size(); + } + } + return shortestPath; +} + +std::vector shortest_path_from_begin_to_end(const std::vector & interfaces, + const int beginPhase, + const std::set & endPhases) +{ + std::vector startPath; + startPath.push_back(beginPhase); + return shortest_path_to_end(startPath, interfaces, endPhases); +} + +static bool captures_interface(const std::vector * sortedDomains, const InterfaceID & interface) +{ + if (sortedDomains == nullptr) + return false; + if (interface.first_ls() == interface.second_ls()) + return first_sorted_vector_of_domains_contains_all_domains_in_second_vector(*sortedDomains, {interface.first_ls()}); + return first_sorted_vector_of_domains_contains_all_domains_in_second_vector(*sortedDomains, {interface.first_ls(),interface.second_ls()}); +} + +static bool interface_has_uncaptured_edge_intersection(const LevelSetElementCutter & cutter, + const std::vector & elemNodesCoords, + const std::vector *> & elemNodesSnappedDomains, + const InterfaceID & interface) +{ + ThrowRequire(elemNodesCoords.size() == 4 || elemNodesCoords.size() == 3); + const stk::topology topology = (elemNodesCoords.size() == 4)? stk::topology::TETRAHEDRON_4 : stk::topology::TRIANGLE_3_2D; + + const unsigned numEdges = topology.num_edges(); + for(unsigned i=0; i < numEdges; ++i) + { + const unsigned * edgeNodeOrdinals = get_edge_node_ordinals(topology, i); + const int n0 = edgeNodeOrdinals[0]; + const int n1 = edgeNodeOrdinals[1]; + if (!captures_interface(elemNodesSnappedDomains[n0], interface) && !captures_interface(elemNodesSnappedDomains[n1], interface)) + { + const Segment3d edge(elemNodesCoords[n0], elemNodesCoords[n1]); + if (cutter.have_crossing(interface, edge)) + return true; + } + } + return false; +} + +static std::vector +get_sorted_cutting_interfaces_with_uncaptured_intersections(const LevelSetElementCutter & cutter, + const std::vector & elemNodesCoords, + const std::vector *> & elemNodesSnappedDomains) +{ + std::set interfacesWithUncapturedCrossings; + for (auto && interface : cutter.get_sorted_cutting_interfaces()) + if (interface_has_uncaptured_edge_intersection(cutter, elemNodesCoords, elemNodesSnappedDomains, interface)) + interfacesWithUncapturedCrossings.insert(interface); + + cutter.add_interfaces_with_uncaptured_intersection_within_element(elemNodesCoords, elemNodesSnappedDomains, interfacesWithUncapturedCrossings); + + std::vector interfaces(interfacesWithUncapturedCrossings.begin(), interfacesWithUncapturedCrossings.end()); + return interfaces; +} + +static int get_interface_index(const std::vector & sortedInterfaces, const InterfaceID interface) +{ + const auto iter = std::lower_bound(sortedInterfaces.begin(), sortedInterfaces.end(), interface); + return std::distance(sortedInterfaces.begin(), iter); +} + +static Vector3d get_centroid(const std::vector & elemNodesCoords) +{ + Vector3d centroid = Vector3d::ZERO; + for(auto && nodeCoords : elemNodesCoords) + { + centroid += nodeCoords; + } + centroid *= 1./elemNodesCoords.size(); + return centroid; +} + +std::vector +LevelSetElementCutter::get_interface_signs_based_on_crossings(const std::vector & elemNodesCoords, + const std::vector *> & elemNodesSnappedDomains) const +{ + const auto allInterfaces = get_sorted_cutting_interfaces(); + std::vector interfaceSigns(allInterfaces.size(), 0); + + const auto intersectingInterfaces = get_sorted_cutting_interfaces_with_uncaptured_intersections(*this, elemNodesCoords, elemNodesSnappedDomains); + const Vector3d centroid = get_centroid(elemNodesCoords); + + const bool oneLSPerPhase = Phase_Support::has_one_levelset_per_phase(); + if (oneLSPerPhase) + { + std::set subPhases; + const int ownerStartPhase = get_starting_phase_for_cutting_surfaces(); + + for (auto && interface : allInterfaces) + { + if (std::binary_search(intersectingInterfaces.begin(), intersectingInterfaces.end(), interface)) + { + subPhases.insert(interface.first_ls()); + subPhases.insert(interface.second_ls()); + } + else + { + interfaceSigns[get_interface_index(allInterfaces, interface)] = -2; + } + } + if (subPhases.empty()) + { + subPhases.insert(myElementInterfaceCutter->get_ls_per_interface_phase_at_location(centroid)); + } + + if (subPhases.count(ownerStartPhase) == 0) + { + std::vector fixPath = shortest_path_from_begin_to_end(allInterfaces, ownerStartPhase, subPhases); + ThrowRequireMsg(!fixPath.empty(), "Cannot fix starting phase."); + for (unsigned i=1; i fixPath[i-1]) ? 1 : -1; + interfaceSigns[get_interface_index(allInterfaces, interface)] = sign; + } + } + } + else + { + for (auto && interface : allInterfaces) + if (!std::binary_search(intersectingInterfaces.begin(), intersectingInterfaces.end(), interface)) + interfaceSigns[get_interface_index(allInterfaces, interface)] = sign_at_position(interface, centroid); + } + + return interfaceSigns; +} + +void LevelSetElementCutter::update_edge_crossings(const unsigned iEdge, const std::vector> & nodesIsovar) +{ + ThrowRequire(iEdge < myParentEdges.size()); + CDFEM_Parent_Edge * parentEdge = const_cast(myParentEdges[iEdge]); + ThrowRequire(parentEdge); + if (myParentEdgesAreOrientedSameAsElementEdges[iEdge]) + { + parentEdge->find_crossings(nodesIsovar); + } + else + { + const std::vector> orientedIsovar = {nodesIsovar[1], nodesIsovar[0]}; + parentEdge->find_crossings(orientedIsovar); + } +} + +static std::function &)> +temporary_build_always_true_diagonal_picker() +{ + auto diagonalPicker = + [](const std::array & faceNodes) + { + return true; + }; + return diagonalPicker; +} + +typedef std::function ParentEdgeFilter; + +static ParentEdgeFilter keep_owned_edges_filter(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & parentElementSelector) +{ + const auto filter = [&mesh, &parentElementSelector](const CDFEM_Parent_Edge & edge) + { + const std::pair edgeNodes = edge.get_parent_nodes(); + std::vector edgeElems; + stk::mesh::get_entities_through_relations(mesh, {edgeNodes.first, edgeNodes.second}, stk::topology::ELEMENT_RANK, edgeElems); + { + bool foundOwnedElement = false; + for (auto && edgeElem : edgeElems) + if (parentElementSelector(mesh.bucket(edgeElem)) && mesh.parallel_owner_rank(edgeElem) == mesh.parallel_rank()) + foundOwnedElement = true; + ThrowRequire(foundOwnedElement); + } + const int parallelRank = mesh.parallel_rank(); // Assumes local proc owns at least one selected element of edge + for (auto && edgeElem : edgeElems) + if (mesh.parallel_owner_rank(edgeElem) < parallelRank && parentElementSelector(mesh.bucket(edgeElem))) + return false; + return true; + }; + return filter; +} + +static ParentEdgeFilter keep_all_edges_filter() +{ + const auto filter = [](const CDFEM_Parent_Edge & edge) + { + return true; + }; + return filter; +} + +static void append_intersection_points_from_filtered_parent_edges(std::vector & intersectionPoints, + const ParentEdgeMap & parentEdges, + const IntersectionPointFilter & intersectionPointFilter, + const ParentEdgeFilter edgeFilter) +{ + const bool intersectionPointIsOwned = true; + std::vector intersectionPointSortedDomains; + for (auto && mapEntry : parentEdges) + { + const CDFEM_Parent_Edge & edge = mapEntry.second; + const auto & edgeCrossings = edge.get_crossings(); + if (!edgeCrossings.empty() && edgeFilter(edge)) + { + const std::pair edgeNodes = edge.get_parent_nodes(); + for (auto & crossing : edgeCrossings) + { + const InterfaceID & interface = crossing.first; + const double location = crossing.second; + interface.fill_sorted_domains(intersectionPointSortedDomains); + std::vector intersectionPointNodes{edgeNodes.first, edgeNodes.second}; + if (intersectionPointFilter(intersectionPointNodes, intersectionPointSortedDomains)) + intersectionPoints.emplace_back(intersectionPointIsOwned, intersectionPointNodes, std::vector{1.-location, location}, intersectionPointSortedDomains); + } + } + } +} + +static void append_intersection_points_from_owned_parent_edges(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & parentElementSelector, + std::vector & intersectionPoints, + const ParentEdgeMap & parentEdges, + const IntersectionPointFilter & intersectionPointFilter) +{ + append_intersection_points_from_filtered_parent_edges(intersectionPoints, parentEdges, intersectionPointFilter, keep_owned_edges_filter(mesh, parentElementSelector)); +} + +static void append_intersection_points_from_all_parent_edges(std::vector & intersectionPoints, + const ParentEdgeMap & parentEdges, + const IntersectionPointFilter & intersectionPointFilter) +{ + append_intersection_points_from_filtered_parent_edges(intersectionPoints, parentEdges, intersectionPointFilter, keep_all_edges_filter()); +} + +LevelSetInterfaceGeometry::LevelSetInterfaceGeometry(const stk::mesh::MetaData & meta) +: LevelSetInterfaceGeometry(AuxMetaData::get(meta).active_part(), CDFEM_Support::get(meta), Phase_Support::get(meta)) {} + +void LevelSetInterfaceGeometry::prepare_to_process_elements(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const +{ + myParentsToChildMapper.build_map(mesh, myActivePart, myCdfemSupport, myPhaseSupport); + const auto should_build_linearized_edge = build_no_linearized_edges_function(); + myParentEdges = build_parent_edges(mesh, myParentsToChildMapper, should_build_linearized_edge, myActivePart, myCdfemSupport, myPhaseSupport); +} + +void LevelSetInterfaceGeometry::prepare_to_process_elements(const stk::mesh::BulkData & mesh, + const std::vector & elementsToIntersect, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const +{ + myParentsToChildMapper.build_map(mesh, myActivePart, myCdfemSupport, myPhaseSupport); + const auto should_build_linearized_edge = build_no_linearized_edges_function(); + myParentEdges = build_parent_edges_using_elements(mesh, myParentsToChildMapper, should_build_linearized_edge, elementsToIntersect, myActivePart, myCdfemSupport, myPhaseSupport); +} + +std::vector LevelSetInterfaceGeometry::get_edge_intersection_points(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const +{ + std::vector intersectionPoints; + prepare_to_process_elements(mesh, nodesToCapturedDomains); + const IntersectionPointFilter intersectionPointFilter = keep_all_intersection_points_filter(); + append_intersection_points_from_all_parent_edges(intersectionPoints, myParentEdges, intersectionPointFilter); + return intersectionPoints; +} + +void LevelSetInterfaceGeometry::append_element_intersection_points(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToCapturedDomains, + const std::vector & elementsToIntersect, + const IntersectionPointFilter & intersectionPointFilter, + std::vector & intersectionPoints) const +{ + const stk::mesh::Selector parentElementSelector = get_parent_element_selector(myActivePart, myCdfemSupport, myPhaseSupport); + prepare_to_process_elements(mesh, elementsToIntersect, nodesToCapturedDomains); + append_intersection_points_from_owned_parent_edges(mesh, parentElementSelector, intersectionPoints, myParentEdges, intersectionPointFilter); + append_intersection_points_from_within_elements_and_owned_faces(mesh, parentElementSelector, elementsToIntersect, *this, intersectionPointFilter, intersectionPoints); +} + +static int get_domain_for_uncut_element(const stk::mesh::BulkData & mesh, + const stk::mesh::Entity element, + const std::vector & elementParentEdges, + const bool oneLSPerPhase) +{ + if (oneLSPerPhase) + { + const auto edgePhases = get_phases_present_on_edges_and_interior(elementParentEdges); + if (edgePhases.size() == 1) + return *edgePhases.begin(); + } + return -1; +} + +void LevelSetInterfaceGeometry::store_phase_for_uncut_elements(const stk::mesh::BulkData & mesh) const +{ + std::vector elementsToIntersect; + stk::mesh::get_entities( mesh, stk::topology::ELEMENT_RANK, elementsToIntersect, false); + + myParentsToChildMapper.build_map(mesh, myActivePart, myCdfemSupport, myPhaseSupport); + const auto linearize_all_edges = build_all_linearized_edges_function(); + ParentEdgeMap parentEdges = build_parent_edges_using_elements(mesh, myParentsToChildMapper, linearize_all_edges, elementsToIntersect, myActivePart, myCdfemSupport, myPhaseSupport); + + const bool oneLSPerPhase = myCdfemSupport.num_ls_fields() > 1 && Phase_Support::has_one_levelset_per_phase(); + std::vector elementParentEdges; + std::vector areParentEdgesOrientedSameAsElementEdges; + + const std::vector parentElements = get_owned_parent_elements(mesh, myActivePart, myCdfemSupport, myPhaseSupport); + for (auto element : parentElements) + { + fill_element_parent_edges(mesh, element, parentEdges, elementParentEdges, areParentEdgesOrientedSameAsElementEdges); + const int elementDomain = get_domain_for_uncut_element(mesh, + element, + elementParentEdges, + oneLSPerPhase); + if (elementDomain >= 0) + myUncutElementPhases[element] = elementDomain; + } +} + +static NodeToCapturedDomainsMap store_and_communicate_new_snap_node_domains(const stk::mesh::BulkData & mesh, const std::vector & intersectionPoints, const std::vector & snapInfos) +{ + std::vector snapNodes; + NodeToCapturedDomainsMap newSnapnodesToCapturedDomains; + for (auto && snapInfo : snapInfos) + { + if (snapInfo.get_owner() == mesh.parallel_rank()) + { + const size_t intersectionPointIndex = snapInfo.get_intersection_point_index(); + stk::mesh::Entity snapNode = mesh.get_entity(stk::topology::NODE_RANK, snapInfo.get_node_global_id()); + snapNodes.push_back(snapNode); + const IntersectionPoint & intersectionPoint = intersectionPoints[intersectionPointIndex]; + + newSnapnodesToCapturedDomains[snapNode] = intersectionPoint.get_sorted_domains(); + } + } + + communicate_node_captured_domains_for_given_nodes(mesh, snapNodes, newSnapnodesToCapturedDomains); + + return newSnapnodesToCapturedDomains; +} + +static std::vector get_node_parametric_coords_after_snapping(const stk::mesh::BulkData & mesh, + stk::mesh::Entity element, + stk::mesh::Entity snapNode, + const Vector3d snapNodeLocation) +{ + stk::topology elementTopology = mesh.bucket(element).topology(); + const MasterElement & masterElement = MasterElementDeterminer::getMasterElement(elementTopology); + const double * elemNodeParamCoords = masterElement.nodal_parametric_coordinates(); + const int dim = mesh.mesh_meta_data().spatial_dimension(); + const StkMeshEntities elementNodes{mesh.begin_nodes(element), mesh.end_nodes(element)}; + const FieldRef coordsField(mesh.mesh_meta_data().coordinate_field()); + + std::vector nodeLocations; + fill_element_node_coordinates(mesh, element, coordsField, nodeLocations); + + std::vector nodesCoords; + for (unsigned n=0; n= baseShapeFcn) + return 1.; + return baseShapeFcn/(baseShapeFcn-shapeFcn); +} + +static Vector3d find_point_within_deformed_and_undeformed_tet(const std::vector & deformedElementParamCoords, + const int lnn) +{ + stk::topology topology = stk::topology::TETRAHEDRON_4; + std::array permutations{0, 1, 2, 4}; + std::array permuteNodes; + topology.permutation_node_ordinals(permutations[lnn], permuteNodes.data()); + ThrowAssert(permuteNodes[0] == lnn); + const Vector3d & pt = deformedElementParamCoords[lnn]; + const Vector3d oppositePt = 1./3.*(deformedElementParamCoords[permuteNodes[1]] + deformedElementParamCoords[permuteNodes[2]] + deformedElementParamCoords[permuteNodes[3]]); + double fraction = 1.0; + fraction = std::min(fraction, truncate_to_maintain_positive_shape_function(pt[0], oppositePt[0])); + fraction = std::min(fraction, truncate_to_maintain_positive_shape_function(pt[1], oppositePt[1])); + fraction = std::min(fraction, truncate_to_maintain_positive_shape_function(pt[2], oppositePt[2])); + fraction = std::min(fraction, truncate_to_maintain_positive_shape_function(1.-pt[0]-pt[1]-pt[2], 1.-oppositePt[0]-oppositePt[1]-oppositePt[2])); + const double centroidWt = 0.25*fraction; + return centroidWt*pt + (1.-centroidWt)*oppositePt; +} + +static Vector3d find_point_within_deformed_and_undeformed_tri(const std::vector & deformedElementParamCoords, + const int lnn) +{ + stk::topology topology = stk::topology::TRIANGLE_3_2D; + std::array permutations{0, 2, 1}; + std::array permuteNodes; + topology.permutation_node_ordinals(permutations[lnn], permuteNodes.data()); + ThrowAssert(permuteNodes[0] == lnn); + const Vector3d & pt = deformedElementParamCoords[lnn]; + const Vector3d oppositePt = 0.5*(deformedElementParamCoords[permuteNodes[1]] + deformedElementParamCoords[permuteNodes[2]]); + double fraction = 1.0; + fraction = std::min(fraction, truncate_to_maintain_positive_shape_function(pt[0], oppositePt[0])); + fraction = std::min(fraction, truncate_to_maintain_positive_shape_function(pt[1], oppositePt[1])); + fraction = std::min(fraction, truncate_to_maintain_positive_shape_function(1.-pt[0]-pt[1], 1.-oppositePt[0]-oppositePt[1])); + const double centroidWt = 1./3.*fraction; + return centroidWt*pt + (1.-centroidWt)*oppositePt; +} + +static int get_node_of_element(const stk::mesh::BulkData & mesh, + stk::mesh::Entity element, + stk::mesh::Entity node) +{ + const StkMeshEntities elementNodes{mesh.begin_nodes(element), mesh.end_nodes(element)}; + for (unsigned n=0; n & deformedElementParamCoords) +{ + const int lnn = get_node_of_element(mesh, element, snapNode); + + stk::topology elementTopology = mesh.bucket(element).topology(); + if (elementTopology.base() == stk::topology::TETRAHEDRON_4) + return find_point_within_deformed_and_undeformed_tet(deformedElementParamCoords, lnn); + ThrowRequire(elementTopology.base() == stk::topology::TRIANGLE_3_2D); + return find_point_within_deformed_and_undeformed_tri(deformedElementParamCoords, lnn); +} + +static std::vector*> get_node_snap_domains(const stk::mesh::BulkData & mesh, + stk::mesh::Entity element, + stk::mesh::Entity snapNode, + const std::vector & snapNodeDomains, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) +{ + const StkMeshEntities elementNodes{mesh.begin_nodes(element), mesh.end_nodes(element)}; + std::vector*> nodeDomains; + nodeDomains.reserve(elementNodes.size()); + for (auto node : StkMeshEntities{mesh.begin_nodes(element), mesh.end_nodes(element)}) + { + if (node == snapNode) + { + nodeDomains.push_back(&snapNodeDomains); + } + else + { + const auto iter = nodesToCapturedDomains.find(node); + if (iter == nodesToCapturedDomains.end()) + nodeDomains.push_back(nullptr); + else + nodeDomains.push_back(&(iter->second)); + } + } + + return nodeDomains; +} + +static bool domain_is_common_to_all_entities(const int domain, const std::vector*> & entitiesDomains) +{ + for (auto && domains : entitiesDomains) + if (!std::binary_search(domains->begin(), domains->end(), domain)) + return false; + return true; +} + +static std::set get_common_domains(std::vector*> entitiesDomains) +{ + std::set commonDomains; + + if (entitiesDomains.empty()) + return commonDomains; + + for (auto & entityDomains : entitiesDomains) + if (!entityDomains) + return commonDomains; + + for (int domain : *entitiesDomains[0]) + if (domain_is_common_to_all_entities(domain, entitiesDomains)) + commonDomains.insert(domain); + + return commonDomains; +} + +static bool will_have_uncaptured_edge_intersection_after_snapping(const Element_Cutter & cutter, stk::topology elementTopology, const std::vector nodeCoords, + const std::vector*> & nodeDomains) +{ + std::vector interfacesWithCuttingSurface; + cutter.fill_interfaces_with_cutting_surface(interfacesWithCuttingSurface); + + const unsigned numEdges = elementTopology.num_edges(); + for(unsigned i=0; i < numEdges; ++i) + { + const unsigned * edgeNodeOrdinals = get_edge_node_ordinals(elementTopology(), i); + const std::vector* node0Domains = nodeDomains[edgeNodeOrdinals[0]]; + const std::vector* node1Domains = nodeDomains[edgeNodeOrdinals[1]]; + + for (auto && interface : interfacesWithCuttingSurface) + { + if (!captures_interface(node0Domains, interface) && !captures_interface(node1Domains, interface)) + { + const Segment3d edge(nodeCoords[edgeNodeOrdinals[0]], nodeCoords[edgeNodeOrdinals[1]]); + if (cutter.have_crossing(interface, edge)) + return true; + } + } + } + return false; +} + +static int get_domain_of_element_if_it_will_be_uncut_after_snapping(const stk::mesh::BulkData & mesh, + const ParentsToChildMapper parentsToChildMapper, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + stk::mesh::Entity element, + stk::mesh::Entity snapNode, + const Vector3d & snapNodeLocation, + const std::vector*> & elementNodeDomains) +{ + const auto diagonalPicker = temporary_build_always_true_diagonal_picker(); + const bool oneLSPerPhase = true; + + ParentEdgeMap parentEdges = build_parent_edges_using_elements(mesh, parentsToChildMapper, build_all_linearized_edges_function(), {element}, activePart, cdfemSupport, phaseSupport); + if (parentEdges.empty()) + return -1; // not parent element + + std::vector elementParentEdges; + std::vector areParentEdgesOrientedSameAsElementEdges; + fill_element_parent_edges(mesh, element, parentEdges, elementParentEdges, areParentEdgesOrientedSameAsElementEdges); + + const auto edgePhases = get_phases_present_on_edges_and_interior(elementParentEdges); + if (edgePhases.size() == 1) + return *edgePhases.begin(); + + stk::topology elementTopology = mesh.bucket(element).topology(); + const MasterElement & masterElement = MasterElementDeterminer::getMasterElement(elementTopology); + std::unique_ptr elementCutter = create_element_cutter(oneLSPerPhase, masterElement, elementParentEdges, areParentEdgesOrientedSameAsElementEdges, diagonalPicker); + + const std::vector nodeParamCoordsAfterSnapping = get_node_parametric_coords_after_snapping(mesh, element, snapNode, snapNodeLocation); + if (!will_have_uncaptured_edge_intersection_after_snapping(*elementCutter, elementTopology, nodeParamCoordsAfterSnapping, elementNodeDomains)) + { + const Vector3d evaluationPt = find_point_within_deformed_and_undeformed_element(mesh, element, snapNode, nodeParamCoordsAfterSnapping); + return elementCutter->get_ls_per_interface_phase_at_location(evaluationPt); + } + + return -1; +} + +static int get_sign_of_uncrossed_edges(const InterfaceID interface, const std::vector & elementParentEdges) +{ + int uncrossedSign = 0; + for (auto && edge : elementParentEdges) + { + if (edge->have_crossing(interface)) + { + return 0; + } + else + { + const int edgeSign = edge->get_crossing_sign(interface); + if (edgeSign == -uncrossedSign) + return 0; + uncrossedSign = edgeSign; + } + } + return uncrossedSign; +} + +static int get_sign_of_element_if_it_will_be_uncut_after_snapping(const stk::mesh::BulkData & mesh, + const ParentsToChildMapper parentsToChildMapper, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + stk::mesh::Entity element, + stk::mesh::Entity snapNode, + const Vector3d & snapNodeLocation, + const InterfaceID & interface) +{ + const auto diagonalPicker = temporary_build_always_true_diagonal_picker(); + const bool oneLSPerPhase = false; + + ParentEdgeMap parentEdges = build_parent_edges_using_elements(mesh, parentsToChildMapper, build_all_linearized_edges_function(), {element}, activePart, cdfemSupport, phaseSupport); + if (parentEdges.empty()) + return -1; // not parent element + + std::vector elementParentEdges; + std::vector areParentEdgesOrientedSameAsElementEdges; + fill_element_parent_edges(mesh, element, parentEdges, elementParentEdges, areParentEdgesOrientedSameAsElementEdges); + + const int uncrossedSign = get_sign_of_uncrossed_edges(interface, elementParentEdges); + if (uncrossedSign != 0) + return uncrossedSign; + + stk::topology elementTopology = mesh.bucket(element).topology(); + const MasterElement & masterElement = MasterElementDeterminer::getMasterElement(elementTopology); + std::unique_ptr elementCutter = create_element_cutter(oneLSPerPhase, masterElement, elementParentEdges, areParentEdgesOrientedSameAsElementEdges, diagonalPicker); + + const std::vector nodeParamCoordsAfterSnapping = get_node_parametric_coords_after_snapping(mesh, element, snapNode, snapNodeLocation); + const Vector3d evaluationPt = find_point_within_deformed_and_undeformed_element(mesh, element, snapNode, nodeParamCoordsAfterSnapping); + return elementCutter->sign_at_position(interface, evaluationPt); +} + +static void set_domains_for_element_if_it_will_be_uncut_after_snapping(const stk::mesh::BulkData & mesh, + const ParentsToChildMapper parentsToChildMapper, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + stk::mesh::Entity element, + stk::mesh::Entity snapNode, + const std::vector & snapNodeDomains, + const Vector3d & snapNodeLocation, + const NodeToCapturedDomainsMap & nodesToCapturedDomains, + ElementToDomainMap & elementsToDomain ) +{ + auto iter = elementsToDomain.lower_bound(element); + if (iter == elementsToDomain.end() || iter->first != element) + { + const std::vector*> nodeDomains = get_node_snap_domains(mesh, element, snapNode, snapNodeDomains, nodesToCapturedDomains); + + const std::set commonDomains = get_common_domains(nodeDomains); + + int elementDomain = -1; + if (commonDomains.size() == 1) + { + elementDomain = *commonDomains.begin(); + } + else if (commonDomains.size() > 1) + { + elementDomain = get_domain_of_element_if_it_will_be_uncut_after_snapping(mesh, parentsToChildMapper, activePart, cdfemSupport, phaseSupport, element, snapNode, snapNodeLocation, nodeDomains); + } + + if (elementDomain >= 0) + elementsToDomain.emplace_hint(iter, element, elementDomain); + } +} + +static void set_sign_for_element_if_it_will_be_uncut_after_snapping(const stk::mesh::BulkData & mesh, + const ParentsToChildMapper parentsToChildMapper, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport, + stk::mesh::Entity element, + stk::mesh::Entity snapNode, + const std::vector & snapNodeDomains, + const Vector3d & snapNodeLocation, + const NodeToCapturedDomainsMap & nodesToCapturedDomains, + ElementToDomainMap & elementsToDomain ) +{ + auto iter = elementsToDomain.lower_bound(element); + if (iter == elementsToDomain.end() || iter->first != element) + { + const std::vector*> nodeDomains = get_node_snap_domains(mesh, element, snapNode, snapNodeDomains, nodesToCapturedDomains); + + const std::set commonDomains = get_common_domains(nodeDomains); + + int elementSign = 0; + if (commonDomains.size() == 1) + { + const int lsIndex = *commonDomains.begin(); + const InterfaceID interface(lsIndex,lsIndex); + elementSign = get_sign_of_element_if_it_will_be_uncut_after_snapping(mesh, parentsToChildMapper, activePart, cdfemSupport, phaseSupport, element, snapNode, snapNodeLocation, interface); + } + + if (elementSign != 0) + elementsToDomain.emplace_hint(iter, element, elementSign); + } +} + +void LevelSetInterfaceGeometry::store_phase_for_elements_that_will_be_uncut_after_snapping(const stk::mesh::BulkData & mesh, + const std::vector & intersectionPoints, + const std::vector & snapInfos, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const +{ + const bool oneLSPerPhase = myCdfemSupport.num_ls_fields() > 1 && Phase_Support::has_one_levelset_per_phase(); + if (!oneLSPerPhase && myCdfemSupport.num_ls_fields() > 1) + return; //FIXME: Fix for more than one ls per interface + + const NodeToCapturedDomainsMap newSnapnodesToCapturedDomains = store_and_communicate_new_snap_node_domains(mesh, intersectionPoints, snapInfos); + + for (auto && snapInfo : snapInfos) + { + stk::mesh::Entity snapNode = mesh.get_entity(stk::topology::NODE_RANK, snapInfo.get_node_global_id()); + const stk::math::Vector3d snapLocation = snapInfo.get_snap_location(); + const auto & newSnapNodeDomains = newSnapnodesToCapturedDomains.at(snapNode); + for (auto elem : StkMeshEntities{mesh.begin_elements(snapNode), mesh.end_elements(snapNode)}) + { + if (mesh.bucket(elem).owned() && mesh.bucket(elem).member(myActivePart)) + { + if (oneLSPerPhase) + set_domains_for_element_if_it_will_be_uncut_after_snapping(mesh, myParentsToChildMapper, myActivePart, myCdfemSupport, myPhaseSupport, elem, snapNode, newSnapNodeDomains, snapLocation, nodesToCapturedDomains, myUncutElementPhases); + else + set_sign_for_element_if_it_will_be_uncut_after_snapping(mesh, myParentsToChildMapper, myActivePart, myCdfemSupport, myPhaseSupport, elem, snapNode, newSnapNodeDomains, snapLocation, nodesToCapturedDomains, myUncutElementPhases); + } + } + } +} + +} diff --git a/packages/krino/krino/krino_lib/Akri_LevelSetInterfaceGeometry.hpp b/packages/krino/krino/krino_lib/Akri_LevelSetInterfaceGeometry.hpp new file mode 100644 index 000000000000..393c97c3e622 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_LevelSetInterfaceGeometry.hpp @@ -0,0 +1,122 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_LEVELSETINTERFACEGEOMETRY_H_ +#define KRINO_INCLUDE_AKRI_LEVELSETINTERFACEGEOMETRY_H_ +#include +#include +#include +#include +#include "../interface_geometry_interface/Akri_InterfaceGeometry.hpp" + +namespace krino { + +class CDFEM_Support; +class Phase_Support; + +class LevelSetElementCutter : public ElementCutter +{ +public: + LevelSetElementCutter(const stk::mesh::BulkData & mesh, + stk::mesh::Entity element, + const ParentEdgeMap & parentEdges, + const Phase_Support & phaseSupport, + const std::function &)> & intersectingPlanesDiagonalPicker); + virtual ~LevelSetElementCutter() {} + + virtual bool might_have_interior_or_face_intersections() const override + { return myElementInterfaceCutter->get_num_cutting_surfaces() > 1; } + virtual void fill_interior_intersections(const ElementIntersectionPointFilter & intersectionPointFilter, std::vector & intersections) const override + { myElementInterfaceCutter->fill_interior_intersections(intersectionPointFilter, intersections); } + virtual std::vector get_sorted_cutting_interfaces() const override + { std::vector interfaces; myElementInterfaceCutter->fill_interfaces_with_cutting_surface(interfaces); return interfaces; } + virtual std::vector get_interface_signs_based_on_crossings(const std::vector & elemNodesCoords, + const std::vector *> & elemNodesSnappedDomains) const override; + virtual void fill_tetrahedron_face_interior_intersections(const std::array & faceNodes, + const InterfaceID & interface1, + const InterfaceID & interface2, + const ElementIntersectionPointFilter & intersectionPointFilter, + std::vector & intersections) const override + { myElementInterfaceCutter->fill_tetrahedron_face_interior_intersections(faceNodes, interface1, interface2, intersectionPointFilter, intersections); } + virtual std::string visualize(const stk::mesh::BulkData & mesh) const override; + virtual bool have_crossing(const InterfaceID interface, const Segment3d & edge) const override + { return myElementInterfaceCutter->have_crossing(interface, edge); } + virtual double interface_crossing_position(const InterfaceID interface, const Segment3d & edge) const override + { return myElementInterfaceCutter->interface_crossing_position(interface, edge); } + virtual int sign_at_position(const InterfaceID interface, const Vector3d & paramCoords) const override + { return myElementInterfaceCutter->sign_at_position(interface, paramCoords); } + virtual int get_starting_phase_for_cutting_surfaces() const override + { return myElementInterfaceCutter->get_starting_phase_for_cutting_surfaces(); } + + PhaseTag get_starting_phase(const CDFEM_Support & cdfemSupport, const Phase_Support & phaseSupport) const; + void update_edge_crossings(const unsigned iEdge, const std::vector> & nodesIsovar); + void add_interfaces_with_uncaptured_intersection_within_element(const std::vector & elemNodesCoords, + const std::vector *> & elemNodesSnappedDomains, + std::set & interfacesWithUncapturedCrossings) const + { return myElementInterfaceCutter->add_interfaces_with_uncaptured_intersection_within_element(elemNodesCoords, elemNodesSnappedDomains, interfacesWithUncapturedCrossings); } + +private: + std::vector myParentEdges; + std::vector myParentEdgesAreOrientedSameAsElementEdges; + std::unique_ptr myElementInterfaceCutter; +}; + +class LevelSetInterfaceGeometry : public InterfaceGeometry { + +public: + LevelSetInterfaceGeometry(const stk::mesh::MetaData & meta); + LevelSetInterfaceGeometry(const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport) +: myActivePart(activePart), + myCdfemSupport(cdfemSupport), + myPhaseSupport(phaseSupport) {} + virtual ~LevelSetInterfaceGeometry() {} + + virtual void prepare_to_process_elements(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const override; + virtual void prepare_to_process_elements(const stk::mesh::BulkData & mesh, + const std::vector & elementsToIntersect, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const override; + + virtual std::vector get_edge_intersection_points(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const override; + virtual void append_element_intersection_points(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToCapturedDomains, + const std::vector & elementsToIntersect, + const IntersectionPointFilter & intersectionPointFilter, + std::vector & intersectionPoints) const override; + + // FIXME: Temporary methods + virtual void store_phase_for_uncut_elements(const stk::mesh::BulkData & mesh) const override; + virtual void store_phase_for_elements_that_will_be_uncut_after_snapping(const stk::mesh::BulkData & mesh, + const std::vector & intersectionPoints, + const std::vector & independentSnapInfos, + const NodeToCapturedDomainsMap & nodesToCapturedDomains) const override; + + virtual const ElementToDomainMap & get_phase_for_uncut_elements() const override { return myUncutElementPhases; } + + virtual std::unique_ptr build_element_cutter(const stk::mesh::BulkData & mesh, + stk::mesh::Entity element, + const std::function &)> & intersectingPlanesDiagonalPicker) const override; + + virtual PhaseTag get_starting_phase(const ElementCutter * cutter) const override; + +private: + const stk::mesh::Part & myActivePart; + const CDFEM_Support & myCdfemSupport; + const Phase_Support & myPhaseSupport; + mutable ParentEdgeMap myParentEdges; + mutable ParentsToChildMapper myParentsToChildMapper; + mutable ElementToDomainMap myUncutElementPhases; +}; + +} + + +#endif /* KRINO_INCLUDE_AKRI_LEVELSETINTERFACEGEOMETRY_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_LevelSet_Identifier.hpp b/packages/krino/krino/krino_lib/Akri_LevelSet_Identifier.hpp new file mode 100644 index 000000000000..4de543ea1156 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_LevelSet_Identifier.hpp @@ -0,0 +1,37 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_LevelSet_Identifier_h +#define Akri_LevelSet_Identifier_h + +#include + +namespace krino { + +class LevelSet_Identifier { +public: + explicit LevelSet_Identifier(const unsigned id) : my_id(id) {} + + bool operator < ( const LevelSet_Identifier & RHS ) const { return my_id < RHS.my_id; } + bool operator == ( const LevelSet_Identifier & RHS ) const { return my_id == RHS.my_id; } + bool operator != ( const LevelSet_Identifier & RHS ) const { return my_id != RHS.my_id; } + + unsigned get() const { return my_id; } + + friend std::ostream& operator<<(std::ostream & os, const LevelSet_Identifier & id) + { + os << id.get(); + return os; + } +private: + unsigned my_id; +}; + +} // namespace krino + +#endif // Akri_LevelSet_Identifier_h diff --git a/packages/krino/krino/krino_lib/Akri_LowerEnvelope.cpp b/packages/krino/krino/krino_lib/Akri_LowerEnvelope.cpp new file mode 100644 index 000000000000..0f660042073a --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_LowerEnvelope.cpp @@ -0,0 +1,317 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include + +#include + +namespace krino{ + +LS_Segment::LS_Segment(const double xL, const double xR, int id_) +: x_left(xL), x_right(xR), LS_index(id_) +{ +} + +int +get_min(const std::vector & phi) +{ + double min_value = 0.0; + int min_index = -1; + for (unsigned i=0; i +find_crossing_position_and_sign(const InterfaceID key, const std::vector & pos, const std::vector> & phi) +{ + const int num_nodes = pos.size(); + std::vector key_isovar(num_nodes); + for ( int node = 0; node < num_nodes; ++node ) + { + key_isovar[node] = phi[node][key.first_ls()]-phi[node][key.second_ls()]; + } + + if ( LevelSet::sign_change(key_isovar[0], key_isovar[num_nodes-1]) ) + { + for ( int s = 0; s < num_nodes-1; ++s ) + { + const double ls0 = key_isovar[s]; + const double ls1 = key_isovar[s+1]; + if ( LevelSet::sign_change(ls0, ls1) ) + { + const double interval_position = ls0 / ( ls0 - ls1 ); + const double abs_position = (1.-interval_position)*pos[s] + interval_position*pos[s+1]; + return std::make_pair(abs_position, LevelSet::sign(ls1)); + } + } + } + + const int node0_sign = LevelSet::sign(key_isovar[0]); + const int node1_sign = LevelSet::sign(key_isovar[num_nodes-1]); + ThrowRequire(node0_sign == node1_sign); + return std::make_pair(-1., node0_sign); +} + +bool +find_lower_envelope_crossing_point( const std::array,2> & phi, double & x) +{ + const int id0 = get_min(phi[0]); + const int id1 = get_min(phi[1]); + + if ( id0 != id1 ) + { + const double ls0 = (phi[0][id0]-phi[0][id1]); + const double ls1 = (phi[1][id0]-phi[1][id1]); + + x = ls0 / ( ls0 - ls1 ); + return true; + } + + return false; +} + +bool +find_lower_envelope_crossing_point(const std::array,3> & phi, std::array & point) +{ + const int id0 = get_min(phi[0]); + const int id1 = get_min(phi[1]); + const int id2 = get_min(phi[2]); + + if (id0 != id1 && id1 != id2 && id0 != id2) + { + const double d10 = (phi[0][id1]-phi[0][id0]); + const double d11 = (phi[1][id1]-phi[1][id0]); + const double d12 = (phi[2][id1]-phi[2][id0]); + const double d20 = (phi[0][id2]-phi[0][id0]); + const double d21 = (phi[1][id2]-phi[1][id0]); + const double d22 = (phi[2][id2]-phi[2][id0]); + + const double a00 = d11-d10; + const double a01 = d12-d10; + const double a10 = d21-d20; + const double a11 = d22-d20; + + const double b0 = -d10; + const double b1 = -d20; + + const double det = a00*a11-a01*a10; + if (det == 0.) return false; + + const double x = (b0*a11-b1*a01)/det; + const double y = (-b0*a10+b1*a00)/det; + if (x < 0. || y < 0. || 1.-x-y < 0.) return false; + + point = {{x,y}}; + return true; + } + + return false; +} + +bool +find_lower_envelope_crossing_point(const std::array,4> & phi, std::array & point) +{ + const int id0 = get_min(phi[0]); + const int id1 = get_min(phi[1]); + const int id2 = get_min(phi[2]); + const int id3 = get_min(phi[3]); + + if (id0 != id1 && id0 != id2 && id0 != id3 && id1 != id2 && id1 != id3 && id2 != id3) + { + const double d10 = (phi[0][id1]-phi[0][id0]); + const double d11 = (phi[1][id1]-phi[1][id0]); + const double d12 = (phi[2][id1]-phi[2][id0]); + const double d13 = (phi[3][id1]-phi[3][id0]); + const double d20 = (phi[0][id2]-phi[0][id0]); + const double d21 = (phi[1][id2]-phi[1][id0]); + const double d22 = (phi[2][id2]-phi[2][id0]); + const double d23 = (phi[3][id2]-phi[3][id0]); + const double d30 = (phi[0][id3]-phi[0][id0]); + const double d31 = (phi[1][id3]-phi[1][id0]); + const double d32 = (phi[2][id3]-phi[2][id0]); + const double d33 = (phi[3][id3]-phi[3][id0]); + + const double a00 = d11-d10; + const double a01 = d12-d10; + const double a02 = d13-d10; + const double a10 = d21-d20; + const double a11 = d22-d20; + const double a12 = d23-d20; + const double a20 = d31-d30; + const double a21 = d32-d30; + const double a22 = d33-d30; + const double b0 = -d10; + const double b1 = -d20; + const double b2 = -d30; + const double det = a00*(a22*a11-a21*a12)-a10*(a22*a01-a21*a02)+a20*(a12*a01-a11*a02); + const double x =( b0*(a22*a11-a21*a12)-b1*(a22*a01-a21*a02)+b2*(a12*a01-a11*a02))/det; + const double y =(-b0*(a22*a10-a20*a12)+b1*(a22*a00-a20*a02)-b2*(a12*a00-a10*a02))/det; + const double z =( b0*(a21*a10-a20*a11)-b1*(a21*a00-a20*a01)+b2*(a11*a00-a10*a01))/det; + + point = {{x,y,z}}; + return true; + } + + return false; +} + +void +SegmentLowerEnvelope::adjust_piecewise_linear_positions(Segment_Vector & segments, const std::vector & pos, const std::vector> & phi) +{ + if (pos.size() > 2) + { + // Even for piecewise edges, the structure of the phases must be no more complicated than that + // given by the linear version of the edge. So adjust the linear locations and then remove + // the degenerate, inverted segements. + + // Use piecewise approximation to find actual transition locations + for(Segment_Vector::iterator it = segments.begin(); it != segments.end()-1; ++it) + { + LS_Segment & cur = *it; + LS_Segment & next = *(it+1); + if (cur.ls_index() == next.ls_index()) continue; + InterfaceID iface(cur.ls_index(), next.ls_index()); + + const double crossing_pos = (find_crossing_position_and_sign(iface, pos, phi)).first; + ThrowRequire(crossing_pos >= 0.); + cur = LS_Segment(cur.left_endpoint(), crossing_pos, cur.ls_index()); + next = LS_Segment(crossing_pos, next.right_endpoint(), next.ls_index()); + } + + // Now remove inverted segments + if (segments.size() > 2) + { + for(Segment_Vector::iterator it = segments.begin()+1; it != segments.end()-1; ++it) + { + LS_Segment & prev = *(it-1); + LS_Segment & cur = *it; + LS_Segment & next = *(it+1); + + if (cur.right_endpoint() < cur.left_endpoint()) + { + InterfaceID iface(prev.ls_index(), next.ls_index()); + + const double crossing_pos = (find_crossing_position_and_sign(iface, pos, phi)).first; + ThrowRequire(crossing_pos >= 0.); + prev = LS_Segment(prev.left_endpoint(), cur.right_endpoint(), prev.ls_index()); + cur = LS_Segment(cur.right_endpoint(), crossing_pos, prev.ls_index()); + next = LS_Segment(crossing_pos, next.right_endpoint(), next.ls_index()); + } + } + } + } +} + +void +SegmentLowerEnvelope::collapse_identical_segments(Segment_Vector & segments) +{ + if (segments.size() > 1) + { + Segment_Vector uncollapsed; + uncollapsed.swap(segments); + segments.push_back(uncollapsed.front()); + for(Segment_Vector::const_iterator it = uncollapsed.begin()+1; it != uncollapsed.end(); ++it) + { + const LS_Segment & prev = *(it-1); + const LS_Segment & curr = *it; + if (prev.ls_index() == curr.ls_index()) + { + segments.back() = LS_Segment(segments.back().left_endpoint(), curr.right_endpoint(), prev.ls_index()); + } + else + { + segments.push_back(curr); + } + } + } +} + +void +SegmentLowerEnvelope::add_segment(Segment_Vector & segments, + const double x0, const std::vector & phi0, const int min0, + const double x1, const std::vector & phi1, const int min1) +{ + if (min0 == min1) + { + const LS_Segment segment(x0, x1, min0); + segments.push_back(segment); + return; + } + else + { + InterfaceID key(min0, min1); + const double ls0 = (phi0[key.first_ls()]-phi0[key.second_ls()]); // ls >= 0 for phi2 >= phi1 + const double ls1 = (phi1[key.first_ls()]-phi1[key.second_ls()]); + ThrowRequire(ls0*ls1 <= 0.); + const double cut = ls0 / (ls0 - ls1); + + { + const double x_cut = (1.-cut)*x0 + cut*x1; + ThrowAssert(phi0.size() == phi1.size()); + std::vector phiAtCut(phi0.size()); + for (unsigned i=0; i 1.-tol; + + if (isInfinitesmalCutAt0) + { + if (segments.empty()) + segments.emplace_back(0., 0., min0); + if (minCutMatchesEndPoint) + segments.emplace_back(x0, x1, min1); + else + add_segment(segments, x0, phiAtCut, minAtCut, x1, phi1, min1); + } + else if (isInfinitesmalCutAt1) + { + if (minCutMatchesEndPoint) + segments.emplace_back(x0, x1, min0); + else + add_segment(segments, x0, phi0, min0, x1, phiAtCut, minAtCut); + if (x1 == 1.) + { + if (segments.back().left_endpoint() == 1.) + segments.pop_back(); + segments.emplace_back(1., 1., min1); + } + } + else + { + if (minCutMatchesEndPoint) + segments.emplace_back(x0, x_cut, min0); + else + add_segment(segments, x0, phi0, min0, x_cut, phiAtCut, minAtCut); + + if (minCutMatchesEndPoint) + segments.emplace_back(x_cut, x1, min1); + else + add_segment(segments, x_cut, phiAtCut, minAtCut, x1, phi1, min1); + } + } + } +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_LowerEnvelope.hpp b/packages/krino/krino/krino_lib/Akri_LowerEnvelope.hpp new file mode 100644 index 000000000000..88cd8f6e6210 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_LowerEnvelope.hpp @@ -0,0 +1,120 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_LowerEnvelope_h +#define Akri_LowerEnvelope_h + +#include +#include + +#include + +namespace krino { + +class LS_Segment +{ +public: + LS_Segment(const double xL, const double xR, int id); + + double length() const { return x_right - x_left; } + int ls_index() const { return LS_index; } + double left_endpoint() const { return x_left; } + double right_endpoint() const { return x_right; } + + friend bool operator==(const LS_Segment & lhs, const LS_Segment & rhs); + friend std::ostream & operator << (std::ostream &os, const LS_Segment & segment); + +private: + double x_left; + double x_right; + int LS_index; +}; +typedef std::vector Segment_Vector; + +inline std::ostream & operator << (std::ostream &os, const LS_Segment & segment) +{ + os << "segment LS id = " << segment.LS_index + << " left point = " << segment.x_left + << " right point = " << segment.x_right; + return os; +} + +inline std::ostream & operator << (std::ostream &os, const Segment_Vector & segmentVec) +{ + os << "Segments: "; + for (auto && segment : segmentVec) + os << "{" << segment << "} "; + return os; +} + +std::pair +find_crossing_position_and_sign(const InterfaceID key, const std::vector & pos, const std::vector> & phi); + +bool find_lower_envelope_crossing_point(const std::array,2> & phi, double & x); + +bool find_lower_envelope_crossing_point(const std::array,3> & phi, std::array & point); + +bool find_lower_envelope_crossing_point(const std::array,4> & phi, std::array & point); + +int get_min(const std::vector & phi); + +class SegmentLowerEnvelope +{ +public: + // Must be larger than machine epsilon, but puts limit on smallest effective snap tolerance + static constexpr double MinSize() {return 1.e-12;}; + + // linear (2 points) + static Segment_Vector find_lower_envelope(const std::vector & phi0, + const std::vector & phi1) { SegmentLowerEnvelope env(0., phi0, 1., phi1); return env.get_segments(); } + static Segment_Vector find_lower_envelope(const double x0, const std::vector & phi0, + const double x1, const std::vector & phi1) { SegmentLowerEnvelope env(x0, phi0, x1, phi1); return env.get_segments(); } + // piecewise linear (>2 points) + static Segment_Vector find_lower_envelope(const std::vector & pos, const std::vector> & phi) + { SegmentLowerEnvelope env(pos,phi); return env.get_segments(); } + + SegmentLowerEnvelope(const std::vector & phi0, const std::vector & phi1) : SegmentLowerEnvelope(0., phi0, 1., phi1) {} + SegmentLowerEnvelope(const double x0, const std::vector & phi0, + const double x1, const std::vector & phi1) + { + add_segment(mySegments, x0, phi0, x1, phi1); + collapse_identical_segments(mySegments); + } + SegmentLowerEnvelope(const std::vector & pos, const std::vector> & phi) + { + ThrowRequire(pos.size() == phi.size() && pos.size() > 1); + add_segment(mySegments, pos.front(), phi.front(), pos.back(), phi.back()); + collapse_identical_segments(mySegments); + adjust_piecewise_linear_positions(mySegments, pos, phi); + } + const Segment_Vector & get_segments() const { return mySegments; } + +private: + static void add_segment(Segment_Vector & segments, + const double x0, const std::vector & phi0, + const double x1, const std::vector & phi1) + { + const int min0 = get_min(phi0); + const int min1 = get_min(phi1); + return add_segment(segments, x0, phi0, min0, x1, phi1, min1); + } + + static void add_segment(Segment_Vector & segments, + const double x0, const std::vector & phi0, const int min0, + const double x1, const std::vector & phi1, const int min1); + + static void collapse_identical_segments(Segment_Vector & segments); + + static void adjust_piecewise_linear_positions(Segment_Vector & segments, const std::vector & pos, const std::vector> & phi); + + Segment_Vector mySegments; +}; + +} // namespace krino + +#endif // Akri_LowerEnvelope_h diff --git a/packages/krino/krino/krino_lib/Akri_MasterElementDeterminer.cpp b/packages/krino/krino/krino_lib/Akri_MasterElementDeterminer.cpp new file mode 100644 index 000000000000..dad407903149 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MasterElementDeterminer.cpp @@ -0,0 +1,105 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include + +#include +#include +#include +#include + +namespace krino { + +const MasterElement& +MasterElementDeterminer::getMasterElement(stk::mesh::Bucket & bucket, FieldRef field) +{ + stk::topology field_topology = get_field_topology(bucket, field); + return MasterElementDeterminer::getMasterElement(field_topology); +} + +stk::topology +MasterElementDeterminer::get_field_topology(const stk::mesh::Bucket & b, const FieldRef field) +{ + // As an optimization, assume there is only 1 field topology active for a given mesh topology and field + typedef std::map, stk::topology> FieldAndMeshTopoToFieldTopoMap; + static FieldAndMeshTopoToFieldTopoMap field_topo_map; + + const stk::topology mesh_topology = b.topology(); + const unsigned field_ordinal = field.field().mesh_meta_data_ordinal(); + FieldAndMeshTopoToFieldTopoMap::iterator it = field_topo_map.find(std::make_pair(field_ordinal, mesh_topology)); + if (it != field_topo_map.end()) + { + return it->second; + } + + stk::mesh::MetaData & meta = field.field().mesh_meta_data(); + stk::topology field_topology = AuxMetaData::get(meta).get_nodal_field_topology(field, b); + + field_topo_map.insert(FieldAndMeshTopoToFieldTopoMap::value_type(std::make_pair(field_ordinal, mesh_topology), field_topology)); + return field_topology; +} + +const MasterElement & +MasterElementDeterminer::getMasterElement(stk::topology t) +{ + static std::vector> all_master_elems(stk::topology::BEGIN_TOPOLOGY + stk::topology::NUM_TOPOLOGIES); + std::unique_ptr & master_elem = all_master_elems[t()]; + if (nullptr == master_elem.get()) + { + std::unique_ptr basis; + switch(t()) + { + case stk::topology::LINE_2: + basis = std::make_unique(); + break; + case stk::topology::LINE_3: + basis = std::make_unique(); + break; + case stk::topology::TRI_3: + case stk::topology::TRI_3_2D: + basis = std::make_unique(); + break; + case stk::topology::TRI_6: + case stk::topology::TRI_6_2D: + basis = std::make_unique(); + break; + case stk::topology::QUAD_4: + case stk::topology::QUAD_4_2D: + basis = std::make_unique(); + break; + case stk::topology::QUAD_9: + case stk::topology::QUAD_9_2D: + basis = std::make_unique(); + break; + case stk::topology::TET_4: + basis = std::make_unique(); + break; + case stk::topology::TET_10: + basis = std::make_unique(); + break; + case stk::topology::HEX_8: + basis = std::make_unique(); + break; + case stk::topology::HEX_27: + basis = std::make_unique(); + break; + case stk::topology::WEDGE_6: + basis = std::make_unique(); + break; + default: + ThrowRuntimeError("Element topology not found in MasterElementDeterminer::getMasterElement: " << t.name()); + break; + } + master_elem = std::make_unique(t, std::move(basis)); + } + return *master_elem; +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_MasterElementDeterminer.hpp b/packages/krino/krino/krino_lib/Akri_MasterElementDeterminer.hpp new file mode 100644 index 000000000000..af84aaea85f2 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MasterElementDeterminer.hpp @@ -0,0 +1,30 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_MasterElementDeterminer_h +#define Akri_MasterElementDeterminer_h + +#include + +namespace krino { class FieldRef; } +namespace stk { namespace mesh { class Bucket; } } +namespace stk { class topology; } + +namespace krino { + +class MasterElementDeterminer +{ +public: + static const MasterElement& getMasterElement(stk::mesh::Bucket & bucket, FieldRef field); + static const MasterElement& getMasterElement(stk::topology topology); + static stk::topology get_field_topology(const stk::mesh::Bucket & b, const FieldRef field); +}; + +} // end namespace krino + +#endif // Akri_MasterElementDeterminer_h diff --git a/packages/krino/krino/krino_lib/Akri_MathUtil.cpp b/packages/krino/krino/krino_lib/Akri_MathUtil.cpp new file mode 100644 index 000000000000..f2a7d53a2b6f --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MathUtil.cpp @@ -0,0 +1,233 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include + +namespace krino { + +Vector3d +get_parametric_coordinates_of_point(const std::vector & nodeCoords, const Vector3d & pt) +{ + if (nodeCoords.size() == 3) + { + const Vector3d relativeCoords1 = nodeCoords[1] - nodeCoords[0]; + const Vector3d relativeCoords2 = nodeCoords[2] - nodeCoords[0]; + const Vector3d relativeCoords = pt - nodeCoords[0]; + + const double a00 = relativeCoords1[0]; + const double a01 = relativeCoords2[0]; + const double a10 = relativeCoords1[1]; + const double a11 = relativeCoords2[1]; + const double b0 = relativeCoords[0]; + const double b1 = relativeCoords[1]; + const double det = a00*a11-a01*a10; + const double x = (b0*a11-b1*a01)/det; + const double y = (-b0*a10+b1*a00)/det; + return Vector3d(x,y,0.); + } + else + { + const Vector3d relativeCoords1 = nodeCoords[1] - nodeCoords[0]; + const Vector3d relativeCoords2 = nodeCoords[2] - nodeCoords[0]; + const Vector3d relativeCoords3 = nodeCoords[3] - nodeCoords[0]; + const Vector3d relativeCoords = pt - nodeCoords[0]; + const double a00 = relativeCoords1[0]; + const double a01 = relativeCoords2[0]; + const double a02 = relativeCoords3[0]; + const double a10 = relativeCoords1[1]; + const double a11 = relativeCoords2[1]; + const double a12 = relativeCoords3[1]; + const double a20 = relativeCoords1[2]; + const double a21 = relativeCoords2[2]; + const double a22 = relativeCoords3[2]; + const double b0 = relativeCoords[0]; + const double b1 = relativeCoords[1]; + const double b2 = relativeCoords[2]; + const double det = a00*(a22*a11-a21*a12)-a10*(a22*a01-a21*a02)+a20*(a12*a01-a11*a02); + const double x =( b0*(a22*a11-a21*a12)-b1*(a22*a01-a21*a02)+b2*(a12*a01-a11*a02))/det; + const double y =(-b0*(a22*a10-a20*a12)+b1*(a22*a00-a20*a02)-b2*(a12*a00-a10*a02))/det; + const double z =( b0*(a21*a10-a20*a11)-b1*(a21*a00-a20*a01)+b2*(a11*a00-a10*a01))/det; + return Vector3d(x,y,z); + } +} + +std::pair find_root( const std::function & f, + const double xa, + const double xb, + const double fa, + const double fb, + const unsigned maxIters, + const double xTol) +{ + boost::uintmax_t iterCount = maxIters; + auto tol_function = [&xTol](const double a, const double b) { return std::abs(b-a) <= xTol; }; + auto result = boost::math::tools::toms748_solve(f, xa, xb, fa, fb, tol_function, iterCount); + const bool success = iterCount < maxIters; + return {success, 0.5*(result.first+result.second)}; +} + +std::pair find_bracketed_root_newton_raphson( const std::function(const double)> & f, + double x, + double fx, + double dfx, + double xa, + double xb, + double fa, + double fb, + const unsigned maxIters, + const double fTol) +{ + unsigned iter = 0; + while (iter++ < maxIters) + { + if (dfx == 0.) + { + x = 0.5*(xa+xb); // zero slope so use bisection + } + else + { + x -= fx / dfx; + if (x < xa || x > xb) + x = 0.5*(xa+xb); // Newton-Raphson step went out of bounds so use bisection + } + + std::tie(fx, dfx) = f(x); + + if (std::abs(fx) <= fTol) + return {true, x}; + + if (fx*fb < 0.) + { + xa = x; + fa = fx; + } + else + { + xb = x; + fb = fx; + } + + const double xTol = 4*std::numeric_limits::epsilon()*(fabs(xa)+fabs(xb)); + if (xb-xa < xTol) + return {false, x}; + } + return {true, x}; +} + +void attempt_to_bracket_root_newton_raphson( const std::function(const double)> & f, + const double guess, + double & x, + double & fx, + double & dfx, + double & xa, + double & xb, + double & fa, + double & fb, + bool & solnIsConvergedAtX, + bool & solnIsBracketed, + const unsigned maxIters, + const double fTol) +{ + x = guess; + std::tie(fx,dfx) = f(x); + + if (std::abs(fx) <= fTol) + { + solnIsConvergedAtX = true; + solnIsBracketed = false; + return; + } + + xa = x; + xb = x; + fa = fx; + fb = fx; + + unsigned iter = 0; + while (iter++ < maxIters) + { + if (dfx == 0.) + { + // FAILED: zero slope + solnIsConvergedAtX = false; + solnIsBracketed = false; + return; + } + + xa = x; + fa = fx; + + x -= fx / dfx; + std::tie(fx,dfx) = f(x); + + if (std::abs(fx) <= fTol) + { + solnIsConvergedAtX = true; + solnIsBracketed = false; + return; + } + + if (fa*fx > 0. && std::abs(fx) > std::abs(fa)) + { + // Jacobian is poor, try line search + const double tau = 0.5; + const int maxLineSearchIters = 5; + unsigned lineSearchIters = 0; + while (lineSearchIters++ < maxLineSearchIters && fa*fx > 0. && std::abs(fx) > std::abs(fa)) + { + x = xa + tau*(x-xa); + std::tie(fx,dfx) = f(x); + } + } + + if (fa*fx < 0.) + { + solnIsConvergedAtX = false; + solnIsBracketed = true; + xb = x; + fb = fx; + if (xa > xb) + { + std::swap(xa,xb); + std::swap(fa,fb); + } + return; + } + } + + //FAILED: did not bracket root + solnIsConvergedAtX = false; + solnIsBracketed = false; +} + +std::pair find_root_newton_raphson( const std::function(const double)> & f, + const double guess, + const unsigned maxIters, + const double fTol) +{ + double x, fx, dfx, xa, xb, fa, fb; + bool solnIsConvergedAtX, solnIsBracketed; + attempt_to_bracket_root_newton_raphson(f, guess, x, fx, dfx, xa, xb, fa, fb, solnIsConvergedAtX, solnIsBracketed, maxIters, fTol); + + if (solnIsConvergedAtX) + return {true, x}; + + if (!solnIsBracketed) + return {false, guess}; + + return find_bracketed_root_newton_raphson(f, x, fx, dfx, xa, xb, fa, fb, maxIters, fTol); +} + +} + + diff --git a/packages/krino/krino/krino_lib/Akri_MathUtil.hpp b/packages/krino/krino/krino_lib/Akri_MathUtil.hpp new file mode 100644 index 000000000000..6741d5bd9eac --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MathUtil.hpp @@ -0,0 +1,35 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_MATHUTIL_H_ +#define KRINO_INCLUDE_AKRI_MATHUTIL_H_ +#include +#include +#include + +namespace krino { + +Vector3d get_parametric_coordinates_of_point(const std::vector & nodeCoords, const Vector3d & pt); + +std::pair find_root( const std::function & f, + const double xa, + const double xb, + const double fa, + const double fb, + const unsigned maxIters = 100, + const double tol = 1.e-4); + +std::pair find_root_newton_raphson( const std::function(const double)> & f, + const double guess, + const unsigned maxIters = 100, + const double fTol = 1.e-4); + +} + + +#endif /* KRINO_INCLUDE_AKRI_MATHUTIL_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_MeshClone.cpp b/packages/krino/krino/krino_lib/Akri_MeshClone.cpp new file mode 100644 index 000000000000..49d9abaea644 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MeshClone.cpp @@ -0,0 +1,629 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace krino{ + +bool +MeshClone::stash_or_restore_mesh(stk::mesh::BulkData & mesh, const unsigned step_count) +{ /* %TRACE[ON]% */ Trace trace__("krino::MeshClone::stash_or_restore_mesh(tftk::mesh::Mesh &mesh)"); /* %TRACE% */ + + const auto empty_function = []() {}; + return stash_or_restore_mesh(mesh, step_count, empty_function, empty_function); +} + +bool +MeshClone::stash_or_restore_mesh(stk::mesh::BulkData & mesh, const unsigned step_count, + const std::function & notify_of_pre_mesh_modification, + const std::function & notify_of_post_mesh_modification) +{ /* %TRACE[ON]% */ Trace trace__("krino::MeshClone::stash_or_restore_mesh(tftk::mesh::Mesh &mesh)"); /* %TRACE% */ + bool restored_mesh = false; + MeshClone * clone = const_cast(mesh.mesh_meta_data().get_attribute()); + if (!clone) + { + clone = new MeshClone(mesh, sierra::Diag::sierraTimer(), step_count); + mesh.mesh_meta_data().declare_attribute_with_delete(clone); + } + else + { + if (step_count > clone->my_step_count) + { + if (!clone->mesh_is_up_to_date()) + { + clone->update(step_count); + } + } + else if (!clone->mesh_is_up_to_date()) + { + notify_of_pre_mesh_modification(); + clone->restore(step_count); + notify_of_post_mesh_modification(); + restored_mesh = true; + } + } + return restored_mesh; +} + +bool +MeshClone::exists(const stk::mesh::BulkData & mesh) +{ /* %TRACE[ON]% */ Trace trace__("krino::MeshClone::exists(stk::mesh::BulkData & mesh)"); /* %TRACE% */ + MeshClone * clone = const_cast(mesh.mesh_meta_data().get_attribute()); + return (clone != nullptr); +} + +MeshClone & +MeshClone::get(const stk::mesh::BulkData & mesh) +{ /* %TRACE[ON]% */ Trace trace__("krino::MeshClone::update_cloned_fields(stk::mesh::BulkData & mesh)"); /* %TRACE% */ + MeshClone * clone = const_cast(mesh.mesh_meta_data().get_attribute()); + ThrowRequireMsg(clone != nullptr, "Could not find MeshClone."); + return *clone; +} + +MeshClone::MeshClone( stk::mesh::BulkData & orig_mesh, stk::diag::Timer parent_timer, const unsigned step_count ) +: my_orig_mesh(&orig_mesh), + my_timer("Clone mesh", parent_timer), + my_step_count(step_count), + my_synchronized_count(orig_mesh.synchronized_count()) +{ + stk::diag::TimeBlock timer__(my_timer); + const stk::mesh::MetaData & in_meta = my_orig_mesh->mesh_meta_data(); + my_meta = std::make_unique(); + clone_meta_data_parts_and_fields(in_meta, *my_meta); + + my_mesh = std::make_unique(*my_meta, + my_orig_mesh->parallel(), + stk::mesh::BulkData::NO_AUTO_AURA +#ifdef SIERRA_MIGRATION + ,my_orig_mesh->add_fmwk_data() +#endif + ); + + my_meta->commit(); + + my_mesh->modification_begin(); + clone_bulk_data_entities(*my_orig_mesh, *my_mesh, false); + my_mesh->modification_end(); + + copy_field_data(*my_orig_mesh, *my_mesh); +} + +void MeshClone::update(const unsigned step_count) +{ /* %TRACE[ON]% */ Trace trace__("krino::MeshClone::update(const unsigned step_count)"); /* %TRACE% */ + stk::diag::TimeBlock timer__(my_timer); + + const stk::mesh::BulkData & in_mesh = *my_orig_mesh; + stk::mesh::BulkData & out_mesh = *my_mesh; + + clone_mesh(in_mesh, out_mesh, false); + + my_step_count = step_count; + mark_mesh_as_up_to_date(); +} + +void MeshClone::restore(const unsigned step_count) +{ /* %TRACE[ON]% */ Trace trace__("krino::MeshClone::restore(const unsigned step_count)"); /* %TRACE% */ + stk::diag::TimeBlock timer__(my_timer); + krinolog << "Restoring mesh from clone." << stk::diag::dendl; + + const stk::mesh::BulkData & in_mesh = *my_mesh; + stk::mesh::BulkData & out_mesh = *my_orig_mesh; + + clone_mesh(in_mesh, out_mesh, false); + + ThrowRequire(step_count == my_step_count); + mark_mesh_as_up_to_date(); +} + +void MeshClone::clone_mesh(const stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh, const bool full_overwrite) +{ /* %TRACE[ON]% */ Trace trace__("krino::MeshClone::clone_mesh(const stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh, const bool full_overwrite)"); /* %TRACE% */ + if (full_overwrite) + { + // Ugly, but legal and effective. + stk::mesh::MetaData & out_meta = out_mesh.mesh_meta_data(); + out_mesh.~BulkData(); + + const stk::mesh::BulkData::AutomaticAuraOption aura_option = + in_mesh.is_automatic_aura_on() ? + stk::mesh::BulkData::AUTO_AURA : + stk::mesh::BulkData::NO_AUTO_AURA; + + new (&out_mesh) stk::mesh::BulkData(out_meta, + in_mesh.parallel(), + aura_option +#ifdef SIERRA_MIGRATION + ,in_mesh.add_fmwk_data() +#endif + ); + + out_mesh.modification_begin(); + clone_bulk_data_entities(in_mesh, out_mesh, false); + out_mesh.modification_end(); + } + else + { + // I don't think you can start with clone_parallel because you can't know where something goes unless you know about it locally. + // Therefore I don't think that creation can go before deletion because you can't know if it already exists. + // Best to just delete everything wrong and then rebuild. + ThrowRequireMsg(out_mesh.modification_begin(), "MeshClone::restore must not be called within a modification cycle."); + delete_extraneous_entities(in_mesh, out_mesh); + // Occasionally, there is an issue with deleting and recreating the same entity within the same modification cycle, + // so we need to end here and begin again for the creation. + out_mesh.modification_end(); + + //Delete graph because the following mod cycle creates elements and faces in the same mode cycle. + out_mesh.delete_face_adjacent_element_graph(); + + out_mesh.modification_begin(); + clone_bulk_data_entities(in_mesh, out_mesh, true); + out_mesh.modification_end(); + } + + copy_field_data(in_mesh, out_mesh); +} + + +void +MeshClone::clone_meta_data_parts_and_fields(const stk::mesh::MetaData & in_meta, stk::mesh::MetaData & out_meta) +{ + /* %TRACE[ON]% */ Trace trace__("void MeshClone::clone_meta_data(stk::mesh::MetaData & in_meta)"); /* %TRACE% */ + + // This is pretty nasty. We want the part.mesh_meta_data_ordinal to be the same for the in_meta and out_meta. + // To accomplish this, we must be careful about the order of the part creation. + // Specifically, we must clone the parts that were created before in_meta.initialize() were called, then + // call out_meta.initialize(), then clone the rest. + + const stk::mesh::PartVector & in_parts = in_meta.get_parts(); + + unsigned ipart = 0; + bool more_to_do = ipart < in_parts.size(); + while (more_to_do) + { + stk::mesh::Part * in_part = in_parts[ipart++]; + if (stk::mesh::is_topology_root_part(*in_part)) + { + more_to_do = false; + } + else + { + more_to_do = ipart < in_parts.size(); + ThrowRequire(in_part->primary_entity_rank() == stk::topology::INVALID_RANK); + stk::mesh::Part & out_part = out_meta.declare_part(in_part->name()); + ThrowRequire(out_part.mesh_meta_data_ordinal() == in_part->mesh_meta_data_ordinal()); + } + } + + out_meta.initialize(in_meta.spatial_dimension(), in_meta.entity_rank_names()); + + for ( auto&& in_part : in_parts ) + { + stk::mesh::Part & out_part = + (in_part->primary_entity_rank() == stk::topology::INVALID_RANK) ? + out_meta.declare_part(in_part->name()) : + out_meta.declare_part(in_part->name(), in_part->primary_entity_rank(), in_part->force_no_induce()); + ThrowRequire(out_part.mesh_meta_data_ordinal() == in_part->mesh_meta_data_ordinal()); + if (stk::io::is_part_io_part(*in_part)) + { + stk::io::put_io_part_attribute(out_part); + } + } + + for ( auto&& in_part : in_parts ) + { + stk::mesh::Part & out_part = out_meta.get_part(in_part->mesh_meta_data_ordinal()); + const stk::mesh::PartVector & in_subsets = in_part->subsets(); + for (auto && in_subset : in_subsets) + { + stk::mesh::Part & out_subset = out_meta.get_part(in_subset->mesh_meta_data_ordinal()); + out_meta.declare_part_subset(out_part, out_subset); + } + } + + const stk::mesh::FieldVector & in_fields = in_meta.get_fields(); + for ( auto&& in_field : in_fields ) + { + if (in_field->state() == stk::mesh::StateNone) + { + stk::topology::rank_t entity_rank = static_cast(in_field->entity_rank()); + stk::mesh::FieldBase * out_field = out_meta.declare_field_base(in_field->name(), entity_rank, in_field->data_traits(), in_field->field_array_rank(), in_field->dimension_tags(), in_field->number_of_states()); + + for ( auto&& in_restriction : in_field->restrictions() ) + { + const stk::mesh::Selector out_selector = MeshClone::translate_selector(in_restriction.selector(), out_meta); + + out_meta.declare_field_restriction( *out_field, out_selector, in_restriction.num_scalars_per_entity(), in_restriction.dimension() ); + } + } + } +} + +void +MeshClone::delete_extraneous_entities(const stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh) +{ + /* %TRACE[ON]% */ Trace trace__("void MeshClone::delete_extraneous_entities(stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh)"); /* %TRACE% */ + + std::vector entities; + std::vector relatives; + std::vector relative_ordinals; + std::vector in_mesh_comm_procs; + std::vector out_mesh_comm_procs; + + const stk::mesh::EntityRank highest_entity_rank = static_cast(in_mesh.mesh_meta_data().entity_rank_count()-1); + for (stk::mesh::EntityRank entity_rank = highest_entity_rank; entity_rank >= stk::topology::NODE_RANK; --entity_rank) + { + stk::mesh::get_entities( out_mesh, entity_rank, entities ); + + for (auto&& out_entity : entities) + { + // Delete all entities for which any of the following are true: + // 1. There is no corresponding entity in in_mesh + // 2. The parallel owner changed. + // 3. The comm procs or comm shared procs differ. (Ugh. This is to work around strange corner case issues.) + // 4. The entity has invalid nodes (due to the nodes violating 1 or 2). + + stk::mesh::Entity in_entity = get_entity_on_other_mesh(out_mesh, out_entity, in_mesh); + bool delete_entity = !in_mesh.is_valid(in_entity); + + if (!delete_entity) + { + delete_entity = + in_mesh.parallel_owner_rank(in_entity) != out_mesh.parallel_owner_rank(out_entity); + } + + if (!delete_entity) + { + in_mesh.comm_procs(in_mesh.entity_key(in_entity), in_mesh_comm_procs); + out_mesh.comm_procs(out_mesh.entity_key(out_entity), out_mesh_comm_procs); + delete_entity = in_mesh_comm_procs != out_mesh_comm_procs; + } + + if (!delete_entity) + { + in_mesh.comm_shared_procs(in_entity, in_mesh_comm_procs); + out_mesh.comm_shared_procs(out_entity, out_mesh_comm_procs); + delete_entity = in_mesh_comm_procs != out_mesh_comm_procs; + } + + if (delete_entity) + { + ThrowRequireMsg(disconnect_and_destroy_entity(out_mesh, out_entity), "Could not destroy entity " << out_mesh.entity_key(out_entity) << debug_entity(out_mesh, out_entity)); + } + } + } + + for (stk::mesh::EntityRank entity_rank = stk::topology::ELEMENT_RANK; entity_rank > stk::topology::NODE_RANK; --entity_rank) + { + stk::mesh::get_entities( out_mesh, entity_rank, entities ); + + for (auto&& out_entity : entities) + { + const unsigned num_nodes = out_mesh.bucket(out_entity).topology().num_nodes(); + if (out_mesh.count_valid_connectivity(out_entity, stk::topology::NODE_RANK) != num_nodes) + { + ThrowRequireMsg(disconnect_and_destroy_entity(out_mesh, out_entity), "Could not destroy entity " << out_mesh.entity_key(out_entity)); + } + } + } +} + +void +MeshClone::delete_all_entities(stk::mesh::BulkData & mesh) +{ + /* %TRACE[ON]% */ Trace trace__("void MeshClone::delete_extraneous_entities(stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh)"); /* %TRACE% */ + + std::vector entities; + + const stk::mesh::EntityRank highest_entity_rank = static_cast(mesh.mesh_meta_data().entity_rank_count()-1); + for (stk::mesh::EntityRank entity_rank = highest_entity_rank; entity_rank >= stk::topology::NODE_RANK; --entity_rank) + { + stk::mesh::get_entities( mesh, entity_rank, entities ); + + for (auto && entity : entities) + { + ThrowRequireMsg(mesh.destroy_entity(entity), "Could not destroy entity " << mesh.entity_key(entity)); + } + } +} + +void +MeshClone::clone_bulk_data_entities(const stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh, const bool search_for_existing) +{ + /* %TRACE[ON]% */ Trace trace__("void MeshClone::clone_bulk_data(stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh)"); /* %TRACE% */ + + const bool debug = false; + + const stk::mesh::MetaData & in_meta = in_mesh.mesh_meta_data(); + const stk::mesh::MetaData & out_meta = out_mesh.mesh_meta_data(); + + std::vector sharing; + + const stk::mesh::EntityRank highest_entity_rank = static_cast(in_meta.entity_rank_count()-1); + for (stk::mesh::EntityRank entity_rank = stk::topology::NODE_RANK; entity_rank <= highest_entity_rank; ++entity_rank) + { + stk::mesh::Selector not_ghost = in_meta.locally_owned_part() | in_meta.globally_shared_part(); + const stk::mesh::BucketVector & buckets = in_mesh.get_buckets(entity_rank, not_ghost); + + for ( auto&& bucket_ptr : buckets ) + { + const stk::mesh::Bucket & b = *bucket_ptr; + + stk::mesh::PartVector in_bucket_parts, out_bucket_parts; + get_bucket_parts(b,in_bucket_parts); + + translate_parts(in_bucket_parts, out_meta, out_bucket_parts); + + const bool bucket_is_locally_owned = b.member(in_meta.locally_owned_part()); + + + const int length = b.size(); + for (int i = 0; i < length; ++i) + { + stk::mesh::Entity in_entity = b[i]; + + stk::mesh::Entity out_entity; + if (search_for_existing) + { + out_entity = out_mesh.get_entity( entity_rank, in_mesh.identifier(in_entity) ); + if (out_mesh.is_valid(out_entity) && + out_mesh.bucket(out_entity).member(out_meta.locally_owned_part())) + { + // make sure parts are right + stk::mesh::PartVector current_parts; + get_bucket_parts(out_mesh.bucket(out_entity), current_parts); + out_mesh.change_entity_parts(out_entity, out_bucket_parts, current_parts); + } + } + + // Unfortunately, there is a subtle bug that may be present in the input mesh that can cause a face to be present on + // this processor even when there are no locally owned elements using that face. We will skip these faces. + if (!bucket_is_locally_owned && entity_rank == in_meta.side_rank()) + { + bool found_locally_owned_elem = false; + const unsigned num_elems = in_mesh.num_elements(in_entity); + const stk::mesh::Entity* elems = in_mesh.begin_elements(in_entity); + for (unsigned ielem=0; ielem 1) + { + in_mesh.comm_shared_procs(in_entity,sharing); + for ( size_t k = 0 ; k < sharing.size() ; ++k ) + { + out_mesh.add_node_sharing(out_entity, sharing[k]); + } + } + + for (stk::mesh::EntityRank relative_rank = stk::topology::NODE_RANK; relative_rank < entity_rank; ++relative_rank) + { + const unsigned num_relatives = in_mesh.num_connectivity(in_entity, relative_rank); + const stk::mesh::Entity* in_relatives = in_mesh.begin(in_entity, relative_rank); + const stk::mesh::ConnectivityOrdinal * relative_ordinals = in_mesh.begin_ordinals(in_entity, relative_rank); + const stk::mesh::Permutation * relative_permutations = in_mesh.begin_permutations(in_entity, relative_rank); + + for (unsigned relative_index=0; relative_index( stk::mesh::field_data( out_field, b ) ); + + for (unsigned ib=0; ib( stk::mesh::field_data( in_field, in_entity ) ); + for ( unsigned i = 0; i < out_length; i++ ) out_data[i] = in_data[i]; + } + } +} + +void +MeshClone::copy_field_data(const stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh) +{ + /* %TRACE[ON]% */ Trace trace__("void MeshClone::copy_field_data(stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh)"); /* %TRACE% */ + + const stk::mesh::MetaData & in_meta = in_mesh.mesh_meta_data(); + stk::mesh::MetaData & out_meta = out_mesh.mesh_meta_data(); + + const stk::mesh::FieldVector & in_fields = in_meta.get_fields(); + const stk::mesh::FieldVector & out_fields = out_meta.get_fields(); + ThrowAssert(in_fields.size() == out_fields.size()); + + const bool out_mesh_aura_from_communication = out_mesh.is_automatic_aura_on() && !in_mesh.is_automatic_aura_on(); + + for ( unsigned field_index=0; field_index < in_fields.size(); ++field_index ) + { + const stk::mesh::FieldBase & in_field = *in_fields[field_index]; + const stk::mesh::FieldBase & out_field = *out_fields[field_index]; + copy_field_data(in_mesh, out_mesh, in_field, out_field, out_mesh_aura_from_communication); + } + + if (out_mesh_aura_from_communication) + { + const std::vector ghostings = out_mesh.ghostings(); + const std::vector const_fields(out_fields.begin(), out_fields.end()); + stk::mesh::communicate_field_data(*ghostings[stk::mesh::BulkData::AURA], const_fields); + } +} + +void +MeshClone::translate_parts(const stk::mesh::PartVector & in_parts, const stk::mesh::MetaData & out_meta, stk::mesh::PartVector & out_parts) +{ + out_parts.clear(); + for ( auto&& in_part : in_parts ) + { + stk::mesh::Part & out_part = out_meta.get_part(in_part->mesh_meta_data_ordinal()); + stk::mesh::insert(out_parts, out_part); + } +} + +stk::mesh::Selector +MeshClone::translate_selector(const stk::mesh::Selector & in_selector, const stk::mesh::MetaData & out_meta) +{ + if (in_selector == stk::mesh::Selector()) + { + return in_selector; + } + ThrowRequireMsg(in_selector.is_all_unions(), "Cannot translate selector " << in_selector); + stk::mesh::PartVector in_parts, out_parts; + in_selector.get_parts(in_parts); + translate_parts(in_parts, out_meta, out_parts); + return stk::mesh::selectUnion(out_parts); +} + +stk::mesh::Part * +MeshClone::translate_part(const stk::mesh::Part & in_part, const stk::mesh::MetaData & out_meta) +{ + return &out_meta.get_part(in_part.mesh_meta_data_ordinal()); +} + +void +MeshClone::get_bucket_parts(const stk::mesh::Bucket & bucket, stk::mesh::PartVector & parts) +{ + parts.clear(); + + stk::mesh::PartVector const& bucket_parts = bucket.supersets(); + for ( stk::mesh::PartVector::const_iterator ip = bucket_parts.begin(); ip != bucket_parts.end(); ++ip ) + { + stk::mesh::Part & bucket_part = **ip; + if (bucket_part.primary_entity_rank() != stk::topology::INVALID_RANK && bucket_part.primary_entity_rank() != bucket.entity_rank()) + { + continue; + } + if (stk::mesh::is_auto_declared_part(bucket_part) && !stk::mesh::is_topology_root_part(bucket_part)) + { + continue; + } + + parts.push_back(&bucket_part); + } + std::sort( parts.begin(), parts.end(), stk::mesh::PartLess() ); +} + +stk::mesh::Entity +MeshClone::get_entity_on_other_mesh(const stk::mesh::BulkData & mesh, stk::mesh::Entity entity, const stk::mesh::BulkData & other_mesh) +{ + stk::mesh::EntityRank entity_rank = mesh.entity_rank(entity); + stk::mesh::Entity other_entity = other_mesh.get_entity( entity_rank, mesh.identifier(entity) ); + + if (other_mesh.is_valid(other_entity) && entity_rank != stk::topology::NODE_RANK) + { + // check if nodes are the same + const unsigned num_entity_nodes = mesh.num_nodes(entity); + const unsigned num_other_entity_nodes = other_mesh.num_nodes(other_entity); + if (num_entity_nodes != num_other_entity_nodes) + { + return stk::mesh::Entity(); + } + const stk::mesh::Entity* entity_nodes = mesh.begin_nodes(entity); + const stk::mesh::Entity* other_entity_nodes = other_mesh.begin_nodes(other_entity); + for (unsigned n=0; n +#include +#include +#include + +namespace krino { + +class MeshClone { +public: + + MeshClone( stk::mesh::BulkData & orig_mesh, stk::diag::Timer parent_timer, const unsigned step_count = 0 ); + + static bool exists(const stk::mesh::BulkData & mesh); + static MeshClone & get(const stk::mesh::BulkData & mesh); + static bool stash_or_restore_mesh(stk::mesh::BulkData & mesh, const unsigned step_count); + static bool stash_or_restore_mesh(stk::mesh::BulkData & mesh, + const unsigned step_count, + const std::function & notify_of_pre_mesh_modification, + const std::function & notify_of_post_mesh_modification); + bool mesh_is_up_to_date() const {return my_orig_mesh->synchronized_count() == my_synchronized_count;} + void mark_mesh_as_up_to_date() {my_synchronized_count = my_orig_mesh->synchronized_count();} + void mark_mesh_as_out_of_date() {--my_synchronized_count;} + +private: + void update(const unsigned step_count = 0); + void restore(const unsigned step_count = 0); + + static void clone_meta_data_parts_and_fields(const stk::mesh::MetaData & in_meta, stk::mesh::MetaData & out_meta); + static void translate_parts(const stk::mesh::PartVector & in_parts, const stk::mesh::MetaData & out_meta, stk::mesh::PartVector & out_parts); + static stk::mesh::Part * translate_part(const stk::mesh::Part & in_part, const stk::mesh::MetaData & out_meta); + static stk::mesh::Selector translate_selector(const stk::mesh::Selector & in_selector, const stk::mesh::MetaData & out_meta); + static void get_bucket_parts(const stk::mesh::Bucket & bucket, stk::mesh::PartVector & parts); + + static void clone_mesh(const stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh, const bool full_overwrite=false); + static void clone_bulk_data(const stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh); + static void copy_field_data(const stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh, const stk::mesh::FieldBase & in_field, const stk::mesh::FieldBase & out_field, const bool out_mesh_aura_from_communication); + static void copy_field_data(const stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh); + + static void delete_extraneous_entities(const stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh); + static void delete_all_entities(stk::mesh::BulkData & mesh); + static void clone_bulk_data_entities(const stk::mesh::BulkData & in_mesh, stk::mesh::BulkData & out_mesh, const bool search_for_existing); + + static stk::mesh::Entity get_entity_on_other_mesh(const stk::mesh::BulkData & mesh, stk::mesh::Entity entity, const stk::mesh::BulkData & other_mesh); + + stk::mesh::BulkData* my_orig_mesh; + std::unique_ptr my_meta; + std::unique_ptr my_mesh; + + mutable stk::diag::Timer my_timer; + unsigned my_step_count; + size_t my_synchronized_count; +}; + +} // namespace krino + +#endif // Akri_MeshClone_h diff --git a/packages/krino/krino/krino_lib/Akri_MeshDiagnostics.cpp b/packages/krino/krino/krino_lib/Akri_MeshDiagnostics.cpp new file mode 100644 index 000000000000..e030340d2567 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MeshDiagnostics.cpp @@ -0,0 +1,70 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace krino{ + +void print_volume_or_surface_area(const stk::mesh::BulkData& mesh, const stk::mesh::EntityRank entity_rank, const stk::mesh::Selector & active_selector, const stk::mesh::PartVector & parts) +{ + double overall_sum = 0.; + + const std::string label = (entity_rank == stk::topology::ELEMENT_RANK) ? "Volume" : "Surface Area"; + + krinolog << stk::diag::dendl; + + for (auto && part : parts) + { + const stk::mesh::Selector selector = mesh.mesh_meta_data().locally_owned_part() & active_selector & *part; + const double local_part_sum = compute_volume_or_surface_area(mesh, entity_rank, selector); + double global_part_sum = 0.; + + stk::all_reduce_sum(mesh.parallel(), &local_part_sum, &global_part_sum, 1); + overall_sum += global_part_sum; + + krinolog << label << " for part " << part->name() << " = " << global_part_sum << stk::diag::dendl; + } + + krinolog << "Sum of " << label << " for all parts (which may overlap) = " << overall_sum << stk::diag::dendl; +} + +double +compute_volume_or_surface_area(const stk::mesh::BulkData& mesh, const stk::mesh::EntityRank entity_rank, const stk::mesh::Selector & selector) +{ + const FieldRef coords_field(mesh.mesh_meta_data().coordinate_field()); + const unsigned dim = mesh.mesh_meta_data().spatial_dimension(); + + std::vector coords; + std::vector intg_weights; + + double volume_or_area = 0.; + for ( auto && bucket : mesh.get_buckets( entity_rank, selector ) ) + { + const krino::MasterElement& master_elem = MasterElementDeterminer::getMasterElement(bucket->topology()); + + for ( auto && entity : *bucket ) + { + ElementObj::gather_nodal_field(mesh, entity, coords_field, coords, dim); + ElementObj::integration_weights( intg_weights, dim, coords, master_elem ); + + for ( double intg_wt : intg_weights ) volume_or_area += intg_wt; + } + } + return volume_or_area; +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_MeshDiagnostics.hpp b/packages/krino/krino/krino_lib/Akri_MeshDiagnostics.hpp new file mode 100644 index 000000000000..668e904d417f --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MeshDiagnostics.hpp @@ -0,0 +1,23 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_MeshDiagnostics_h +#define Akri_MeshDiagnostics_h + +#include +#include +#include + +namespace krino { + +void print_volume_or_surface_area(const stk::mesh::BulkData& mesh, const stk::mesh::EntityRank entity_rank, const stk::mesh::Selector & active_selector, const stk::mesh::PartVector & parts); +double compute_volume_or_surface_area(const stk::mesh::BulkData& mesh, const stk::mesh::EntityRank entity_rank, const stk::mesh::Selector & selector); + +} // namespace krino + +#endif // Akri_MeshDiagnostics_h diff --git a/packages/krino/krino/krino_lib/Akri_MeshHelpers.cpp b/packages/krino/krino/krino_lib/Akri_MeshHelpers.cpp new file mode 100644 index 000000000000..f701face6949 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MeshHelpers.cpp @@ -0,0 +1,2560 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include // Needed for all_reduce_max +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino{ + +template +class ContainerResizer +{ +public: + ContainerResizer(CONTAINER & container) : myContainer(container) {} + void resize(size_t size) { myContainer.resize(size); } +private: + CONTAINER & myContainer; +}; + +template +class ContainerResizer> +{ +public: + ContainerResizer(std::array & container) : myContainer(container) {} + void resize(size_t size) { ThrowRequire(SIZE == size); } +private: + std::array & myContainer; +}; + +template +void resize_container(CONTAINER & container, size_t size) +{ + ContainerResizer resizer(container); + resizer.resize(size); +} + +void fill_procs_owning_or_sharing_or_ghosting_node(const stk::mesh::BulkData& bulkData, stk::mesh::Entity node, std::vector & procsOwningSharingOrGhostingNode) +{ + ThrowAssert(bulkData.parallel_owner_rank(node)==bulkData.parallel_rank()); + bulkData.comm_procs(bulkData.entity_key(node), procsOwningSharingOrGhostingNode); + procsOwningSharingOrGhostingNode.push_back(bulkData.parallel_rank()); +} + +//-------------------------------------------------------------------------------- + +template +void fill_element_node_coordinates(const stk::mesh::BulkData & mesh, stk::mesh::Entity element, const FieldRef coordsField, CONTAINER & elementNodeCoords) +{ + StkMeshEntities elementNodes{mesh.begin_nodes(element), mesh.end_nodes(element)}; + resize_container(elementNodeCoords, elementNodes.size()); + for (size_t n=0; n(coordsField, elementNodes[n]); + for ( int d = 0; d < DIM; ++d ) + elementNodeCoords[n][d] = coords[d]; + } +} + +void fill_element_node_coordinates(const stk::mesh::BulkData & mesh, stk::mesh::Entity element, const FieldRef coordsField, const int dim, std::vector & elementNodeCoords) +{ + ThrowAssert(dim == 3 || dim == 2); + if (dim == 2) + fill_element_node_coordinates<2>(mesh, element, coordsField, elementNodeCoords); + else + fill_element_node_coordinates<3>(mesh, element, coordsField, elementNodeCoords); +} + +void fill_element_node_coordinates(const stk::mesh::BulkData & mesh, stk::mesh::Entity element, const FieldRef coordsField, std::vector & elementNodeCoords) +{ + const int dim = mesh.mesh_meta_data().spatial_dimension(); + elementNodeCoords.clear(); + for (auto node : StkMeshEntities{mesh.begin_nodes(element), mesh.end_nodes(element)}) + elementNodeCoords.emplace_back(field_data(coordsField, node), dim); +} + +static std::array gather_tet_coordinates(const stk::mesh::BulkData & mesh, stk::mesh::Entity element, const FieldRef coordsField) +{ + ThrowAssert(mesh.bucket(element).topology() == stk::topology::TETRAHEDRON_4); + std::array elementNodeCoords; + fill_element_node_coordinates<3>(mesh, element, coordsField, elementNodeCoords); + return elementNodeCoords; +} + +static std::array gather_tri_coordinates(const stk::mesh::BulkData & mesh, stk::mesh::Entity element, const FieldRef coordsField) +{ + ThrowAssert(mesh.bucket(element).topology() == stk::topology::TRIANGLE_3_2D); + std::array elementNodeCoords; + fill_element_node_coordinates<2>(mesh, element, coordsField, elementNodeCoords); + return elementNodeCoords; +} + +static double compute_tri_volume(const std::array & elementNodeCoords) +{ + return 0.5*(Cross(elementNodeCoords[1]-elementNodeCoords[0], elementNodeCoords[2]-elementNodeCoords[0]).length()); +} + +static double compute_tet_volume(const std::array & elementNodeCoords) +{ + return Dot(elementNodeCoords[3]-elementNodeCoords[0],Cross(elementNodeCoords[1]-elementNodeCoords[0], elementNodeCoords[2]-elementNodeCoords[0]))/6.0; +} + +static double compute_tri_or_tet_volume(const stk::mesh::BulkData & mesh, stk::mesh::Entity element, const FieldRef coordsField) +{ + stk::topology elemTopology = mesh.bucket(element).topology(); + + if (elemTopology == stk::topology::TETRAHEDRON_4) + { + const auto elementNodeCoords = gather_tet_coordinates(mesh, element, coordsField); + return compute_tet_volume(elementNodeCoords); + } + + ThrowRequireMsg(elemTopology == stk::topology::TRIANGLE_3_2D, "Topology " << elemTopology << " not supported in compute_tri_or_tet_volume."); + const auto elementNodeCoords = gather_tri_coordinates(mesh, element, coordsField); + return compute_tri_volume(elementNodeCoords); +} + +static void update_min_max_values(const double currentValue, double & minValue, double & maxValue) +{ + minValue = std::min(minValue, currentValue); + maxValue = std::max(maxValue, currentValue); +} + +template +void update_min_max_edge_lengths_squared(const CONTAINER & elementNodeCoords, double & minEdgeLengthSqr, double & maxEdgeLengthSqr) +{ + for ( size_t inode = 0; inode < elementNodeCoords.size(); ++inode ) + for ( size_t jnode = inode+1; jnode < elementNodeCoords.size(); ++jnode ) + update_min_max_values((elementNodeCoords[inode] - elementNodeCoords[jnode]).length_squared(), minEdgeLengthSqr, maxEdgeLengthSqr); +} + +template +void update_max_edge_lengths_squared(const CONTAINER & elementNodeCoords, double & maxEdgeLengthSqr) +{ + // This is a little strange for non-simplex elements since it goes from each node to every other node + for ( size_t inode = 0; inode < elementNodeCoords.size(); ++inode ) + for ( size_t jnode = inode+1; jnode < elementNodeCoords.size(); ++jnode ) + maxEdgeLengthSqr = std::max(maxEdgeLengthSqr, (elementNodeCoords[inode] - elementNodeCoords[jnode]).length_squared()); +} + +//-------------------------------------------------------------------------------- + +double +compute_maximum_element_size(stk::mesh::BulkData& mesh) +{ + const unsigned ndim = mesh.mesh_meta_data().spatial_dimension(); + double max_sqr_edge_length = 0.0; + + const FieldRef coordsField(mesh.mesh_meta_data().coordinate_field()); + + stk::mesh::Selector locally_owned_selector = mesh.mesh_meta_data().locally_owned_part(); + + const stk::mesh::BucketVector & buckets = mesh.get_buckets( stk::topology::ELEMENT_RANK, locally_owned_selector ); + std::vector elementNodeCoords; + + for ( auto && bucket : buckets ) + { + for ( auto && elem : *bucket ) + { + fill_element_node_coordinates(mesh, elem, coordsField, ndim, elementNodeCoords); + update_max_edge_lengths_squared(elementNodeCoords, max_sqr_edge_length); + } + } + + const double local_max = max_sqr_edge_length; + stk::all_reduce_max(mesh.parallel(), &local_max, &max_sqr_edge_length, 1); + + return std::sqrt(max_sqr_edge_length); +} + +//-------------------------------------------------------------------------------- + +void compute_element_quality(const stk::mesh::BulkData & mesh, double & minEdgeLength, double & maxEdgeLength, double & minVolume, double & maxVolume) +{ + double minEdgeLengthSqr = std::numeric_limits::max(); + double maxEdgeLengthSqr = -std::numeric_limits::max(); + minVolume = std::numeric_limits::max(); + maxVolume = std::numeric_limits::lowest(); + + const FieldRef coordsField(mesh.mesh_meta_data().coordinate_field()); + + stk::mesh::Selector locally_owned_selector = mesh.mesh_meta_data().locally_owned_part(); + + const stk::mesh::BucketVector & buckets = mesh.get_buckets( stk::topology::ELEMENT_RANK, locally_owned_selector ); + + for ( auto && bucket : buckets ) + { + stk::topology elem_topology = bucket->topology(); + const unsigned num_nodes = elem_topology.num_nodes(); + std::vector elem_node_coords(num_nodes); + + for ( auto && elem : *bucket ) + { + if (elem_topology == stk::topology::TETRAHEDRON_4) + { + const auto elementNodeCoords = gather_tet_coordinates(mesh, elem, coordsField); + update_min_max_values(compute_tet_volume(elementNodeCoords), minVolume, maxVolume); + update_min_max_edge_lengths_squared(elementNodeCoords, minEdgeLengthSqr, maxEdgeLengthSqr); + } + else if (elem_topology == stk::topology::TRIANGLE_3_2D) + { + const auto elementNodeCoords = gather_tri_coordinates(mesh, elem, coordsField); + update_min_max_values(compute_tri_volume(elementNodeCoords), minVolume, maxVolume); + update_min_max_edge_lengths_squared(elementNodeCoords, minEdgeLengthSqr, maxEdgeLengthSqr); + } + else + { + ThrowRuntimeError("Topology " << elem_topology << " not supported in compute_element_quality."); + } + } + } + + const double localMinEdgeLength = std::sqrt(minEdgeLengthSqr); + stk::all_reduce_min(mesh.parallel(), &localMinEdgeLength, &minEdgeLength, 1); + const double localMaxEdgeLength = std::sqrt(maxEdgeLengthSqr); + stk::all_reduce_max(mesh.parallel(), &localMaxEdgeLength, &maxEdgeLength, 1); + const double localMinVolume = minVolume; + stk::all_reduce_min(mesh.parallel(), &localMinVolume, &minVolume, 1); + const double localMaxVolume = maxVolume; + stk::all_reduce_max(mesh.parallel(), &localMaxVolume, &maxVolume, 1); +} + +static std::vector get_owned_nodes_with_nodal_volume_below_threshold(const stk::mesh::BulkData & mesh, const stk::mesh::Selector & blockSelector, const double threshold) +{ + ThrowRequireMsg(mesh.is_automatic_aura_on() || mesh.parallel_size() == 1, "Method requires automatic aura."); + + // This would be more efficient if a nodal field was used because it could compute the element volume only once. + std::vector ownedNodesWithNodalVolBelowThreshold; + + const FieldRef coordsField(mesh.mesh_meta_data().coordinate_field()); + std::vector elementNodeCoords; + + stk::mesh::Selector ownedBlockSelector = mesh.mesh_meta_data().locally_owned_part() & blockSelector; + + const stk::mesh::BucketVector & buckets = mesh.get_buckets( stk::topology::NODE_RANK, ownedBlockSelector ); + + for ( auto && bucket : buckets ) + { + for ( auto && node : *bucket ) + { + double nodalVolume = 0.; + for (auto && element : StkMeshEntities{mesh.begin_elements(node), mesh.end_elements(node)}) + { + if (blockSelector(mesh.bucket(element))) + nodalVolume += compute_tri_or_tet_volume(mesh, element, coordsField); + + if (nodalVolume >= threshold) + break; + } + if (nodalVolume < threshold) + ownedNodesWithNodalVolBelowThreshold.push_back(node); + } + } + return ownedNodesWithNodalVolBelowThreshold; +} + +static std::vector get_nodes_with_no_attached_elements(const stk::mesh::BulkData & mesh) +{ + ThrowRequireMsg(mesh.is_automatic_aura_on() || mesh.parallel_size() == 1, "Method requires automatic aura."); + + std::vector nodesWithNoAttachedElements; + + for ( auto && bucket : mesh.buckets(stk::topology::NODE_RANK) ) + for ( auto && node : *bucket ) + if (mesh.num_elements(node) == 0) + nodesWithNoAttachedElements.push_back(node); + + return nodesWithNoAttachedElements; +} + +static +void pack_entities_for_sharing_procs(const stk::mesh::BulkData & mesh, + const std::vector & entities, + stk::CommSparse &commSparse) +{ + std::vector sharingProcs; + stk::pack_and_communicate(commSparse,[&]() + { + for (auto entity : entities) + { + if (mesh.bucket(entity).shared()) + { + mesh.comm_shared_procs(entity, sharingProcs); + for (int procId : sharingProcs) + commSparse.send_buffer(procId).pack(mesh.entity_key(entity)); + } + } + }); +} + +static +void unpack_shared_entities(const stk::mesh::BulkData & mesh, + std::vector & sharedEntities, + stk::CommSparse &commSparse) +{ + stk::unpack_communications(commSparse, [&](int procId) + { + stk::CommBuffer & buffer = commSparse.recv_buffer(procId); + + while ( buffer.remaining() ) + { + stk::mesh::EntityKey entityKey; + commSparse.recv_buffer(procId).unpack(entityKey); + stk::mesh::Entity entity = mesh.get_entity(entityKey); + ThrowAssert(mesh.is_valid(entity)); + sharedEntities.push_back(entity); + } + }); +} + +static +void append_shared_entities_to_owned_ones(const stk::mesh::BulkData & mesh, + std::vector & entities) +{ + stk::CommSparse commSparse(mesh.parallel()); + pack_entities_for_sharing_procs(mesh, entities, commSparse); + + std::vector sharedEntities; + unpack_shared_entities(mesh, sharedEntities, commSparse); + entities.insert(entities.end(), sharedEntities.begin(), sharedEntities.end()); +} + +//-------------------------------------------------------------------------------- + +void delete_node_and_all_entities_using_it(stk::mesh::BulkData & mesh, const stk::mesh::Entity node) +{ + std::vector relatives; + + const stk::mesh::EntityRank highestEntityRank = static_cast(mesh.mesh_meta_data().entity_rank_count()-1); + for (stk::mesh::EntityRank irank = highestEntityRank; irank != stk::topology::NODE_RANK; --irank) + { + relatives.assign(mesh.begin(node, irank), mesh.end(node, irank)); + for (auto && relative : relatives) + ThrowRequire(mesh.destroy_entity(relative)); + } + ThrowRequire(mesh.destroy_entity(node)); +} + +static void delete_nodes_and_all_entities_using_them(stk::mesh::BulkData & mesh, const std::vector & nodesToDelete) +{ + mesh.modification_begin(); + for (auto && node : nodesToDelete) + delete_node_and_all_entities_using_it(mesh, node); + mesh.modification_end(); +} + +static size_t delete_nodes_with_nodal_volume_below_threshold_and_all_entities_using_them(stk::mesh::BulkData & mesh, const stk::mesh::Selector & blockSelector, const double threshold) +{ + std::vector nodesToDelete = get_owned_nodes_with_nodal_volume_below_threshold(mesh, blockSelector, threshold); + const size_t globalNumNodesToDelete = stk::get_global_sum(mesh.parallel(), nodesToDelete.size()); + + if (globalNumNodesToDelete > 0) + { + append_shared_entities_to_owned_ones(mesh, nodesToDelete); + delete_nodes_and_all_entities_using_them(mesh, nodesToDelete); + } + return globalNumNodesToDelete; +} + +static size_t delete_nodes_with_no_attached_elements(stk::mesh::BulkData & mesh) +{ + const std::vector nodesToDelete = get_nodes_with_no_attached_elements(mesh); + const size_t globalNumNodesToDelete = stk::get_global_sum(mesh.parallel(), nodesToDelete.size()); + + if (globalNumNodesToDelete > 0) + delete_nodes_and_all_entities_using_them(mesh, nodesToDelete); + + return globalNumNodesToDelete; +} + +void delete_all_entities_using_nodes_with_nodal_volume_below_threshold(stk::mesh::BulkData & mesh, const stk::mesh::Selector & blockSelector, const double threshold) +{ + const int maxIterations = 10; + int iteration = 0; + while (++iteration <= maxIterations) + { + const size_t numNodesDeletedWithSmallVolume = delete_nodes_with_nodal_volume_below_threshold_and_all_entities_using_them(mesh, blockSelector, threshold); + if (numNodesDeletedWithSmallVolume > 0) + { + sierra::Env::outputP0() << "Iteration " << iteration << ":" << std::endl; + sierra::Env::outputP0() << " Deleted " << numNodesDeletedWithSmallVolume << " node(s) with a nodal volume less than " << threshold << " (and all attached entities)." << std::endl; + const size_t numNodesDeletedWithNoElements = delete_nodes_with_no_attached_elements(mesh); + sierra::Env::outputP0() << " Deleted " << numNodesDeletedWithNoElements << " node(s) that have no attached elements." << std::endl; + } + else + break; + } + if (iteration < maxIterations) + sierra::Env::outputP0() << "Successfully deleted all nodes with a nodal volume less than " << threshold << "." << std::endl; + else + sierra::Env::outputP0() << "Terminating after performing max iterations. There still may be nodes with a nodal volume less than " << threshold << "." << std::endl; +} + +//-------------------------------------------------------------------------------- + +struct ActiveChildNodeRequest +{ + std::vector m_parents; + std::vector m_sharing_procs; + std::vector > m_id_proc_pairs_from_all_procs; + bool m_id_procs_pairs_have_been_sorted; + stk::mesh::Entity *m_node_entity; + + ActiveChildNodeRequest(const std::vector & parents, stk::mesh::Entity *entity_place_holder=NULL) + : m_parents(parents), m_sharing_procs(), m_id_proc_pairs_from_all_procs(), + m_id_procs_pairs_have_been_sorted(false), m_node_entity(entity_place_holder) + { + std::sort(m_parents.begin(), m_parents.end()); + } + + void add_proc_id_pair(int proc_id, stk::mesh::EntityId id) + { + m_id_proc_pairs_from_all_procs.push_back(std::make_pair(proc_id, id)); + } + + void calculate_sharing_procs(stk::mesh::BulkData& mesh) + { + ThrowRequire(!m_parents.empty()); + + stk::mesh::EntityKey key0(stk::topology::NODE_RANK, m_parents[0]); + mesh.comm_shared_procs(key0, m_sharing_procs); + std::sort(m_sharing_procs.begin(), m_sharing_procs.end()); + + std::vector sharingProcs; + for (unsigned i=1; i working_set; + m_sharing_procs.swap(working_set); + std::set_intersection(working_set.begin(),working_set.end(),sharingProcs.begin(),sharingProcs.end(),std::back_inserter(m_sharing_procs)); + } + } + + size_t num_sharing_procs() const + { + return m_sharing_procs.size(); + } + + int sharing_proc(int index) const + { + return m_sharing_procs[index]; + } + + stk::mesh::EntityId suggested_node_id() const + { + ThrowRequireMsg(!m_id_procs_pairs_have_been_sorted, "Invalid use of child node calculation. Contact sierra-help"); + return m_id_proc_pairs_from_all_procs[0].second; + } + + void sort_id_proc_pairs() + { + m_id_procs_pairs_have_been_sorted = true; + std::sort(m_id_proc_pairs_from_all_procs.begin(), m_id_proc_pairs_from_all_procs.end()); + } + + stk::mesh::EntityId get_id_for_child() const + { + ThrowRequireMsg(m_id_procs_pairs_have_been_sorted, "Invalid use of child node calculation. Contact sierra-help"); + return m_id_proc_pairs_from_all_procs[0].second; + } + + void set_node_entity_for_request(stk::mesh::BulkData& mesh, const stk::mesh::PartVector & node_parts) + { + this->sort_id_proc_pairs(); + stk::mesh::EntityId id_for_child = get_id_for_child(); + *m_node_entity = mesh.declare_node(id_for_child, node_parts); + for (size_t i=0;i & child_node_requests, const stk::mesh::PartVector & node_parts, bool assert_32bit_ids, bool make_64bit_ids) +{ + std::vector communicate_request(child_node_requests.size(), false); + + unsigned num_nodes_requested = child_node_requests.size(); + std::vector available_node_ids; + + EntityIdPool::generate_new_ids(mesh, stk::topology::NODE_RANK, num_nodes_requested, available_node_ids, assert_32bit_ids, make_64bit_ids); + + while ( true ) + { + int more_work_to_be_done = false; + + std::vector active_child_node_requests; + + for (unsigned it_req=0; it_req & request_parents = request.parents; + + bool request_is_ready = true; + for (auto && request_parent : request_parents) + { + if (!mesh.is_valid(*request_parent)) + { + request_is_ready = false; + } + } + + if (request_is_ready) + { + stk::mesh::Entity *request_child = request.child; + + communicate_request[it_req] = true; + more_work_to_be_done = true; + + std::vector parent_ids(request_parents.size()); + for (size_t parent_index=0; parent_index & request_parents = active_child_node_requests[request_index].m_parents; + const stk::mesh::EntityId this_procs_suggested_id = active_child_node_requests[request_index].suggested_node_id(); + const size_t num_parents = request_parents.size(); + comm_spec.send_buffer(other_proc).pack(num_parents); + for (size_t parent_index=0; parent_index request_parents(num_parents); + for (size_t parent_index=0; parent_index::iterator iter = std::lower_bound(active_child_node_requests.begin(), active_child_node_requests.end(), from_other_proc); + + if ( iter != active_child_node_requests.end() && *iter == from_other_proc) + { + iter->add_proc_id_pair(i, suggested_node_id); + } + } + } + } + + for (size_t request_index=0;request_index::iterator iter = std::find(communicate_request.begin(), communicate_request.end(), false); + ThrowRequireMsg(iter == communicate_request.end(), "Invalid child node request. Contact sierra-help."); +} + +//-------------------------------------------------------------------------------- + +void +batch_create_sides(stk::mesh::BulkData & mesh, const std::vector & side_requests) +{ + const stk::mesh::EntityRank side_rank = mesh.mesh_meta_data().side_rank(); + + if (!mesh.has_face_adjacent_element_graph()) + { + mesh.initialize_face_adjacent_element_graph(); + } + + mesh.modification_begin(); + for (auto && side_request : side_requests) + { + stk::mesh::Entity element = side_request.element; + const unsigned element_side_ord = side_request.element_side_ordinal; + + stk::mesh::Entity existing_element_side = find_entity_by_ordinal(mesh, element, side_rank, element_side_ord); + if (mesh.is_valid(existing_element_side)) + { + continue; + } + + mesh.declare_element_side(element, element_side_ord, side_request.side_parts); + } + mesh.modification_end(); + + ThrowAssert(check_face_and_edge_ownership(mesh)); + ThrowAssert(check_face_and_edge_relations(mesh)); +} + +//-------------------------------------------------------------------------------- + +void +make_side_ids_consistent_with_stk_convention(stk::mesh::BulkData & mesh) +{ + const stk::mesh::MetaData & meta = mesh.mesh_meta_data(); + const stk::mesh::EntityRank side_rank = meta.side_rank(); + stk::mesh::Selector not_ghost_selector(meta.locally_owned_part() | meta.globally_shared_part()); + + std::vector sides; + stk::mesh::get_selected_entities( not_ghost_selector, mesh.buckets(side_rank), sides ); + + std::vector side_requests; + + mesh.modification_begin(); + for (auto&& side : sides) + { + const unsigned num_side_elems = mesh.num_elements(side); + const stk::mesh::Entity* side_elems = mesh.begin_elements(side); + const stk::mesh::ConnectivityOrdinal* side_elem_ordinals = mesh.begin_element_ordinals(side); + ThrowRequire(num_side_elems > 0); + stk::mesh::EntityId newId = 0; + for (unsigned i=0; i * const coordsField) +{ + stk::topology elem_topology = mesh.bucket(element).topology(); + if (elem_topology == stk::topology::TETRAHEDRON_4) + { + const std::array nodes = gather_tet_coordinates(mesh, element, coordsField); + const double vol = compute_tet_volume(nodes); + compute_tri_or_tet_volume(mesh, element, *coordsField); + const double edge_rms = std::sqrt( + ((nodes[1]-nodes[0]).length_squared() + + (nodes[2]-nodes[0]).length_squared() + + (nodes[3]-nodes[0]).length_squared() + + (nodes[3]-nodes[1]).length_squared() + + (nodes[3]-nodes[2]).length_squared() + + (nodes[2]-nodes[1]).length_squared())/6.); + return vol/(edge_rms*edge_rms*edge_rms); + } + ThrowRuntimeError("Topology " << elem_topology << " not supported in compute_element_volume_to_edge_ratio."); +} + +//-------------------------------------------------------------------------------- + +static void +debug_entity(std::ostream & output, const stk::mesh::BulkData & mesh, stk::mesh::Entity entity, const bool includeFields) +{ + if (!mesh.is_valid(entity)) + { + output << "Invalid entity: " << mesh.entity_key(entity) << std::endl; + return; + } + output << mesh.entity_key(entity) << ", parallel owner = " << mesh.parallel_owner_rank(entity) << " {" << std::endl; + output << " Connectivity:" << std::endl; + const stk::mesh::EntityRank end_rank = static_cast(mesh.mesh_meta_data().entity_rank_count()); + for (stk::mesh::EntityRank r = stk::topology::BEGIN_RANK; r < end_rank; ++r) { + unsigned num_rels = mesh.num_connectivity(entity, r); + stk::mesh::Entity const *rel_entities = mesh.begin(entity, r); + stk::mesh::ConnectivityOrdinal const *rel_ordinals = mesh.begin_ordinals(entity, r); + stk::mesh::Permutation const *rel_permutations = mesh.begin_permutations(entity, r); + for (unsigned i = 0; i < num_rels; ++i) { + output << " " << mesh.entity_key(rel_entities[i]) + << " @" << rel_ordinals[i]; + if (rel_permutations) output << ":" << (int)rel_permutations[i]; + output << std::endl; + } + } + output << " Parts: "; + const stk::mesh::PartVector & parts = mesh.bucket(entity).supersets(); + for(stk::mesh::PartVector::const_iterator part_iter = parts.begin(); part_iter != parts.end(); ++part_iter) + { + const stk::mesh::Part * const part = *part_iter; + output << part->name() << " "; + } + output << std::endl; + + if (includeFields) + { + const stk::mesh::FieldVector & all_fields = mesh.mesh_meta_data().get_fields(); + for ( stk::mesh::FieldVector::const_iterator it = all_fields.begin(); it != all_fields.end() ; ++it ) + { + const FieldRef field = (const FieldRef)(**it); + + if(field.entity_rank()!=mesh.entity_rank(entity)) continue; + + const unsigned field_length = field.length(); + + field.field().sync_to_host(); + if (field.type_is()) + { + const double * data = field_data(field, entity); + if (NULL != data) + { + if (1 == field_length) + { + output << " Field: field_name=" << field.name() << ", field_state=" << field.state() << ", value=" << *data << std::endl; + } + else + { + for (unsigned i = 0; i < field_length; ++i) + { + output << " Field: field_name=" << field.name() << ", field_state=" << field.state() << ", value[" << i << "]=" << data[i] << std::endl; + } + } + } + } + else if (field.type_is()) + { + const int * data = field_data(field, entity); + if (NULL != data) + { + if (1 == field_length) + { + output << " Field: field_name=" << field.name() << ", field_state=" << field.state() << ", value=" << *data << std::endl; + } + else + { + for (unsigned i = 0; i < field_length; ++i) + { + output << " Field: field_name=" << field.name() << ", field_state=" << field.state() << ", value[" << i << "]=" << data[i] << std::endl; + } + } + } + } + } + output << std::endl; + } +} + +std::string +debug_entity(const stk::mesh::BulkData & mesh, stk::mesh::Entity entity, const bool includeFields) +{ + std::ostringstream out; + debug_entity(out, mesh, entity, includeFields); + return out.str(); +} + +std::string +debug_entity(const stk::mesh::BulkData & mesh, stk::mesh::Entity entity) +{ + return debug_entity(mesh, entity, false); +} + +//-------------------------------------------------------------------------------- + +std::vector +get_side_permutation(stk::topology topology, stk::mesh::Permutation node_permutation) +{ + const unsigned perm = node_permutation; + switch (topology) + { + case stk::topology::TRIANGLE_3_2D: + case stk::topology::TRIANGLE_6_2D: + switch (perm) + { + case 0: return {0, 1, 2}; + case 1: return {2, 0, 1}; + case 2: return {1, 2, 0}; + default: ThrowRuntimeError("find_side_permutation error, invalid triangle permutation."); + } + break; + case stk::topology::TETRAHEDRON_4: + case stk::topology::TETRAHEDRON_10: + switch (perm) + { + case 0: return {0, 1, 2, 3}; + case 1: return {1, 2, 0, 3}; + case 2: return {2, 0, 1, 3}; + case 3: return {2, 1, 3, 0}; + case 4: return {1, 3, 2, 0}; + case 5: return {3, 2, 1, 0}; + case 6: return {3, 1, 0, 2}; + case 7: return {1, 0, 3, 2}; + case 8: return {0, 3, 1, 2}; + case 9: return {0, 2, 3, 1}; + case 10: return {2, 3, 0, 1}; + case 11: return {3, 0, 2, 1}; + default: ThrowRuntimeError("find_side_permutation error, invalid tetrahedron permutation."); + } + break; + default: ThrowRuntimeError("find_side_permutation error, unsupported topology."); + } +} + +//-------------------------------------------------------------------------------- + +const stk::mesh::Part & +find_element_part(const stk::mesh::BulkData& mesh, stk::mesh::Entity elem) +{ + ThrowAssert(mesh.entity_rank(elem) == stk::topology::ELEMENT_RANK); + const stk::mesh::Part * elem_io_part = nullptr; + + const stk::mesh::PartVector & elem_parts = mesh.bucket(elem).supersets(); + for(stk::mesh::PartVector::const_iterator part_iter = elem_parts.begin(); part_iter != elem_parts.end(); ++part_iter) + { + const stk::mesh::Part * const part = *part_iter; + if (part->primary_entity_rank() == stk::topology::ELEMENT_RANK && part->subsets().empty() && part->topology() != stk::topology::INVALID_TOPOLOGY) + { + // there should only be one element rank part without subsets with topology on the element + ThrowRequireMsg(nullptr == elem_io_part, "For element " << mesh.identifier(elem) << ", more than one element rank part was found: " << elem_io_part->name() << " " << part->name()); + elem_io_part = part; + } + } + ThrowRequire(NULL != elem_io_part); + + return *elem_io_part; +} + +//-------------------------------------------------------------------------------- + +void +disconnect_entity(stk::mesh::BulkData & mesh, stk::mesh::Entity entity) +{ + stk::mesh::EntityRank entity_rank = mesh.entity_rank(entity); + std::vector relatives; + std::vector relative_ordinals; + + const stk::mesh::EntityRank highest_entity_rank = static_cast(mesh.mesh_meta_data().entity_rank_count()-1); + for (stk::mesh::EntityRank irank = highest_entity_rank; irank != entity_rank; --irank) + { + // Previously this attempted to delete forward or backward and still the list got corrupted, + // so just copy into vector and delete from there. + relatives.assign(mesh.begin(entity, irank),mesh.end(entity, irank)); + relative_ordinals.assign(mesh.begin_ordinals(entity, irank), mesh.end_ordinals(entity, irank)); + + for (size_t irel = 0; irel < relatives.size(); ++irel) + { + mesh.destroy_relation( relatives[irel], entity, relative_ordinals[irel]); + } + } +} + +//-------------------------------------------------------------------------------- + +bool +disconnect_and_destroy_entity(stk::mesh::BulkData & mesh, stk::mesh::Entity entity) +{ + disconnect_entity(mesh, entity); + return mesh.destroy_entity(entity); +} + +//-------------------------------------------------------------------------------- + +bool +check_induced_parts(const stk::mesh::BulkData & mesh) +{ /* %TRACE[ON]% */ Trace trace__("krino::debug_induced_parts()"); /* %TRACE% */ + + // This method requires aura to work correctly. + if (!mesh.is_automatic_aura_on() && mesh.parallel_size() > 1) + { + // Skip check if we don't have aura + return true; + } + + bool success = true; + + const stk::mesh::MetaData & meta = mesh.mesh_meta_data(); + stk::mesh::Selector not_ghost_selector(meta.locally_owned_part() | meta.globally_shared_part()); + + std::vector< stk::mesh::Entity> entities; + + for (stk::mesh::EntityRank entity_rank = stk::topology::NODE_RANK; entity_rank <= stk::topology::ELEMENT_RANK; ++entity_rank) + { + stk::mesh::get_selected_entities( not_ghost_selector, mesh.buckets(entity_rank), entities ); + + for (unsigned i=0; iprimary_entity_rank() == entity_rank) + { + bool have_relative_missing_part = false; + const unsigned num_relatives = mesh.num_connectivity(entity, relative_rank); + const stk::mesh::Entity* relatives = mesh.begin(entity, relative_rank); + for (unsigned it_rel=0; it_relname() << " is found on " << mesh.entity_key(entity) << " but is missing from relatives: "; + for (unsigned it_rel=0; it_relprimary_entity_rank() == relative_rank) + { + bool found_relative_with_part = false; + const unsigned num_relatives = mesh.num_connectivity(entity, relative_rank); + const stk::mesh::Entity* relatives = mesh.begin(entity, relative_rank); + for (unsigned it_rel=0; it_relname() << " is found on " << mesh.entity_key(entity) << " but not on any of its relatives: "; + for (unsigned it_rel=0; it_rel & remote_entity_node_ids) +{ + stk::mesh::Entity entity = mesh.get_entity(remote_entity_key); + if (!mesh.is_valid(entity)) + { + krinolog << "Shared entity error, local entity does not exist, remote entity: " << remote_entity_key << stk::diag::dendl; + return false; + } + + std::vector entity_nodes(mesh.begin_nodes(entity), mesh.end_nodes(entity)); + if (entity_nodes.size() != remote_entity_node_ids.size()) + { + krinolog << "Shared entity error, number_of nodes don't match, number of remote nodes = " << remote_entity_node_ids.size() << stk::diag::dendl; + krinolog << "Local entity: " << debug_entity(mesh, entity) << stk::diag::dendl; + return false; + } + + bool nodes_match = true; + for (size_t node_index=0;node_index & entities) +{ + bool success = true; + + std::vector sharing_procs; + stk::CommSparse comm_spec(mesh.parallel()); + + for (int phase=0;phase<2;++phase) + { + for (std::vector::iterator it_entity = entities.begin(); it_entity != entities.end(); ++it_entity) + { + stk::mesh::Entity entity = *it_entity; + std::vector entity_nodes(mesh.begin_nodes(entity), mesh.end_nodes(entity)); + stk::mesh::EntityKey entity_key = mesh.entity_key(entity); + ThrowRequire(mesh.bucket(entity).shared()); + + mesh.shared_procs_intersection(entity_nodes, sharing_procs); + for (size_t proc_index=0;proc_index entity_node_ids(num_nodes); + for (size_t node_index=0; node_index sharing_procs; + stk::CommSparse comm_spec(mesh.parallel()); + + bool success = true; + stk::mesh::Selector shared_selector = meta.globally_shared_part(); + std::vector entities; + + for (stk::mesh::EntityRank entity_rank = stk::topology::EDGE_RANK; entity_rank < stk::topology::ELEMENT_RANK; ++entity_rank) + { + stk::mesh::get_selected_entities( shared_selector, mesh.buckets( entity_rank ), entities ); + + if (!check_shared_entity_nodes(mesh, entities)) success = false; + } + return success; +} + +//-------------------------------------------------------------------------------- + +bool +check_face_and_edge_relations(const stk::mesh::BulkData & mesh) +{ + const stk::mesh::MetaData & meta = mesh.mesh_meta_data(); + + bool success = true; + stk::mesh::Selector not_ghost_selector = meta.locally_owned_part() | meta.globally_shared_part(); + + for (stk::mesh::EntityRank entity_rank = stk::topology::EDGE_RANK; entity_rank <= stk::topology::FACE_RANK; ++entity_rank) + { + const stk::mesh::BucketVector & buckets = mesh.get_buckets( entity_rank, not_ghost_selector ); + + stk::mesh::BucketVector::const_iterator ib = buckets.begin(); + stk::mesh::BucketVector::const_iterator ib_end = buckets.end(); + + for ( ; ib != ib_end; ++ib ) + { + const stk::mesh::Bucket & b = **ib; + const size_t length = b.size(); + for (size_t it_entity = 0; it_entity < length; ++it_entity) + { + stk::mesh::Entity entity = b[it_entity]; + stk::topology entity_topology = mesh.bucket(entity).topology(); + std::vector entity_nodes(mesh.begin_nodes(entity), mesh.end_nodes(entity)); + std::vector entity_elems; + stk::mesh::get_entities_through_relations(mesh, entity_nodes, stk::topology::ELEMENT_RANK, entity_elems); + + if (entity_elems.empty()) + { + krinolog << "Relation error, entity not attached to any elements: " << stk::diag::dendl; + krinolog << "Entity: " << debug_entity(mesh, entity) << stk::diag::dendl; + success = false; + } + + bool have_coincident_shell = false; + std::pair shell_relationship(stk::mesh::INVALID_CONNECTIVITY_ORDINAL, stk::mesh::INVALID_PERMUTATION); + for (auto&& elem : entity_elems) + { + stk::topology elem_topology = mesh.bucket(elem).topology(); + if (elem_topology.is_shell() && elem_topology.num_nodes() == entity_topology.num_nodes()) + { + have_coincident_shell = true; + shell_relationship = determine_shell_side_ordinal_and_permutation(mesh, elem, entity); + break; + } + } + + for (auto&& elem : entity_elems) + { + stk::topology elem_topology = mesh.bucket(elem).topology(); + const bool is_coincident_shell = (elem_topology.is_shell() && elem_topology.num_nodes() == entity_topology.num_nodes()); + bool should_be_attached = true; + if (have_coincident_shell && !is_coincident_shell) + { + // Volume elements should only be attached to inward pointing faces when the surface has a shell. + std::pair relationship = determine_ordinal_and_permutation(mesh, elem, entity); + const bool elem_polarity = entity_topology.is_positive_polarity(relationship.second); + const bool shell_polarity = entity_topology.is_positive_polarity(shell_relationship.second); + should_be_attached = elem_polarity != shell_polarity; + } + + const unsigned num_elem_entities = mesh.num_connectivity(elem, entity_rank); + const stk::mesh::Entity* elem_entities = mesh.begin(elem, entity_rank); + const stk::mesh::ConnectivityOrdinal * elem_ordinals = mesh.begin_ordinals(elem, entity_rank); + const stk::mesh::Permutation * elem_permutations = mesh.begin_permutations(elem, entity_rank); + bool already_attached = false; + for (unsigned it_s=0; it_s relationship = + is_coincident_shell ? + shell_relationship : + determine_ordinal_and_permutation(mesh, elem, entity); + if (relationship.first != elem_ordinals[it_s]) + { + krinolog << "Relation error, ordinal is incorrect: " << relationship.first << "!=" << elem_ordinals[it_s] << stk::diag::dendl; + krinolog << "Entity: " << debug_entity(mesh, entity) << stk::diag::dendl; + krinolog << "Element: " << debug_entity(mesh, elem) << stk::diag::dendl; + success = false; + } + if (relationship.second != elem_permutations[it_s]) + { + krinolog << "Relation error, permutation is incorrect: " << relationship.second << "!=" << elem_permutations[it_s] << stk::diag::dendl; + krinolog << "Entity: " << debug_entity(mesh, entity) << stk::diag::dendl; + krinolog << "Element: " << debug_entity(mesh, elem) << stk::diag::dendl; + success = false; + } + } + } + } + if (!already_attached && should_be_attached) + { + + krinolog << "Relation error, entity is not attached to element: " << stk::diag::dendl; + krinolog << "Entity: " << debug_entity(mesh, entity) << stk::diag::dendl; + krinolog << "Element: " << debug_entity(mesh, elem) << stk::diag::dendl; + std::pair relationship = determine_ordinal_and_permutation(mesh, elem, entity); + for (unsigned it_s=0; it_s entities; + stk::mesh::get_entities( mesh, meta.side_rank(), entities ); + + mesh.modification_begin(); + for (auto&& entity : entities) + { + attach_entity_to_elements(mesh, entity); + } + mesh.modification_end(); +} + +void +attach_entity_to_elements(stk::mesh::BulkData & mesh, stk::mesh::Entity entity) +{ + //Sorry! Passing these scratch vectors into stk's declare_relation function is + //a performance improvement (fewer allocations). But stk will try to clean up + //this ugliness soon. (i.e., find a better way to get the performance.) + stk::mesh::OrdinalVector scratch1, scratch2, scratch3; + + stk::mesh::MetaData & meta = mesh.mesh_meta_data(); + stk::topology entity_topology = mesh.bucket(entity).topology(); + stk::mesh::EntityRank entity_rank = entity_topology.rank(); + std::vector entity_nodes(mesh.begin_nodes(entity), mesh.end_nodes(entity)); + std::vector entity_elems; + stk::mesh::get_entities_through_relations(mesh, entity_nodes, stk::topology::ELEMENT_RANK, entity_elems); + + bool have_coincident_shell = false; + std::pair shell_relationship(stk::mesh::INVALID_CONNECTIVITY_ORDINAL, stk::mesh::INVALID_PERMUTATION); + for (auto&& elem : entity_elems) + { + stk::topology elem_topology = mesh.bucket(elem).topology(); + if (elem_topology.is_shell() && elem_topology.num_nodes() == entity_topology.num_nodes()) + { + have_coincident_shell = true; + shell_relationship = determine_shell_side_ordinal_and_permutation(mesh, elem, entity); + break; + } + } + + for (auto&& elem : entity_elems) + { + if (!mesh.bucket(elem).member(meta.locally_owned_part())) + { + continue; + } + bool already_attached = false; + const unsigned num_elem_entities = mesh.num_connectivity(elem, entity_rank); + const stk::mesh::Entity* elem_entities = mesh.begin(elem, entity_rank); + for (unsigned it_s=0; it_s relationship(stk::mesh::INVALID_CONNECTIVITY_ORDINAL, stk::mesh::INVALID_PERMUTATION); + if (!have_coincident_shell) + { + relationship = determine_ordinal_and_permutation(mesh, elem, entity); + } + else + { + stk::topology elem_topology = mesh.bucket(elem).topology(); + if (elem_topology.is_shell() && elem_topology.num_nodes() == entity_topology.num_nodes()) + { + ThrowAssertMsg(shell_relationship.second == determine_permutation(mesh, elem, entity, shell_relationship.first), "All shells should have same permutation for side."); + relationship = shell_relationship; + } + else + { + relationship = determine_ordinal_and_permutation(mesh, elem, entity); + const bool elem_polarity = entity_topology.is_positive_polarity(relationship.second); + const bool shell_polarity = entity_topology.is_positive_polarity(shell_relationship.second); + if (elem_polarity == shell_polarity) + { + // Side does not touch volume element; + continue; + } + } + } + + mesh.declare_relation( elem, entity, relationship.first, relationship.second, scratch1, scratch2, scratch3 ); + const bool successfully_attached = (find_entity_by_ordinal(mesh, elem, entity_rank, relationship.first) == entity); + if (!successfully_attached) + { + krinolog << "Could not attach " << debug_entity(mesh,entity) << " to element " << debug_entity(mesh,elem) << stk::diag::dendl; + krinolog << "Existing attached entities:" << stk::diag::dendl; + for (unsigned it_s=0; it_s & entities, + stk::CommSparse &commSparse) +{ + entities.clear(); + stk::unpack_communications(commSparse, [&](int procId) + { + stk::CommBuffer & buffer = commSparse.recv_buffer(procId); + + while ( buffer.remaining() ) + { + stk::mesh::EntityKey entityKey; + commSparse.recv_buffer(procId).unpack(entityKey); + entities.insert(mesh.get_entity(entityKey)); + } + }); +} + +void +update_node_activation(stk::mesh::BulkData & mesh, stk::mesh::Part & active_part) +{ + stk::mesh::MetaData & meta = mesh.mesh_meta_data(); + + stk::mesh::PartVector active_part_vec(1, &active_part); + stk::mesh::PartVector inactive_part_vec; + + std::vector entities; + stk::mesh::Selector locally_owned(meta.locally_owned_part()); + stk::mesh::get_selected_entities( locally_owned, mesh.buckets( stk::topology::NODE_RANK ), entities ); + + for (std::vector::iterator i_node = entities.begin(); i_node != entities.end(); ++i_node) + { + stk::mesh::Entity node = *i_node; + + const unsigned num_node_elems = mesh.num_elements(node); + const stk::mesh::Entity* node_elems = mesh.begin_elements(node); + bool have_active_elems = false; + for (unsigned node_elem_index=0; node_elem_index add_parts; + std::vector remove_parts; + std::vector entities; + + stk::mesh::Selector inactive_locally_owned = mesh.mesh_meta_data().locally_owned_part() & !active_part; + + for (stk::mesh::EntityRank entity_rank = stk::topology::NODE_RANK; entity_rank <= stk::topology::ELEMENT_RANK; ++entity_rank) + { + const stk::mesh::BucketVector & buckets = mesh.get_buckets(entity_rank, inactive_locally_owned); + for (auto&& bucket_ptr : buckets) + { + entities.insert(entities.end(), bucket_ptr->begin(), bucket_ptr->end()); + } + } + add_parts.assign(entities.size(), {&active_part}); + remove_parts.resize(entities.size()); + + mesh.batch_change_entity_parts(entities, add_parts, remove_parts); +} + +//-------------------------------------------------------------------------------- + +void destroy_custom_ghostings(stk::mesh::BulkData & mesh) +{ + const std::vector & ghostings = mesh.ghostings(); + for(unsigned i = stk::mesh::BulkData::AURA+1; i < ghostings.size(); ++i) + { + mesh.destroy_ghosting(*ghostings[i]); + } +} + +//-------------------------------------------------------------------------------- + +void +delete_mesh_entities(stk::mesh::BulkData & mesh, std::vector & child_elems) +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::delete_old_mesh_entities(void)"); /* %TRACE% */ + + stk::mesh::MetaData & meta = mesh.mesh_meta_data(); + + stk::mesh::Selector not_ghost_selector = meta.locally_owned_part() | meta.globally_shared_part(); + stk::mesh::Selector universal_selector = meta.universal_part(); + + std::vector child_sides; + std::vector child_edges; + std::vector child_nodes; + + for (unsigned i=0; i * const coords_field = reinterpret_cast*>(mesh.mesh_meta_data().coordinate_field()); + ThrowRequireMsg(nullptr != coords_field, "Coordinates must be defined."); + + double * child_coords = stk::mesh::field_data(*coords_field, child); + double * parent0_coords = stk::mesh::field_data(*coords_field, parent0); + double * parent1_coords = stk::mesh::field_data(*coords_field, parent1); + + const unsigned ndim = mesh.mesh_meta_data().spatial_dimension(); + unsigned best_dim = 0; + double best_extent = std::abs(parent1_coords[0] - parent0_coords[0]); + for (unsigned dim = 1; dim < ndim; ++dim) + { + const double extent = std::abs(parent1_coords[dim] - parent0_coords[dim]); + if (extent > best_extent) + { + best_dim = dim; + best_extent = extent; + } + } + return std::abs(child_coords[best_dim] - parent0_coords[best_dim])/best_extent; +} + +//-------------------------------------------------------------------------------- + +void +store_edge_node_parent_ids(const stk::mesh::BulkData & mesh, + const FieldRef & parent_id_field, + stk::mesh::Entity edge_node_entity, + stk::mesh::EntityId parent0_id, + stk::mesh::EntityId parent1_id) +{ + if (parent_id_field.type_is()) + { + auto * stored_parent_ids = field_data(parent_id_field, edge_node_entity); + ThrowAssertMsg(stored_parent_ids, "Node " << mesh.identifier(edge_node_entity) + << " does not have the parent_ids field suggesting it is a mesh node."); + stored_parent_ids[0] = parent0_id; + stored_parent_ids[1] = parent1_id; + } + else if (parent_id_field.type_is()) + { + auto * stored_parent_ids = field_data(parent_id_field, edge_node_entity); + ThrowAssertMsg(stored_parent_ids, "Node " << mesh.identifier(edge_node_entity) + << " does not have the parent_ids field suggesting it is a mesh node."); + stored_parent_ids[0] = parent0_id; + stored_parent_ids[1] = parent1_id; + } + else + { + ThrowRequireMsg(false, "Unsupported field type for parent_node_ids_field"); + } +} + +//-------------------------------------------------------------------------------- + +std::array +get_edge_node_parent_ids(const stk::mesh::BulkData & mesh, + const FieldRef & parent_id_field, + const stk::mesh::Entity edge_node_entity) +{ + std::array parent_ids; + + if (parent_id_field.type_is()) + { + const auto * stored_parent_ids = field_data(parent_id_field, edge_node_entity); + ThrowAssertMsg(stored_parent_ids, "No SubElementNode found for node " << mesh.identifier(edge_node_entity) + << ", but it does not have the parent_ids field suggesting it is a mesh node."); + parent_ids[0] = stored_parent_ids[0]; + parent_ids[1] = stored_parent_ids[1]; + } + else if (parent_id_field.type_is()) + { + const auto * stored_parent_ids = field_data(parent_id_field, edge_node_entity); + ThrowAssertMsg(stored_parent_ids, "No SubElementNode found for node " << mesh.identifier(edge_node_entity) + << ", but it does not have the parent_ids field suggesting it is a mesh node."); + parent_ids[0] = stored_parent_ids[0]; + parent_ids[1] = stored_parent_ids[1]; + } + else + { + ThrowRequireMsg(false, "Unsupported field type for parent_node_ids_field"); + } + return parent_ids; +} + +//-------------------------------------------------------------------------------- + +void get_parent_nodes_from_child(const stk::mesh::BulkData & mesh, + stk::mesh::Entity child, const FieldRef & parent_id_field, + std::set & parent_nodes) +{ + if (has_field_data(parent_id_field, child)) + { + auto parent_ids = get_edge_node_parent_ids(mesh, parent_id_field, child); + const stk::mesh::Entity parent0 = mesh.get_entity(stk::topology::NODE_RANK, parent_ids[0]); + const stk::mesh::Entity parent1 = mesh.get_entity(stk::topology::NODE_RANK, parent_ids[1]); + ThrowAssert(mesh.is_valid(parent0) && mesh.is_valid(parent1)); + get_parent_nodes_from_child(mesh, parent0, parent_id_field, parent_nodes); + get_parent_nodes_from_child(mesh, parent1, parent_id_field, parent_nodes); + } + else + { + parent_nodes.insert(child); + } +} + +//-------------------------------------------------------------------------------- + +void debug_print_selector_parts(const stk::mesh::Selector & selector) +{ + stk::mesh::PartVector parts; + selector.get_parts(parts); + krinolog << "Selector contains parts: " << stk::diag::push << stk::diag::dendl; + for(stk::mesh::PartVector::const_iterator it = parts.begin(); it != parts.end(); ++it) + { + krinolog << (*it)->name() << stk::diag::dendl; + } + krinolog << stk::diag::pop << stk::diag::dendl; +} + +//-------------------------------------------------------------------------------- + +stk::mesh::PartVector filter_non_io_parts(const stk::mesh::PartVector & all_parts) +{ + stk::mesh::PartVector io_parts; + + for(stk::mesh::PartVector::const_iterator it = all_parts.begin(); it != all_parts.end(); ++it) + { + if( stk::io::is_part_io_part(**it) ) + { + io_parts.push_back(*it); + } + } + + return io_parts; +} + +void +activate_selected_sides_touching_active_elements(stk::mesh::BulkData & mesh, const stk::mesh::Selector & side_selector, stk::mesh::Part & active_part) +{ + // This method requires AURA + ThrowRequire(mesh.is_automatic_aura_on()); + + mesh.modification_begin(); + stk::mesh::PartVector active_part_vec(1, &active_part); + stk::mesh::PartVector inactive_part_vec; + stk::mesh::Selector select_locally_owned = side_selector & mesh.mesh_meta_data().locally_owned_part(); + + std::vector sides; + stk::mesh::get_selected_entities( select_locally_owned, mesh.buckets( mesh.mesh_meta_data().side_rank() ), sides ); + for (auto && side : sides) + { + bool have_active_elem = false; + const stk::mesh::Entity* side_elems = mesh.begin_elements(side); + const unsigned num_side_elems = mesh.num_elements(side); + for (unsigned ielem=0; ielem & coincident_elems) +{ + stk::topology elem_topology = mesh.bucket(elem).topology(); + const stk::mesh::Entity* elem_nodes = mesh.begin_nodes(elem); + std::vector elem_side_nodes; + std::vector elem_nbrs; + std::vector nbr_side_nodes; + + coincident_elems.clear(); + const unsigned num_sides = elem_topology.num_sides(); + for (unsigned iside=0; iside sorted_elem_side_nodes = elem_side_nodes; + std::sort(sorted_elem_side_nodes.begin(), sorted_elem_side_nodes.end(), stk::mesh::EntityLess(mesh)); + const unsigned unique_len = std::distance(sorted_elem_side_nodes.begin(), std::unique( sorted_elem_side_nodes.begin(), sorted_elem_side_nodes.end() )); + const bool is_degenerate_side = unique_len < mesh.mesh_meta_data().spatial_dimension(); + if (is_degenerate_side) continue; + + stk::mesh::get_entities_through_relations(mesh, elem_side_nodes, stk::topology::ELEMENT_RANK, elem_nbrs); + ThrowRequire(!elem_nbrs.empty()); + + for (auto && nbr : elem_nbrs) + { + if (nbr == elem) continue; + + stk::topology nbr_topology = mesh.bucket(nbr).topology(); + const stk::mesh::Entity* nbr_nodes = mesh.begin_nodes(nbr); + const unsigned num_nbr_sides = nbr_topology.num_sides(); + for (unsigned inbr_side=0; inbr_side 1) + { + // Skip check if we don't have aura + return true; + } + + bool found_mismatched_side = false; + + std::vector element_nodes; + std::vector element_side_nodes; + std::vector side_elements; + std::vector active_side_elements; + + const stk::mesh::BucketVector & buckets = mesh.get_buckets( stk::topology::ELEMENT_RANK, active_part & mesh.mesh_meta_data().locally_owned_part() ); + + for ( auto && bucket : buckets ) + { + stk::topology element_topology = bucket->topology(); + if (element_topology.is_shell()) continue; + const unsigned num_sides = element_topology.num_sides(); + + for ( auto && element : *bucket ) + { + element_nodes.assign(mesh.begin_nodes(element), mesh.end_nodes(element)); + for (unsigned iside=0; iside elements; + std::vector< stk::mesh::Entity> coincident_elements; + std::vector< stk::mesh::Entity> element_nodes; + + ParallelErrorMessage err(mesh.parallel()); + + stk::mesh::get_entities( mesh, stk::topology::ELEMENT_RANK, elements ); + for (auto && element : elements) + { + element_nodes.assign(mesh.begin_nodes(element), mesh.end_nodes(element)); + stk::mesh::get_entities_through_relations(mesh, element_nodes, stk::topology::ELEMENT_RANK, coincident_elements); + ThrowRequire(!coincident_elements.empty()); + stk::topology element_topology = mesh.bucket(element).topology(); + + bool coincident_element_error = false; + for (auto && coincident : coincident_elements) + { + if (coincident == element || element_topology != mesh.bucket(coincident).topology()) continue; + if (!element_topology.is_shell()) + { + err << "Non-shell elements " << mesh.entity_key(element) << " and " + << mesh.entity_key(coincident) << " are fully coincident (overlapping).\n"; + coincident_element_error = true; + } + else + { + stk::EquivalentPermutation result = element_topology.is_equivalent(mesh.begin_nodes(coincident), element_nodes.data()); + ThrowRequire(result.is_equivalent); + if (result.permutation_number != stk::mesh::DEFAULT_PERMUTATION) + { + err << "Elements " << mesh.entity_key(element) << " and " << mesh.entity_key(coincident) + << " are fully coincident shell elements but have different node order.\n"; + coincident_element_error = true; + } + } + } + + // Detect partially coincident, non-shell elements that are both active + if (!coincident_element_error && mesh.bucket(element).member(active_part) && !element_topology.is_shell() && element_topology.base() != stk::topology::BEAM_2) + { + get_partially_and_fully_coincident_elements(mesh, element, coincident_elements); + + for (auto && coincident : coincident_elements) + { + if (mesh.bucket(coincident).member(active_part) && !mesh.bucket(coincident).topology().is_shell() && + mesh.bucket(coincident).topology().base() != stk::topology::BEAM_2) + { + err << "Non-shell elements " << mesh.entity_key(element) << " with topology " + << element_topology.name() << " and " << mesh.entity_key(coincident) + << " with topology " << mesh.bucket(coincident).topology().name() + << " are partially coincident (overlapping) and active.\n"; + coincident_element_error = true; + } + } + } + } + auto err_msg = err.gather_message(); + krinolog << err_msg.second; + return !err_msg.first; +} + +bool +fix_coincident_element_ownership(stk::mesh::BulkData & mesh) +{ + // This method exploits aura to choose which processor the faces and edges should be owned by + if (!mesh.is_automatic_aura_on()) + { + // Make no changes, hope for the best. + return false; + } + + stk::mesh::MetaData & meta = mesh.mesh_meta_data(); + stk::mesh::Selector locally_owned_selector(meta.locally_owned_part()); + + std::vector elems; + std::vector coincident_elems; + + std::vector entities_to_move; + + stk::mesh::get_selected_entities( locally_owned_selector, mesh.buckets(stk::topology::ELEMENT_RANK), elems ); + + for (auto && elem : elems) + { + get_partially_and_fully_coincident_elements(mesh, elem, coincident_elems); + + int new_owner = mesh.parallel_owner_rank(elem); + for (auto && nbr : coincident_elems) + { + const int elem_owner = mesh.parallel_owner_rank(nbr); + if (elem_owner < new_owner) new_owner = elem_owner; + } + + if (new_owner != mesh.parallel_owner_rank(elem)) + { + entities_to_move.push_back(stk::mesh::EntityProc(elem, new_owner)); + } + } + + const int local_made_moves = !entities_to_move.empty(); + int global_made_moves = false; + stk::all_reduce_max(mesh.parallel(), &local_made_moves, &global_made_moves, 1); + + if (global_made_moves) + { + mesh.change_entity_owner(entities_to_move); + } + return global_made_moves; +} + +bool +fix_face_and_edge_ownership(stk::mesh::BulkData & mesh) +{ + // This method exploits aura to choose which processor the faces and edges should be owned by + if (!mesh.is_automatic_aura_on()) + { + // Make no changes, hope for the best. + return false; + } + + stk::mesh::MetaData & meta = mesh.mesh_meta_data(); + stk::mesh::Selector locally_owned_selector(meta.locally_owned_part()); + + std::vector< stk::mesh::Entity> entities; + std::vector< stk::mesh::Entity> entity_elems; + + std::vector entities_to_move; + + for (stk::mesh::EntityRank entity_rank = stk::topology::EDGE_RANK; entity_rank <= stk::topology::FACE_RANK; ++entity_rank) + { + stk::mesh::get_selected_entities( locally_owned_selector, mesh.buckets(entity_rank), entities ); + + for (auto && entity : entities) + { + entity_elems.assign(mesh.begin_elements(entity), mesh.end_elements(entity)); + ThrowRequire(!entity_elems.empty()); + + int new_owner = mesh.parallel_size(); + for (auto && entity_elem : entity_elems) + { + const int elem_owner = mesh.parallel_owner_rank(entity_elem); + if (elem_owner < new_owner) new_owner = elem_owner; + } + + if (new_owner != mesh.parallel_owner_rank(entity)) + { + entities_to_move.push_back(stk::mesh::EntityProc(entity, new_owner)); + } + } + } + + const int local_made_moves = !entities_to_move.empty(); + int global_made_moves = false; + stk::all_reduce_max(mesh.parallel(), &local_made_moves, &global_made_moves, 1); + + if (global_made_moves) + { + mesh.change_entity_owner(entities_to_move); + } + return global_made_moves; +} + +bool +check_face_and_edge_ownership(const stk::mesh::BulkData & mesh) +{ + // This method exploits aura to choose which processor the faces and edges should be owned by + if (!mesh.is_automatic_aura_on()) + { + // Skip check if we don't have aura + return true; + } + + const stk::mesh::MetaData & meta = mesh.mesh_meta_data(); + stk::mesh::Selector locally_owned_selector(meta.locally_owned_part()); + + std::vector< stk::mesh::Entity> entities; + std::vector< stk::mesh::Entity> entity_elems; + std::vector< stk::mesh::Entity> entity_nodes; + + for (stk::mesh::EntityRank entity_rank = stk::topology::EDGE_RANK; entity_rank <= stk::topology::FACE_RANK; ++entity_rank) + { + const stk::mesh::BucketVector & buckets = mesh.get_buckets( entity_rank, locally_owned_selector ); + + stk::mesh::BucketVector::const_iterator ib = buckets.begin(); + stk::mesh::BucketVector::const_iterator ib_end = buckets.end(); + + for ( ; ib != ib_end; ++ib ) + { + const stk::mesh::Bucket & b = **ib; + const size_t length = b.size(); + for (size_t it_entity = 0; it_entity < length; ++it_entity) + { + stk::mesh::Entity entity = b[it_entity]; + const int entity_owner = mesh.parallel_owner_rank(entity); + + entity_nodes.assign(mesh.begin_nodes(entity), mesh.end_nodes(entity)); + entity_elems.assign(mesh.begin_elements(entity), mesh.end_elements(entity)); + + bool have_element_on_owning_proc = false; + for (std::vector::iterator it_elem = entity_elems.begin(); it_elem != entity_elems.end(); ++it_elem) + { + if (mesh.parallel_owner_rank(*it_elem) == entity_owner) + { + have_element_on_owning_proc = true; + break; + } + } + ThrowRequireMsg(have_element_on_owning_proc, "Error: " << mesh.entity_key(entity) + << " is owned on processor " << entity_owner + << " but does not have any elements that are owned on this processor."); + } + } + } + const bool success = true; + return success; +} + +bool +set_region_id_on_unset_entity_and_neighbors(stk::mesh::BulkData & mesh, stk::mesh::Entity & entity, stk::mesh::EntityRank entity_rank, stk::mesh::FieldBase & region_id_field, const unsigned region_id) +{ + unsigned * region_id_data = stk::mesh::field_data(reinterpret_cast&>(region_id_field), entity); + ThrowAssert(NULL != region_id_data); + if (*region_id_data != 0) + { + ThrowAssert(*region_id_data == region_id); + return false; + } + + *region_id_data = region_id; + + // now visit neighbors + const stk::mesh::Entity* begin_nodes = mesh.begin_nodes(entity); + const stk::mesh::Entity* end_nodes = mesh.end_nodes(entity); + + for (const stk::mesh::Entity* it_node = begin_nodes; it_node != end_nodes; ++it_node) + { + stk::mesh::Entity node = *it_node; + + const stk::mesh::Entity* node_entities_begin = mesh.begin(node, entity_rank); + const stk::mesh::Entity* node_entities_end = mesh.end(node, entity_rank); + + for (const stk::mesh::Entity* it_nbr = node_entities_begin; it_nbr != node_entities_end; ++it_nbr) + { + stk::mesh::Entity neighbor = *it_nbr; + set_region_id_on_unset_entity_and_neighbors(mesh, neighbor, entity_rank, region_id_field, region_id); + } + } + + return true; +} + +void +identify_isolated_regions(stk::mesh::Part & part, stk::mesh::FieldBase & region_id_field) +{ /* %TRACE[ON]% */ Trace trace__("krino::Mesh::identify_isolated_portions_of_part(stk::mesh::Part & part)"); /* %TRACE% */ + stk::mesh::BulkData & mesh = part.mesh_bulk_data(); + stk::mesh::MetaData & meta = mesh.mesh_meta_data(); + + stk::mesh::EntityRank entity_rank = part.primary_entity_rank(); + stk::mesh::Selector selector = part & meta.locally_owned_part(); + + std::vector entities; + stk::mesh::get_selected_entities( selector, mesh.buckets(entity_rank), entities ); + + // initialize region id + const stk::mesh::BucketVector & buckets = mesh.get_buckets(entity_rank, selector); + for (stk::mesh::BucketVector::const_iterator ib = buckets.begin(); ib != buckets.end(); ++ib ) + { + const stk::mesh::Bucket & b = **ib; + const size_t length = b.size(); + unsigned * region_id = stk::mesh::field_data(reinterpret_cast&>(region_id_field), b); + ThrowAssert(NULL != region_id); + std::fill(region_id, region_id+length, 0); + } + + unsigned local_region_counter = 0; + unsigned region_id = 1 + local_region_counter*mesh.parallel_size() + mesh.parallel_rank(); + + for (std::vector::iterator it_entity=entities.begin(); it_entity!=entities.end(); ++it_entity) + { + stk::mesh::Entity entity = *it_entity; + + const bool made_changes = set_region_id_on_unset_entity_and_neighbors(mesh, entity, entity_rank, region_id_field, region_id); + if (made_changes) + { + ++local_region_counter; + region_id = 1 + local_region_counter*mesh.parallel_size() + mesh.parallel_rank(); + } + } +} + +const unsigned * get_side_node_ordinals(stk::topology topology, unsigned side_ordinal) +{ + static std::vector< std::vector< std::vector > > all_side_node_ordinals(stk::topology::BEGIN_TOPOLOGY + stk::topology::NUM_TOPOLOGIES); + std::vector< std::vector > & topology_side_node_ordinals = all_side_node_ordinals[topology.value()]; + if (topology_side_node_ordinals.empty()) + { + const size_t num_sides = topology.num_sides(); + topology_side_node_ordinals.resize(num_sides); + for (size_t side_index = 0; side_index < num_sides; ++side_index) + { + std::vector & side_node_ordinals = topology_side_node_ordinals[side_index]; + side_node_ordinals.resize(topology.side_topology(side_index).num_nodes()); + topology.side_node_ordinals(side_index, side_node_ordinals.begin()); + } + } + return &topology_side_node_ordinals[side_ordinal][0]; +} + +const unsigned * get_edge_node_ordinals(stk::topology topology, unsigned edge_ordinal) +{ + static std::vector< std::vector< std::vector > > all_edge_node_ordinals(stk::topology::BEGIN_TOPOLOGY + stk::topology::NUM_TOPOLOGIES); + std::vector< std::vector > & topology_edge_node_ordinals = all_edge_node_ordinals[topology.value()]; + if (topology_edge_node_ordinals.empty()) + { + const size_t num_edges = topology.num_edges(); + topology_edge_node_ordinals.resize(num_edges); + for (size_t edge_index = 0; edge_index < num_edges; ++edge_index) + { + std::vector & edge_node_ordinals = topology_edge_node_ordinals[edge_index]; + edge_node_ordinals.resize(topology.edge_topology(edge_index).num_nodes()); + topology.edge_node_ordinals(edge_index, edge_node_ordinals.begin()); + } + } + return &topology_edge_node_ordinals[edge_ordinal][0]; +} + +stk::mesh::PartVector get_common_io_parts(const stk::mesh::BulkData & mesh, const std::vector entities) +{ + stk::mesh::PartVector common_io_parts; + + bool first = true; + for (auto&& entity : entities) + { + stk::mesh::PartVector entity_io_parts = filter_non_io_parts(mesh.bucket(entity).supersets()); + std::sort(entity_io_parts.begin(), entity_io_parts.end()); + if (first) + { + first = false; + common_io_parts.swap(entity_io_parts); + } + else + { + stk::mesh::PartVector working_set; + working_set.swap(common_io_parts); + std::set_intersection(working_set.begin(),working_set.end(),entity_io_parts.begin(),entity_io_parts.end(),std::back_inserter(common_io_parts)); + } + } + return common_io_parts; +} + +stk::mesh::PartVector get_removable_parts(const stk::mesh::BulkData & mesh, const stk::mesh::Bucket & bucket) +{ + stk::mesh::PartVector removable_parts; + for ( auto&& part : bucket.supersets() ) + { + stk::mesh::EntityRank part_rank = part->primary_entity_rank(); + if ((part_rank == stk::topology::INVALID_RANK || part_rank == bucket.entity_rank()) && + (!stk::mesh::is_auto_declared_part(*part) || stk::mesh::is_topology_root_part(*part))) + { + removable_parts.push_back(part); + } + } + return removable_parts; +} + +stk::mesh::PartVector get_removable_parts(const stk::mesh::BulkData & mesh, const stk::mesh::Entity entity) +{ + return get_removable_parts(mesh, mesh.bucket(entity)); +} + +//-------------------------------------------------------------------------------- +bool is_refinement_child(const stk::mesh::BulkData & stk_bulk, const stk::mesh::Entity entity) +{ + const stk::mesh::EntityRank entity_rank = stk_bulk.entity_rank(entity); + const unsigned num_family_trees = stk_bulk.num_connectivity(entity, stk::topology::CONSTRAINT_RANK); + const stk::mesh::Entity* family_trees = stk_bulk.begin(entity, stk::topology::CONSTRAINT_RANK); + for (unsigned ifamily=0; ifamily < num_family_trees; ++ifamily) + { + const stk::mesh::Entity ft = family_trees[ifamily]; + const stk::mesh::Entity* family_tree_entities = stk_bulk.begin(ft, entity_rank); + if(family_tree_entities[0] != entity) return true; + } + return false; +} + +void +get_refinement_immediate_children(const stk::mesh::BulkData& stk_bulk, stk::mesh::Entity parent, std::vector & children) +{ + children.clear(); + stk::mesh::EntityRank entity_rank = stk_bulk.entity_rank(parent); + const unsigned num_family_trees = stk_bulk.num_connectivity(parent, stk::topology::CONSTRAINT_RANK); + const stk::mesh::Entity* family_trees = stk_bulk.begin(parent, stk::topology::CONSTRAINT_RANK); + for (unsigned ifamily=0; ifamily < num_family_trees; ++ifamily) + { + const stk::mesh::Entity* family_tree_entities = stk_bulk.begin(family_trees[ifamily], entity_rank); + const stk::mesh::Entity tree_parent = family_tree_entities[0]; // 0th entry in the family_tree is the parent + if (parent == tree_parent) // I am the parent + { + const unsigned num_family_tree_entities = stk_bulk.num_connectivity(family_trees[ifamily], entity_rank); + for (unsigned ichild=1; ichild < num_family_tree_entities; ++ichild) + { + children.push_back(family_tree_entities[ichild]); + } + } + } +} + +//-------------------------------------------------------------------------------- + +bool +has_refinement_children(const stk::mesh::BulkData& stk_bulk, stk::mesh::Entity parent) +{ + stk::mesh::EntityRank entity_rank = stk_bulk.entity_rank(parent); + const unsigned num_family_trees = stk_bulk.num_connectivity(parent, stk::topology::CONSTRAINT_RANK); + const stk::mesh::Entity* family_trees = stk_bulk.begin(parent, stk::topology::CONSTRAINT_RANK); + for (unsigned ifamily=0; ifamily < num_family_trees; ++ifamily) + { + const stk::mesh::Entity* family_tree_entities = stk_bulk.begin(family_trees[ifamily], entity_rank); + const stk::mesh::Entity tree_parent = family_tree_entities[0]; // 0th entry in the family_tree is the parent + if (parent == tree_parent) // I am the parent + { + return true; + } + } + return false; +} + +//-------------------------------------------------------------------------------- + +namespace { +void +get_refinement_leaf_children(const stk::mesh::BulkData& stk_bulk, const std::vector & children, std::vector & leaf_children) +{ + std::vector grand_children; + for (auto&& child : children) + { + get_refinement_immediate_children(stk_bulk, child, grand_children); + if (grand_children.empty()) + { + leaf_children.push_back(child); + } + else + { + get_refinement_leaf_children(stk_bulk, grand_children, leaf_children); + } + } +} + +void +get_refinement_all_children(const stk::mesh::BulkData& stk_bulk, const std::vector & children, std::vector & all_children) +{ + std::vector grand_children; + for (auto&& child : children) + { + get_refinement_immediate_children(stk_bulk, child, grand_children); + all_children.insert(all_children.end(), grand_children.begin(), grand_children.end()); + get_refinement_all_children(stk_bulk, grand_children, all_children); + } +} +} + +//-------------------------------------------------------------------------------- + +void +get_refinement_leaf_children(const stk::mesh::BulkData& stk_bulk, stk::mesh::Entity entity, std::vector & leaf_children) +{ + leaf_children.clear(); + std::vector children; + get_refinement_immediate_children(stk_bulk, entity, children); + get_refinement_leaf_children(stk_bulk, children, leaf_children); +} + +void +get_refinement_all_children(const stk::mesh::BulkData& stk_bulk, stk::mesh::Entity entity, std::vector & all_children) +{ + std::vector children; + get_refinement_immediate_children(stk_bulk, entity, children); + all_children = children; + get_refinement_all_children(stk_bulk, children, all_children); +} + +void set_relation_permutation(stk::mesh::BulkData & mesh, stk::mesh::Entity from, stk::mesh::Entity to, stk::mesh::ConnectivityOrdinal to_ord, stk::mesh::Permutation to_permutation) +{ + const stk::mesh::EntityRank from_rank = mesh.entity_rank(from); + const stk::mesh::EntityRank to_rank = mesh.entity_rank(to); + + stk::mesh::Entity const* fwd_rels = mesh.begin(from, to_rank); + stk::mesh::ConnectivityOrdinal const* fwd_ords = mesh.begin_ordinals(from, to_rank); + stk::mesh::Permutation * fwd_perms = const_cast(mesh.begin_permutations(from, to_rank)); + const int num_fwd = mesh.num_connectivity(from, to_rank); + + stk::mesh::Entity const* back_rels = mesh.begin(to, from_rank); + stk::mesh::ConnectivityOrdinal const* back_ords = mesh.begin_ordinals(to, from_rank); + stk::mesh::Permutation * back_perms = const_cast(mesh.begin_permutations(to, from_rank)); + const int num_back = mesh.num_connectivity(to,from_rank); + + // Find and change fwd connectivity + for (int i = 0; i < num_fwd; ++i, ++fwd_rels, ++fwd_ords, ++fwd_perms) { + // Allow clients to make changes to permutation + // Permutations do not affect Relation ordering, so this is safe. + if (*fwd_rels == to && *fwd_ords == to_ord) { + *fwd_perms = to_permutation; + } + } + + // Find and change back connectivity + for (int i = 0; i < num_back; ++i, ++back_rels, ++back_ords, ++back_perms) { + // Allow clients to make changes to permutation + // Permutations do not affect Relation ordering, so this is safe. + if (*back_rels == from && *back_ords == to_ord) { + *back_perms = to_permutation; + } + } +} + +std::pair +determine_shell_side_ordinal_and_permutation(const stk::mesh::BulkData & mesh, stk::mesh::Entity shell, stk::mesh::Entity side) +{ + // The input shell may or may not be attached to side, but should be coincident with the side. + // We will figure out what the ordinal for this shell-side relation based on the existing + // connectivity of the side. + const unsigned num_side_elems = mesh.num_elements(side); + ThrowRequireMsg(num_side_elems > 0, "Cannot determine shell_side_ordinal for completely disconnected side."); + + stk::mesh::EntityRank side_rank = mesh.mesh_meta_data().side_rank(); + stk::topology side_topology = mesh.bucket(side).topology(); + const stk::mesh::Entity * side_elems = mesh.begin_elements(side); + const stk::mesh::ConnectivityOrdinal * side_elem_ordinals = mesh.begin_element_ordinals(side); + const stk::mesh::Permutation * side_elem_permutations = mesh.begin_element_permutations(side); + + std::pair result(stk::mesh::INVALID_CONNECTIVITY_ORDINAL, stk::mesh::INVALID_PERMUTATION); + for (unsigned it = 0; it relative_rank); + + const stk::mesh::Entity * relative_nodes = mesh.begin_nodes(relative); + + const stk::EquivalentPermutation equiv = stk::mesh::sub_rank_equivalent(mesh, entity, ordinal, relative_rank, relative_nodes); + if(!equiv.is_equivalent) + { + ThrowErrorMsg("Could not find connection between " << mesh.entity_key(entity) <<" and " + << mesh.entity_key(relative) << " with ordinal " << ordinal + << debug_entity(mesh, entity) << debug_entity(mesh, relative)); + } + + return static_cast(equiv.permutation_number); +} + +std::pair +determine_ordinal_and_permutation(const stk::mesh::BulkData & mesh, const stk::mesh::Entity entity, const stk::mesh::Entity relative) +{ + const stk::mesh::EntityRank relative_rank = mesh.entity_rank(relative); + ThrowAssert(mesh.entity_rank(entity) > relative_rank); + stk::topology relative_topology = mesh.bucket(relative).topology(); + ThrowAssert(relative_topology.num_nodes() == mesh.num_nodes(relative)); + + const stk::mesh::Entity * relative_nodes = mesh.begin_nodes(relative); + + stk::topology entity_topology = mesh.bucket(entity).topology(); + const bool looking_for_shell_side = entity_topology.is_shell() && relative_rank == mesh.mesh_meta_data().side_rank(); + + for(size_t i = 0; i < entity_topology.num_sub_topology(relative_rank); ++i) + { + if (entity_topology.sub_topology(relative_rank, i) == relative_topology) + { + const stk::EquivalentPermutation equiv = stk::mesh::sub_rank_equivalent(mesh, entity, stk::mesh::ConnectivityOrdinal(i), relative_rank, relative_nodes); + const bool match = equiv.is_equivalent && (!looking_for_shell_side || equiv.permutation_number < relative_topology.num_positive_permutations()); + if(match) + { + return std::pair(static_cast(i), static_cast(equiv.permutation_number)); + } + } + } + ThrowRuntimeError("Could not find connection between " << mesh.entity_key(entity) << " and " << mesh.entity_key(relative) << debug_entity(mesh, entity) << debug_entity(mesh, relative)); +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_MeshHelpers.hpp b/packages/krino/krino/krino_lib/Akri_MeshHelpers.hpp new file mode 100644 index 000000000000..563be698eaf4 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MeshHelpers.hpp @@ -0,0 +1,182 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_MeshHelpers_h +#define Akri_MeshHelpers_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino { class FieldRef; } + +namespace krino { + +typedef std::pair TopologyPartPair; +typedef std::vector TopologyPartVector; + +struct StkMeshEntities +{ + typedef stk::mesh::Entity value_type; + const value_type *mBegin; + const value_type *mEnd; + const value_type * begin() const { return mBegin; } + const value_type * end() const { return mEnd; } + size_t size() const { return mEnd - mBegin; } + bool empty() const { return mEnd == mBegin; } + value_type operator[](int i) const { return *(mBegin + i); } +}; + +void fill_element_node_coordinates(const stk::mesh::BulkData & mesh, stk::mesh::Entity element, const FieldRef coordsField, std::vector & elementNodeCoords); +void fill_procs_owning_or_sharing_or_ghosting_node(const stk::mesh::BulkData& bulkData, stk::mesh::Entity node, std::vector & procsOwningSharingOrGhostingNode); +double compute_maximum_element_size(stk::mesh::BulkData& mesh); +void compute_element_quality(const stk::mesh::BulkData & mesh, double & minEdgeLength, double & maxEdgeLength, double & minVolume, double & maxVolume); +void delete_all_entities_using_nodes_with_nodal_volume_below_threshold(stk::mesh::BulkData & mesh, const stk::mesh::Selector & blockSelector, const double threshold); +std::vector get_side_permutation(stk::topology topology, stk::mesh::Permutation node_permutation); +const stk::mesh::Part & find_element_part(const stk::mesh::BulkData& mesh, stk::mesh::Entity elem); +bool check_induced_parts(const stk::mesh::BulkData & mesh); +void attach_sides_to_elements(stk::mesh::BulkData & mesh); +void attach_entity_to_elements(stk::mesh::BulkData & mesh, stk::mesh::Entity entity); +void unpack_entities_from_other_procs(const stk::mesh::BulkData & mesh, std::set & entities, stk::CommSparse &commSparse); +void update_node_activation(stk::mesh::BulkData & mesh, stk::mesh::Part & active_part); +void activate_all_entities(stk::mesh::BulkData & mesh, stk::mesh::Part & active_part); +void destroy_custom_ghostings(stk::mesh::BulkData & mesh); +void delete_mesh_entities(stk::mesh::BulkData & mesh, std::vector & child_elems); +void debug_print_selector_parts(const stk::mesh::Selector & selector); +stk::mesh::PartVector filter_non_io_parts(const stk::mesh::PartVector & all_parts); +void activate_selected_sides_touching_active_elements(stk::mesh::BulkData & mesh, const stk::mesh::Selector & side_selector, stk::mesh::Part & active_part); +void get_partially_and_fully_coincident_elements(const stk::mesh::BulkData & mesh, stk::mesh::Entity elem, std::vector & coincident_elems); +bool check_element_side_connectivity(const stk::mesh::BulkData & mesh, const stk::mesh::Part & exterior_boundary_part, const stk::mesh::Part & active_part); +bool check_coincident_elements(const stk::mesh::BulkData & mesh, const stk::mesh::Part & active_part); +bool fix_coincident_element_ownership(stk::mesh::BulkData & mesh); +bool fix_face_and_edge_ownership(stk::mesh::BulkData & mesh); +bool check_face_and_edge_ownership(const stk::mesh::BulkData & mesh); +bool check_face_and_edge_relations(const stk::mesh::BulkData & mesh); +bool check_shared_entity_nodes(const stk::mesh::BulkData & mesh, stk::mesh::EntityKey remote_entity_key, std::vector & remote_entity_node_ids); +bool check_shared_entity_nodes(const stk::mesh::BulkData & mesh, std::vector & entities); +bool check_shared_entity_nodes(const stk::mesh::BulkData & mesh); +void disconnect_entity(stk::mesh::BulkData & mesh, stk::mesh::Entity entity); +bool disconnect_and_destroy_entity(stk::mesh::BulkData & mesh, stk::mesh::Entity entity); + +stk::mesh::PartVector get_common_io_parts(const stk::mesh::BulkData & mesh, const std::vector entities); +stk::mesh::PartVector get_removable_parts(const stk::mesh::BulkData & mesh, const stk::mesh::Bucket & bucket); +stk::mesh::PartVector get_removable_parts(const stk::mesh::BulkData & mesh, const stk::mesh::Entity entity); + +void +store_edge_node_parent_ids(const stk::mesh::BulkData & mesh, + const FieldRef & parent_id_field, + stk::mesh::Entity edge_node_entity, + stk::mesh::EntityId parent0_id, + stk::mesh::EntityId parent1_id); + +std::array +get_edge_node_parent_ids(const stk::mesh::BulkData & mesh, + const FieldRef & parent_id_field, + const stk::mesh::Entity edge_node_entity); + +void get_parent_nodes_from_child(const stk::mesh::BulkData & mesh, + stk::mesh::Entity child, const FieldRef & parent_id_field, + std::set & parent_nodes); + +double compute_child_position(const stk::mesh::BulkData & mesh, stk::mesh::Entity child, stk::mesh::Entity parent0, stk::mesh::Entity parent1); + +// topology helpers +// NOTE: These use static storage, but it does not depend on anything so it should be ok for nested or multithreaded usage. +const unsigned * get_side_node_ordinals(stk::topology topology, unsigned side_ordinal); +const unsigned * get_edge_node_ordinals(stk::topology topology, unsigned edge_ordinal); + +std::string debug_entity(const stk::mesh::BulkData & mesh, stk::mesh::Entity entity); +std::string debug_entity(const stk::mesh::BulkData & mesh, stk::mesh::Entity entity, const bool includeFields); + +struct ChildNodeRequest +{ + std::vector parents; + stk::mesh::Entity* child; + + ChildNodeRequest(const std::vector & in_parents, stk::mesh::Entity *in_child) + : parents(in_parents), child(in_child) {} +}; +struct SideRequest +{ + stk::mesh::Entity element; + unsigned element_side_ordinal; + stk::mesh::PartVector side_parts; + + SideRequest(stk::mesh::Entity in_element, unsigned in_element_side_ordinal, const stk::mesh::PartVector & in_side_parts) + : element(in_element), element_side_ordinal(in_element_side_ordinal), side_parts(in_side_parts) {} +}; + +void batch_create_child_nodes(stk::mesh::BulkData & mesh, const std::vector< ChildNodeRequest > & child_node_requests, const stk::mesh::PartVector & node_parts, bool assert_32bit_ids, bool make_64bit_ids); +void batch_create_sides(stk::mesh::BulkData & mesh, const std::vector< SideRequest > & side_requests); +void make_side_ids_consistent_with_stk_convention(stk::mesh::BulkData & mesh); + +double compute_element_volume_to_edge_ratio(stk::mesh::BulkData & mesh, stk::mesh::Entity element, const stk::mesh::Field * const coords_field); + +bool is_refinement_child(const stk::mesh::BulkData & stk_bulk, stk::mesh::Entity entity); +bool has_refinement_children(const stk::mesh::BulkData& stk_bulk, stk::mesh::Entity parent); +void get_refinement_immediate_children(const stk::mesh::BulkData& stk_bulk, stk::mesh::Entity parent, std::vector & children); +void get_refinement_leaf_children(const stk::mesh::BulkData& stk_bulk, stk::mesh::Entity entity, std::vector & leaf_children); +void get_refinement_all_children(const stk::mesh::BulkData& stk_bulk, stk::mesh::Entity entity, std::vector & children); + +// Temporary method for manually correcting the relation permutation +void set_relation_permutation(stk::mesh::BulkData & mesh, stk::mesh::Entity from, stk::mesh::Entity to, stk::mesh::ConnectivityOrdinal to_ord, stk::mesh::Permutation to_permutation); + +std::pair +determine_shell_side_ordinal_and_permutation(const stk::mesh::BulkData & mesh, stk::mesh::Entity shell, stk::mesh::Entity side); + +stk::mesh::Permutation +determine_permutation(const stk::mesh::BulkData & mesh, const stk::mesh::Entity entity, const stk::mesh::Entity relative, const stk::mesh::ConnectivityOrdinal ordinal); + +std::pair +determine_ordinal_and_permutation(const stk::mesh::BulkData & mesh, const stk::mesh::Entity entity, const stk::mesh::Entity relative); + +inline stk::mesh::Entity find_entity_by_ordinal(const stk::mesh::BulkData &mesh, stk::mesh::Entity entity, stk::mesh::EntityRank rank, const unsigned ordinal) +{ + stk::mesh::ConnectivityOrdinal const* relative_ordinals = mesh.begin_ordinals(entity, rank); + stk::mesh::Entity const* relatives = mesh.begin(entity, rank); + const int num_relatives = mesh.num_connectivity(entity, rank); + for (int i = 0; i < num_relatives; ++i) + { + if (relative_ordinals[i] == ordinal) { + return relatives[i]; + } + } + return stk::mesh::Entity(); +} + +template +void pack_entities_for_owning_proc(const stk::mesh::BulkData & mesh, + const CONTAINER & entities, + stk::CommSparse &commSparse) +{ + stk::pack_and_communicate(commSparse,[&]() + { + for (auto entity : entities) + { + const int entityOwner = mesh.parallel_owner_rank(entity); + if (commSparse.parallel_rank() != entityOwner) + commSparse.send_buffer(entityOwner).pack(mesh.entity_key(entity)); + } + }); +} + +} // namespace krino + +#endif // Akri_MeshHelpers_h diff --git a/packages/krino/krino/krino_lib/Akri_MeshInputOptions.cpp b/packages/krino/krino/krino_lib/Akri_MeshInputOptions.cpp new file mode 100644 index 000000000000..aeda2c5e95eb --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MeshInputOptions.cpp @@ -0,0 +1,52 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include + +namespace krino { + +MeshInputOptions::Registry MeshInputOptions::the_registry; + +std::shared_ptr +MeshInputOptions::get_or_create(const std::string & model_name) +{ + std::shared_ptr options = std::make_shared(model_name); + std::pair result = the_registry.insert(options); + + if ( ! result.second ) { + stk::RuntimeWarningAdHoc() << "A mesh database named '" << model_name + << "' has already been defined and will be reused"; + } + + return *result.first; +} + +MeshInputOptions * +MeshInputOptions::get(const std::string &model_name) +{ + MeshInputOptions *options = nullptr; + std::shared_ptr tmp = std::make_shared(model_name); + + Registry::iterator iter = the_registry.find(tmp); + + if ( iter != the_registry.end() ) + options = iter->get() ; + + return options; +} + +int MeshInputOptions::get_generated_mesh_spatial_dimension() const +{ + ThrowRequire(use_generated_mesh()); + return (my_generated_mesh_domain_type == GENERATED_2D_MESH_FOR_INTERFACE_BOUNDING_BOX || my_generated_mesh_domain.size() == 4) ? 2 : 3; +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_MeshInputOptions.hpp b/packages/krino/krino/krino_lib/Akri_MeshInputOptions.hpp new file mode 100644 index 000000000000..203d4ee5d6bd --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MeshInputOptions.hpp @@ -0,0 +1,112 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_MeshInputOptions_h +#define Akri_MeshInputOptions_h + +#include +#include +#include +#include +#include +#include + +namespace krino { + + class MeshInputOptions { + + public: + static std::shared_ptr get_or_create(const std::string & model_name); + static MeshInputOptions * get(const std::string & model_name); + + enum GeneratedMeshDomainType + { + NO_GENERATED_MESH=0, + GENERATED_MESH_FOR_SPECIFIED_DOMAIN, + GENERATED_2D_MESH_FOR_INTERFACE_BOUNDING_BOX, + GENERATED_3D_MESH_FOR_INTERFACE_BOUNDING_BOX + }; + + MeshInputOptions(const std::string & name) : + my_name(name), + my_filename("%B.g"), + my_filetype("exodusII"), + my_generated_mesh_domain_type(NO_GENERATED_MESH), + myGeneratedMeshStructureType(BoundingBoxMeshStructureType::CUBIC_BOUNDING_BOX_MESH), + my_generated_mesh_size(-1.0) {} + + const std::string & get_name() const { return my_name; } + bool is_valid() const + { + return !my_name.empty() && ((! my_filetype.empty() && ! my_filename.empty()) || use_generated_mesh()) ; + } + + void set_generated_mesh_structure_type(BoundingBoxMeshStructureType type) { myGeneratedMeshStructureType = type; } + BoundingBoxMeshStructureType get_generated_mesh_structure_type() const { return myGeneratedMeshStructureType; } + + bool use_generated_mesh() const { return my_generated_mesh_domain_type != NO_GENERATED_MESH; } + + void set_generated_mesh_domain_type(GeneratedMeshDomainType type) { my_generated_mesh_domain_type = type; } + GeneratedMeshDomainType get_generated_mesh_domain_type() const { return my_generated_mesh_domain_type; } + + int get_generated_mesh_spatial_dimension() const; + + void set_generated_mesh_size(double size) { my_generated_mesh_size = size; } + double get_generated_mesh_size() const { return my_generated_mesh_size; } + + void set_generated_mesh_element_type(stk::topology top) { my_generated_mesh_element_type = top; } + stk::topology get_generated_mesh_element_type() const { return my_generated_mesh_element_type; } + + void set_generated_mesh_domain(const std::vector & domain) { my_generated_mesh_domain = domain; } + const std::vector & get_generated_mesh_domain() const { return my_generated_mesh_domain; } + + void set_filename(std::string filename) { my_filename = filename; } + const std::string & get_filename() const { return my_filename; } + + void set_filetype(std::string type) { my_filetype = type; } + const std::string & get_filetype() const { return my_filetype; } + + void set_decomposition_method(std::string decomposition_method) { my_decomposition_method = decomposition_method; } + const std::string & get_decomposition_method() const { return my_decomposition_method; } + + void set_coordinate_system(std::string coordinate_system) { my_coordinate_system = coordinate_system; } + const std::string & get_coordinate_system() const { return my_coordinate_system; } + + void add_property(const Ioss::Property & property) { my_properties.add(property); } + Ioss::PropertyManager & get_properties() { return my_properties; } + + private: + struct compare_ptr { + bool operator()( std::shared_ptr const lhs , + std::shared_ptr const rhs ) const + { + return lhs.get() == nullptr ? rhs.get() != nullptr : + ( rhs.get() == nullptr ? false : lhs->get_name() < rhs->get_name() ); + } + }; + + typedef std::set, compare_ptr> Registry; + static Registry the_registry; + + private: + std::string my_name; + std::string my_filename; + std::string my_filetype; + GeneratedMeshDomainType my_generated_mesh_domain_type; + BoundingBoxMeshStructureType myGeneratedMeshStructureType; + std::vector my_generated_mesh_domain; + stk::topology my_generated_mesh_element_type = stk::topology::INVALID_TOPOLOGY; + double my_generated_mesh_size; + std::string my_decomposition_method; + std::string my_coordinate_system; + Ioss::PropertyManager my_properties; + }; + +} // namespace krino + +#endif /* Akri_MeshInputOptions_h */ diff --git a/packages/krino/krino/krino_lib/Akri_MeshSurface.cpp b/packages/krino/krino/krino_lib/Akri_MeshSurface.cpp new file mode 100644 index 000000000000..36a535fb3da4 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MeshSurface.cpp @@ -0,0 +1,1019 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace krino{ + +void Parallel_Facet_File_Reader::read(const std::string & read_description, const std::function & read_function) +{ + bool ok_locally = true; + + if (0 == stk::EnvData::parallel_rank() ) + { + my_input.exceptions(std::ifstream::failbit | std::ifstream::badbit); + try + { + read_function(); + } + catch (std::ifstream::failure & e) + { + ok_locally = false; + } + } + const bool ok_globally = stk::is_true_on_all_procs(stk::EnvData::parallel_comm(), ok_locally); + if (!ok_globally) + { + ThrowRuntimeError("Error " << read_description << " for file " << my_filename); + } +} + +void Parallel_Facet_File_Reader::open_file() +{ + read("opening file", [this](){my_input.open(my_filename.c_str());}); +} + +void Parallel_Facet_File_Reader::get_batch_size(const int local_num_facets, int & batch_size, int & num_batches) +{ + int num_facets = 0; + stk::all_reduce_sum( stk::EnvData::parallel_comm(), &local_num_facets, &num_facets, 1 ); + + const int min_batch_size = std::min(2048, num_facets); + batch_size = std::max(min_batch_size, 1+num_facets/stk::EnvData::parallel_size()); + num_batches = 1+num_facets/batch_size; + + if(krinolog.shouldPrint(LOG_DEBUG)) + krinolog << "Reading " << num_facets << " facets, using " << num_batches << " batches of " << batch_size << "." << stk::diag::dendl; +} + +Faceted_Surface_From_File::Faceted_Surface_From_File(const std::string & surface_name, const stk::diag::Timer &parent_timer) +: Faceted_Surface(surface_name), + my_timer("Facet File Reader", parent_timer), + my_built_local_facets(false) +{ +} + +void Faceted_Surface_From_File::build_local_facets(const BoundingBox & proc_bbox) +{ + stk::diag::TimeBlock timer_(my_timer); + + if(my_built_local_facets) return; + + std::vector proc_bboxes; + BoundingBox::gather_bboxes( proc_bbox, proc_bboxes ); + + read_file(proc_bboxes); + my_built_local_facets = true; +} + +void Parallel_Facet_File_Reader::close_file() +{ + read("closing file", [this](){my_input.close();}); +} + +FACSurface::FACSurface(const std::string & surface_name, + const stk::diag::Timer &parent_timer, + const std::string & filename, + const int sign, + const Vector3d & scale) + : Faceted_Surface_From_File(surface_name, parent_timer), + my_reader(filename), + my_dist_sign(sign), + my_scale(scale) +{ + // Make sure the file is openable, but close it for now to avoid ulimit restrictions. + my_reader.open_file(); + my_reader.close_file(); +} + +BoundingBox +FACSurface::get_bounding_box() +{ + BoundingBox bbox; + + my_reader.open_file(); + + std::vector points; + my_reader.read("reading points", [this, &points](){read_points(points);}); + + for (auto && point : points) + { + bbox.accommodate(point); + } + + my_reader.close_file(); + + bbox.global_reduce(); + return bbox; +} + +void FACSurface::read_file(const std::vector & proc_bboxes) +{ + my_reader.open_file(); + + std::ifstream & input = my_reader.input(); + std::vector points; + my_reader.read("reading points", [this, &points](){read_points(points);}); + + int num_facets = 0; + my_reader.read("reading number of facets", [&input, &num_facets](){input >> num_facets; ThrowRequire(num_facets > 0);}); + + int batch_size = 0; + int num_batches = 0; + my_reader.get_batch_size(num_facets, batch_size, num_batches); + + for (int batch = 0; batch < num_batches; ++batch) + { + const int current_batch_size = std::min(batch_size, num_facets-batch*batch_size); + my_reader.read("reading facets", [this, &points, current_batch_size, num_facets](){read_facets(current_batch_size, num_facets, points);}); + parallel_distribute_facets(current_batch_size, proc_bboxes); + } + + my_reader.close_file(); +} + +void FACSurface::read_points(std::vector & points) +{ + std::ifstream & input = my_reader.input(); + int num_points; + input >> num_points; + ThrowRequire(num_points > 0); + points.reserve(num_points); + for ( int i = 0; i < num_points; i++ ) + { + int id; + double X, Y, Z; + input >> id; + ThrowRequire(id >= 0 && id < num_points); + input >> X >> Y >> Z; + points.emplace_back(X*my_scale[0], Y*my_scale[1], Z*my_scale[2]); + } +} + +void FACSurface::read_facets(const int batch_size, const int num_facets, const std::vector & points) +{ + std::ifstream & input = my_reader.input(); + const int num_points = points.size(); + for ( int i = 0; i < batch_size; ++i ) + { + int id; + int p1, p2, p3; + input >> id; + ThrowRequire(id >= 0 && id < num_facets); + input >> p1 >> p2 >> p3; + + ThrowRequire(p1 >= 0 && p1 < num_points); + ThrowRequire(p2 >= 0 && p2 < num_points); + ThrowRequire(p3 >= 0 && p3 < num_points); + + if (my_dist_sign == -1) + { + // permute to flip normal direction + int tmp = p1; + p1 = p2; + p2 = tmp; + } + std::unique_ptr facet = std::make_unique( points[p1], points[p2], points[p3] ); + add( std::move(facet) ); + } +} + +PLYSurface::PLYSurface(const std::string & surface_name, + const stk::diag::Timer &parent_timer, + const std::string & filename, + const int sign, + const Vector3d & scale) + : Faceted_Surface_From_File(surface_name, parent_timer), + my_reader(filename), + my_dist_sign(sign), + my_scale(scale) +{ + // Make sure the file is openable, but close it for now to avoid ulimit restrictions. + my_reader.open_file(); + my_reader.close_file(); +} + +BoundingBox PLYSurface::get_bounding_box() +{ + BoundingBox bbox; + + my_reader.open_file(); + + int num_points = 0; + int num_facets = 0; + + my_reader.read("reading file header", [this, &num_points, &num_facets](){read_header(num_points, num_facets);}); + + std::vector points; + my_reader.read("reading points", [this, num_points, &points](){read_points(num_points, points);}); + + for (auto && point : points) + { + bbox.accommodate(point); + } + + my_reader.close_file(); + + bbox.global_reduce(); + + return bbox; +} + +void PLYSurface::read_file(const std::vector & proc_bboxes) +{ + my_reader.open_file(); + + int num_points = 0; + int num_facets = 0; + + my_reader.read("reading file header", [this, &num_points, &num_facets](){read_header(num_points, num_facets);}); + + std::vector points; + my_reader.read("reading points", [this, num_points, &points](){read_points(num_points, points);}); + + int batch_size = 0; + int num_batches = 0; + my_reader.get_batch_size(num_facets, batch_size, num_batches); + + for (int batch = 0; batch < num_batches; ++batch) + { + const int current_batch_size = std::min(batch_size, num_facets-batch*batch_size); + my_reader.read("reading facets", + [this, &points, current_batch_size]() { read_facets(current_batch_size, points); }); + parallel_distribute_facets(current_batch_size, proc_bboxes); + } + + my_reader.close_file(); +} + +void PLYSurface::read_header(int & num_points, int & num_facets) +{ + std::ifstream & input = my_reader.input(); + num_points = 0; + num_facets = 0; + + // Read in the file identifier + std::string symbol; + input >> symbol; + ThrowRequire(symbol.compare("ply") == 0); + + while (symbol.compare("end_header") != 0) + { + ThrowErrorMsgIf(input.eof(), "Problem reading PLY file, reached end of file."); + input >> symbol; + if (symbol.compare("element") == 0) + { + input >> symbol; + if (symbol.compare("vertex") == 0) + { + input >> num_points; + } + else if (symbol.compare("face") == 0) + { + input >> num_facets; + } + } + } +} + +void PLYSurface::read_points(const int num_points, std::vector & points) +{ + std::ifstream & input = my_reader.input(); + points.clear(); + points.reserve(num_points); + for ( int i = 0; i < num_points; i++ ) + { + double X, Y, Z; + input >> X >> Y >> Z; + points.emplace_back(X*my_scale[0], Y*my_scale[1], Z*my_scale[2]); + } + // Move to start of next line to prepare for reading facets + std::string line; + std::getline(input,line); +} + +void PLYSurface::read_facets(const int batch_size, const std::vector & points) +{ + std::ifstream & input = my_reader.input(); + const unsigned num_points = points.size(); + std::string line; + for ( int i = 0; i < batch_size; ++i ) + { + unsigned num_facet_nodes; + unsigned p1, p2, p3; + std::getline(input,line); + std::stringstream linestream(line); + linestream >> num_facet_nodes; + ThrowRequireMsg(num_facet_nodes == 3, "Failed to read face connectivity correctly."); + linestream >> p1 >> p2 >> p3; + ThrowRequire(p1 < num_points); + ThrowRequire(p2 < num_points); + ThrowRequire(p3 < num_points); + + if (my_dist_sign == -1) + { + // permute to flip normal direction + int tmp = p1; + p1 = p2; + p2 = tmp; + } + std::unique_ptr facet = std::make_unique( points[p1], points[p2], points[p3] ); + add( std::move(facet) ); + } +} + +STLSurface::STLSurface(const std::string & surface_name, + const stk::diag::Timer &parent_timer, + const std::string & filename, + const int sign, + const Vector3d & scale) + : Faceted_Surface_From_File(surface_name, parent_timer), + my_is_ascii(true), + my_reader(filename), + my_dist_sign(sign), + my_scale(scale) +{ + // Make sure the file is openable, but close it for now to avoid ulimit restrictions. + my_reader.open_file(); + my_reader.close_file(); +} + +static std::unique_ptr build_facet(const std::array & pts, const int sign) +{ + unsigned p0 = 0; + unsigned p1 = 1; + unsigned p2 = 2; + if (sign == -1) + { + // permute to flip normal direction + p1 = 2; + p2 = 1; + } + + std::unique_ptr facet = std::make_unique( pts[p0], pts[p1], pts[p2] ); + return facet; +} + +static bool is_finite_normal(const Vector3d normal) +{ + bool finiteFlag = true; + if (!std::isfinite(normal[0]) || + !std::isfinite(normal[1]) || + !std::isfinite(normal[2]) ) + { + finiteFlag = false; + krinolog << "Krino::STLSurface found an invalid facet and is skipping." << std::endl; + krinolog << " facet normal " << normal[0] << " " << normal[1] << " " << normal[2] << std::endl; + } + return finiteFlag; +} + +static std::unique_ptr read_ascii_facet(std::ifstream & input, const Vector3d & scale, const int sign) +{ + std::string symbol, nx, ny, nz; + double X, Y, Z; + + // Read in the facet normal + input >> symbol; + ThrowRequire(symbol.compare("normal") == 0); + input >> nx >> ny >> nz; + const Vector3d normal(std::atof(nx.c_str()), std::atof(ny.c_str()), std::atof(nz.c_str())); + + // Read in the "outer loop" line + input >> symbol; + ThrowRequire(symbol.compare("outer") == 0); + input >> symbol; + ThrowRequire(symbol.compare("loop") == 0); + + // Read in the vertices + std::array pts; + for (int i=0; i<3; i++) { + input >> symbol; + ThrowRequire(symbol.compare("vertex") == 0); + input >> X >> Y >> Z; + + pts[i] = Vector3d(X*scale[0], Y*scale[1], Z*scale[2]); + } + + // Read in the "endloop" and "endfacet" lines + input >> symbol; + ThrowRequire(symbol.compare("endloop") == 0); + input >> symbol; + ThrowRequire(symbol.compare("endfacet") == 0); + + std::unique_ptr facet; + if (is_finite_normal(normal)) + { + facet = build_facet(pts, sign); + if (facet->degenerate()) + facet.reset(); + } + + return facet; +} + +static bool read_start_of_next_ascii_facet(std::ifstream & input) +{ + // Read the next line + std::string symbol; + input >> symbol; + return symbol.compare("facet") == 0; +} + +static float read_binary_float(std::ifstream& input) +{ + float val = 0; + input.read((char *) &val, sizeof(val)); + return val; +} + +static Vector3d read_binary_vector(std::ifstream& input) +{ + float x = read_binary_float(input); + float y = read_binary_float(input); + float z = read_binary_float(input); + return Vector3d(x, y, z); +} + +static std::unique_ptr read_binary_facet(std::ifstream & input, const Vector3d & scale, const int sign) +{ + const Vector3d normal = read_binary_vector(input); + + std::array pts; + for (auto && pt : pts) + { + const Vector3d vec = read_binary_vector(input); + pt = Vector3d(vec[0]*scale[0], vec[1]*scale[1], vec[2]*scale[2]); + } + + char dummy[2]; + input.read(dummy, 2); + + std::unique_ptr facet; + if (is_finite_normal(normal)) + { + facet = build_facet(pts, sign); + if (facet->degenerate()) + facet.reset(); + } + + return facet; +} + +BoundingBox +STLSurface::get_bounding_box() +{ + BoundingBox bbox; + + my_reader.open_file(); + + my_reader.read("reading file header", [this](){read_header();}); + my_is_ascii = stk::is_true_on_all_procs(stk::EnvData::parallel_comm(), my_is_ascii); + + if (my_is_ascii) + { + my_reader.read("reading facets", [this, &bbox](){bbox = get_ascii_facet_bounding_box();}); + } + else + { + unsigned numFacets = 0; + my_reader.read("reading num facets", [this, &numFacets](){numFacets = read_num_binary_facets();}); + my_reader.read("reading facets", [this, &bbox, numFacets](){bbox = get_binary_facet_bounding_box(numFacets);}); + } + + my_reader.close_file(); + + bbox.global_reduce(); + return bbox; +} + +void +STLSurface::read_file(const std::vector & proc_bboxes) +{ + my_reader.open_file(); + + my_reader.read("reading file header", [this](){read_header();}); + my_is_ascii = stk::is_true_on_all_procs(stk::EnvData::parallel_comm(), my_is_ascii); + + unsigned numBinaryFacets = 0; + if (!my_is_ascii) + my_reader.read("reading num facets", [this, &numBinaryFacets](){numBinaryFacets = read_num_binary_facets();}); + + const unsigned maxBatchSize = 8192; + + unsigned numValidFacetsRead = 0; + bool done = false; + while (!done) + { + unsigned batchSize = 0; + + if (my_is_ascii) + { + my_reader.read("reading facets", [this, &batchSize](){batchSize = read_ascii_facets(maxBatchSize);}); + } + else + { + my_reader.read("reading facets", [this, &batchSize, &numBinaryFacets](){batchSize = read_binary_facets(maxBatchSize, numBinaryFacets);}); + } + + const unsigned localBatchSize = batchSize; + stk::all_reduce_max( stk::EnvData::parallel_comm(), &localBatchSize, &batchSize, 1 ); + numValidFacetsRead += batchSize; + + parallel_distribute_facets(batchSize, proc_bboxes); + + done = batchSize < maxBatchSize; + } + + my_reader.close_file(); + + krinolog << "Read " << (my_is_ascii ? "ASCII" : "binary") << " STL file " << my_reader.filename() << " with " << numValidFacetsRead << " valid facets." << stk::diag::dendl; +} + +void +STLSurface::read_header() +{ + std::ifstream & input = my_reader.input(); + + std::string symbol; + input >> symbol; + my_is_ascii = symbol.compare("solid") == 0; + + if (my_is_ascii) + { + // Read in strings until you get to facet + while (symbol.compare("facet") != 0) + { + ThrowErrorMsgIf(input.eof(), "Problem reading STL file, no facets found."); + input >> symbol; + } + krinolog << "Reading ASCII STL file." << stk::diag::dendl; + } + else + { + char header_info[80] = ""; + input.clear(); + input.seekg(0); + input.read(header_info, 80); + ThrowErrorMsgIf(!input.good(), "Problem reading STL file, cannot read binary file header."); + } +} + +unsigned STLSurface::read_num_binary_facets() +{ + ThrowAssert(!my_is_ascii); + std::ifstream & input = my_reader.input(); + unsigned numFacets = 0; + input.read((char *) &numFacets, sizeof(numFacets)); + ThrowErrorMsgIf(!input.good(), "Problem reading STL file, cannot read number of facets."); + krinolog << "Reading binary STL file with " << numFacets << " facets." << stk::diag::dendl; + return numFacets; +} + +unsigned STLSurface::read_ascii_facets(const unsigned max_batch_size) +{ + std::ifstream & input = my_reader.input(); + + unsigned count = 0; + bool done = false; + while (!done) + { + std::unique_ptr facet = read_ascii_facet(input, my_scale, my_dist_sign); + if (facet) + { + add(std::move(facet)); + ++count; + } + done = (!read_start_of_next_ascii_facet(input) || count >= max_batch_size); + } + return count; +} + +unsigned STLSurface::read_binary_facets(const unsigned maxBatchSize, unsigned & numRemainingInFile) +{ + std::ifstream & input = my_reader.input(); + + unsigned count = 0; + bool done = numRemainingInFile == 0; + while (!done) + { + std::unique_ptr facet = read_binary_facet(input, my_scale, my_dist_sign); + if (facet) + { + add(std::move(facet)); + ++count; + } + done = (--numRemainingInFile == 0 || count >= maxBatchSize); + } + + return count; +} + +BoundingBox STLSurface::get_ascii_facet_bounding_box() +{ + std::ifstream & input = my_reader.input(); + + BoundingBox bbox; + + bool done = false; + while (!done) + { + std::unique_ptr facet = read_ascii_facet(input, my_scale, my_dist_sign); + if (facet) + bbox.accommodate(facet->bounding_box()); + done = !read_start_of_next_ascii_facet(input); + } + return bbox; +} + +BoundingBox STLSurface::get_binary_facet_bounding_box(const unsigned numFacets) +{ + std::ifstream & input = my_reader.input(); + + BoundingBox bbox; + + for (unsigned count=0; count facet = read_binary_facet(input, my_scale, my_dist_sign); + if (facet) + bbox.accommodate(facet->bounding_box()); + } + return bbox; +} + +EXOSurface::EXOSurface(const std::string & surface_name, + const stk::diag::Timer &parent_timer, + const std::string & filename, + const int sign, + const Vector3d & scale) + : Faceted_Surface_From_File(surface_name, parent_timer), + my_filename(filename), + my_dist_sign(sign), + my_scale(scale) +{ +} + +BoundingBox +EXOSurface::get_bounding_box() +{ + BoundingBox bbox; + + std::vector xyz; + std::unique_ptr io; + if ( 0 == stk::EnvData::parallel_rank() ) + { + /* open file */ + Ioss::DatabaseIO *db = Ioss::IOFactory::create("exodusII", my_filename.c_str(), Ioss::READ_MODEL, MPI_COMM_SELF); + if ( !db ) { + ThrowRuntimeError("error reading file " << my_filename); + } + io = std::make_unique(db, "EXOSurface IC Region"); + + /* exodus description */ + int num_dim = io->get_property("spatial_dimension").get_int(); + int num_nodes = io->get_property("node_count").get_int(); + + ThrowAssert( 3 == num_dim ); + + /* generate coordinates */ + xyz.resize(num_nodes*num_dim); + + Ioss::NodeBlock *nb = io->get_node_blocks()[0]; + nb->get_field_data("mesh_model_coordinates", xyz); + + for (int n = 0; n & proc_bboxes) +{ + const int nodes_per_elem = 3; + int num_elem_blk = 0; + std::vector xyz; + std::vector nmap; + std::unique_ptr io; + if ( 0 == stk::EnvData::parallel_rank() ) + { + /* open file */ + Ioss::DatabaseIO *db = Ioss::IOFactory::create("exodusII", my_filename.c_str(), Ioss::READ_MODEL, MPI_COMM_SELF); + if ( !db ) { + ThrowRuntimeError("error reading file " << my_filename); + } + io = std::make_unique(db, "EXOSurface IC Region"); + + krinolog << "opened file " << my_filename << " for reading..." << std::endl; + + /* exodus description */ + int num_dim = io->get_property("spatial_dimension").get_int(); + int num_nodes = io->get_property("node_count").get_int(); + int num_elem = io->get_property("element_count").get_int(); + num_elem_blk = io->get_property("element_block_count").get_int(); + + krinolog + << "num dim = " << num_dim + << ", num nodes = " << num_nodes + << ", num elems = " << num_elem + << ", num elem blks = " << num_elem_blk << std::endl; + + ThrowAssert( 3 == num_dim ); + + /* generate coordinates */ + xyz.resize(num_nodes*num_dim); + nmap.resize(num_nodes); + + Ioss::NodeBlock *nb = io->get_node_blocks()[0]; + nb->get_field_data("mesh_model_coordinates", xyz); + nb->get_field_data("ids", nmap); + } + + const int local_num_elem_blk = num_elem_blk; + stk::all_reduce_max( stk::EnvData::parallel_comm(), &local_num_elem_blk, &num_elem_blk, 1 ); + + + for (int blk = 0; blk < num_elem_blk; blk++ ) + { + int num_elem_in_blk = 0; + std::vector conn; + + if ( 0 == stk::EnvData::parallel_rank() ) + { + Ioss::ElementBlockContainer ebs = io->get_element_blocks(); + Ioss::ElementBlock *eb = ebs[blk]; + num_elem_in_blk = eb->get_property("entity_count").get_int(); + + std::string eb_name = eb->name(); + krinolog + << "Reading elem blk #" << blk+1 + << " with name " << eb_name << "..." << std::endl; + + int nodes_per_elem_in_blk = eb->get_property("topology_node_count").get_int(); + ThrowAssert( nodes_per_elem_in_blk == nodes_per_elem ); + + conn.resize(num_elem_in_blk*nodes_per_elem_in_blk); + + krinolog + << " num elem in blk = " << num_elem_in_blk + << ", nodes per elem in blk = " << nodes_per_elem_in_blk << std::endl; + + eb->get_field_data("connectivity", conn); + } + + int batch_size = 0; + int num_batches = 0; + Parallel_Facet_File_Reader::get_batch_size(num_elem_in_blk, batch_size, num_batches); + + for (int batch = 0; batch < num_batches; ++batch) + { + const int current_batch_size = std::min(batch_size, num_elem_in_blk-batch*batch_size); + if ( 0 == stk::EnvData::parallel_rank() ) + { + Vector3d pt[3]; + for ( int e = 0; e < current_batch_size; e++ ) + { + for ( int j = 0; j < nodes_per_elem; j++ ) + { + const int nodeid = conn[e*nodes_per_elem + j]; + const int n = std::distance(nmap.begin(), std::find(nmap.begin(), nmap.end(), nodeid)); + double nodeX = xyz[3*n+0]; + double nodeY = xyz[3*n+1]; + double nodeZ = xyz[3*n+2]; + pt[j] = Vector3d( nodeX*my_scale[0], nodeY*my_scale[1], nodeZ*my_scale[2] ); + } + + unsigned p0 = 0; + unsigned p1 = 1; + unsigned p2 = 2; + if (my_dist_sign == -1) + { + // permute to flip normal direction + p1 = 2; + p2 = 1; + } + + std::unique_ptr facet = std::make_unique( pt[p0], pt[p1], pt[p2] ); + add( std::move(facet) ); + } + } + parallel_distribute_facets(current_batch_size, proc_bboxes); + } + } +} + +MeshSurface::MeshSurface(const stk::mesh::MetaData & meta, + const stk::mesh::Field& coord_ref, + const stk::mesh::Selector & surface_selector, + const int sign) + : Faceted_Surface("Mesh surface"), + my_sign(sign), + my_mesh_meta(meta), + my_coord_ref(coord_ref), + my_surface_selector(surface_selector) +{ +} + +void +MeshSurface::build_local_facets(const BoundingBox & proc_bbox) +{ + /* %TRACE[ON]% */ Trace trace__("krino::MeshSurface::build_local_facets()"); /* %TRACE% */ + const stk::mesh::BulkData & mesh = my_mesh_meta.mesh_bulk_data(); + stk::mesh::Selector active_locally_owned_selector = + (NULL != my_mesh_meta.get_part("ACTIVE_CONTEXT_BIT")) ? + (*my_mesh_meta.get_part("ACTIVE_CONTEXT_BIT") & my_mesh_meta.locally_owned_part()) : + stk::mesh::Selector(my_mesh_meta.locally_owned_part()); + + stk::mesh::Selector active_locally_owned_part_selector = active_locally_owned_selector & my_surface_selector; + + stk::mesh::BucketVector const& buckets = mesh.get_buckets( my_mesh_meta.side_rank(), active_locally_owned_part_selector); + + clear(); + + stk::mesh::BucketVector::const_iterator ib = buckets.begin(); + stk::mesh::BucketVector::const_iterator ib_end = buckets.end(); + + for ( ; ib != ib_end ; ++ib ) + { + const stk::mesh::Bucket & b = **ib; + const unsigned length = b.size(); + + for ( unsigned iSide = 0; iSide < length; ++iSide ) + { + stk::mesh::Entity side = b[iSide]; + + stk::topology side_topology = mesh.bucket(side).topology(); + if (stk::topology::TRI_3 == side_topology) + { + add_facet3d(mesh,side,0,1,2); + } + else if (stk::topology::QUAD_4 == side_topology) + { + add_quad3d(mesh,side,0,1,2,3); + } + else if (stk::topology::TRI_6 == side_topology) + { + add_facet3d(mesh,side,0,3,5); + add_facet3d(mesh,side,1,4,3); + add_facet3d(mesh,side,2,5,4); + add_facet3d(mesh,side,3,4,5); + } + else if (stk::topology::LINE_2 == side_topology) + { + add_facet2d(mesh,side,0,1); + } + else if (stk::topology::LINE_3 == side_topology) + { + add_facet2d(mesh,side,0,2); + add_facet2d(mesh,side,2,1); + } + else + { + ThrowRuntimeError("Elements with side topology " << side_topology.name() << " not supported for mesh surface initialization."); + } + } + } +} + +void +MeshSurface::add_facet2d(const stk::mesh::BulkData& mesh, stk::mesh::Entity side, unsigned p0, unsigned p1) +{ + ThrowAssert(2 == my_mesh_meta.spatial_dimension()); + Vector3d pt[2]; + + if (my_sign == -1) + { + // permute to flip normal direction + const unsigned tmp = p0; + p0 = p1; + p1 = tmp; + } + + const stk::mesh::Entity* side_nodes = mesh.begin_nodes(side); + + const double * pt0 = stk::mesh::field_data(my_coord_ref, side_nodes[p0]); + const double * pt1 = stk::mesh::field_data(my_coord_ref, side_nodes[p1]); + + pt[0][0] = pt0[0]; + pt[0][1] = pt0[1]; + pt[0][2] = 0.0; + pt[1][0] = pt1[0]; + pt[1][1] = pt1[1]; + pt[1][2] = 0.0; + + add_facet2d(pt[0], pt[1]); +} + +void +MeshSurface::add_facet3d(const stk::mesh::BulkData& mesh, stk::mesh::Entity side, unsigned p0, unsigned p1, unsigned p2) +{ + ThrowAssert(3 == my_mesh_meta.spatial_dimension()); + Vector3d pt[3]; + + if (my_sign == -1) + { + // permute to flip normal direction + const unsigned tmp = p1; + p1 = p2; + p2 = tmp; + } + + const stk::mesh::Entity* side_nodes = mesh.begin_nodes(side); + + pt[0] = Vector3d(stk::mesh::field_data(my_coord_ref, side_nodes[p0])); + pt[1] = Vector3d(stk::mesh::field_data(my_coord_ref, side_nodes[p1])); + pt[2] = Vector3d(stk::mesh::field_data(my_coord_ref, side_nodes[p2])); + + add_facet3d(pt[0], pt[1], pt[2]); +} + +void +MeshSurface::add_quad3d(const stk::mesh::BulkData& mesh, stk::mesh::Entity side, unsigned p0, unsigned p1, unsigned p2, unsigned p3) +{ + ThrowAssert(3 == my_mesh_meta.spatial_dimension()); + Vector3d pt[5]; + + if (my_sign == -1) + { + // permute to flip normal direction + const unsigned tmp = p1; + p1 = p3; + p3 = tmp; + } + + const stk::mesh::Entity* side_nodes = mesh.begin_nodes(side); + + pt[0] = Vector3d(stk::mesh::field_data(my_coord_ref, side_nodes[p0])); + pt[1] = Vector3d(stk::mesh::field_data(my_coord_ref, side_nodes[p1])); + pt[2] = Vector3d(stk::mesh::field_data(my_coord_ref, side_nodes[p2])); + pt[3] = Vector3d(stk::mesh::field_data(my_coord_ref, side_nodes[p3])); + + // scale is the RMS of the diagonal lengths, sqr_scale is scale*scale, or the average of the lengths squared + const double sqr_scale = 0.5*((pt[0]-pt[2]).length_squared()+(pt[1]-pt[3]).length_squared()); + // tol is 1e-4, sqr_tol is 1e-8 + const double sqr_tol = 1.e-8; + // Test is that the distance between the midpoints of the 2 diagonals is less than tol*scale + const bool is_planar = (0.5*(pt[0]+pt[2])-0.5*(pt[1]+pt[3])).length_squared() < sqr_tol*sqr_scale; + //const bool is_planar = false; // conservative approach, forces 4 facets per quad + + if (is_planar) + { + add_facet3d(pt[0], pt[1], pt[2]); + add_facet3d(pt[0], pt[2], pt[3]); + } + else + { + pt[4] = 0.25 * (pt[0]+pt[1]+pt[2]+pt[3]); + add_facet3d(pt[0], pt[1], pt[4]); + add_facet3d(pt[1], pt[2], pt[4]); + add_facet3d(pt[2], pt[3], pt[4]); + add_facet3d(pt[3], pt[0], pt[4]); + } +} + +void +MeshSurface::add_facet2d(Vector3d & p0, Vector3d & p1) +{ + ThrowAssert(2 == my_mesh_meta.spatial_dimension()); + + std::unique_ptr facet = std::make_unique( p0, p1 ); + add( std::move(facet) ); +} + +void +MeshSurface::add_facet3d(Vector3d & p0, Vector3d & p1, Vector3d & p2) +{ + ThrowAssert(3 == my_mesh_meta.spatial_dimension()); + + std::unique_ptr facet = std::make_unique( p0, p1, p2 ); + add( std::move(facet) ); +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_MeshSurface.hpp b/packages/krino/krino/krino_lib/Akri_MeshSurface.hpp new file mode 100644 index 000000000000..7f2ec3c199c7 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MeshSurface.hpp @@ -0,0 +1,177 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_MeshSurface_h +#define Akri_MeshSurface_h + +#include + +#include +#include + +#include +#include + +namespace stk { namespace mesh { class BulkData; } } +namespace stk { namespace mesh { class Entity; } } +namespace stk { namespace mesh { class MetaData; } } +namespace stk { namespace mesh { class Selector; } } + +namespace krino { + +class MeshSurface : public Faceted_Surface { +public: + MeshSurface(const stk::mesh::MetaData & meta, + const stk::mesh::Field& coord_ref, + const stk::mesh::Selector & surface_selector, + const int sign); + + virtual ~MeshSurface() {} + virtual void build_local_facets(const BoundingBox & proc_bbox) override; + +protected: + void add_facet2d(const stk::mesh::BulkData& mesh, stk::mesh::Entity side, unsigned p0, unsigned p1); + void add_facet3d(const stk::mesh::BulkData& mesh, stk::mesh::Entity side, unsigned p0, unsigned p1, unsigned p2); + void add_quad3d(const stk::mesh::BulkData& mesh, stk::mesh::Entity side, unsigned p0, unsigned p1, unsigned p2, unsigned p3); + void add_facet2d(Vector3d & p0, Vector3d & p1); + void add_facet3d(Vector3d & p0, Vector3d & p1, Vector3d & p2); + +private: + int my_sign; + const stk::mesh::MetaData & my_mesh_meta; + const stk::mesh::Field& my_coord_ref; + const stk::mesh::Selector my_surface_selector; +}; + +class Parallel_Facet_File_Reader { +public: + Parallel_Facet_File_Reader(const std::string & in_filename) : my_filename(in_filename) {} + + static void get_batch_size(const int local_num_facets, int & batch_size, int & num_batches); + + std::ifstream & input() { return my_input; } + const std::string & filename() const { return my_filename; } + + void open_file(); + void close_file(); + void read(const std::string & read_description, const std::function & read_function); + +private: + std::string my_filename; + std::ifstream my_input; +}; + +class Faceted_Surface_From_File : public Faceted_Surface { +public: + Faceted_Surface_From_File(const std::string & surface_name, const stk::diag::Timer &parent_timer); + virtual ~Faceted_Surface_From_File() {} + virtual void build_local_facets(const BoundingBox & proc_bbox) override; + virtual BoundingBox get_bounding_box() override = 0; + +protected: + virtual void read_file(const std::vector & proc_bboxes) = 0; +private: + stk::diag::Timer my_timer; + bool my_built_local_facets; +}; + +class STLSurface : public Faceted_Surface_From_File { +public: + STLSurface(const std::string & surface_name, + const stk::diag::Timer &parent_timer, + const std::string & filename, + const int sign, + const Vector3d & scale); + + virtual ~STLSurface() {} + virtual BoundingBox get_bounding_box() override; + +private: + virtual void read_file(const std::vector & proc_bboxes) override; + void read_header(); + unsigned read_num_binary_facets(); + unsigned read_ascii_facets(const unsigned max_batch_size); + unsigned read_binary_facets(const unsigned maxBatchSize, unsigned & numRemainingInFile); + BoundingBox get_ascii_facet_bounding_box(); + BoundingBox get_binary_facet_bounding_box(const unsigned numFacets); + +private: + bool my_is_ascii; + Parallel_Facet_File_Reader my_reader; + int my_dist_sign; + Vector3d my_scale; +}; + +class FACSurface : public Faceted_Surface_From_File { +public: + FACSurface(const std::string & surface_name, + const stk::diag::Timer &parent_timer, + const std::string & filename, + const int sign, + const Vector3d & scale); + + virtual ~FACSurface() {} + virtual BoundingBox get_bounding_box() override; + +private: + virtual void read_file(const std::vector & proc_bboxes) override; + void read_points(std::vector & points); + void read_facets(const int batch_size, const int num_facets, const std::vector & points); + +private: + Parallel_Facet_File_Reader my_reader; + int my_dist_sign; + Vector3d my_scale; +}; + +class PLYSurface : public Faceted_Surface_From_File { +public: + PLYSurface(const std::string & surface_name, + const stk::diag::Timer &parent_timer, + const std::string & filename, + const int sign, + const Vector3d & scale); + + virtual ~PLYSurface() {} + virtual BoundingBox get_bounding_box() override; + +private: + Parallel_Facet_File_Reader my_reader; + virtual void read_file(const std::vector & proc_bboxes) override; + void read_header(int & num_points, int & num_facets); + void read_points(const int num_points, std::vector & points); + void read_facets(const int batch_size, const std::vector & points); + +private: + int my_dist_sign; + Vector3d my_scale; +}; + +class EXOSurface : public Faceted_Surface_From_File { +public: + EXOSurface(const std::string & surface_name, + const stk::diag::Timer &parent_timer, + const std::string & filename, + const int sign, + const Vector3d & scale); + + virtual ~EXOSurface() {} + virtual BoundingBox get_bounding_box() override; + +private: + virtual void read_file(const std::vector & proc_bboxes) override; + +private: + std::string my_filename; + int my_dist_sign; + Vector3d my_scale; +}; + +} // namespace krino + +#endif // Akri_MeshSurface_h diff --git a/packages/krino/krino/krino_lib/Akri_MortonIndex.hpp b/packages/krino/krino/krino_lib/Akri_MortonIndex.hpp new file mode 100644 index 000000000000..4d811bbf1218 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_MortonIndex.hpp @@ -0,0 +1,58 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_MortonIndex_h +#define Akri_MortonIndex_h + +#include +#include + +namespace krino { + +constexpr int MAX_VALID_MORTON3D_INDEX = 0x1fffff; + +uint64_t morton3d_encode_index(const int i) +{ + ThrowAssertMsg(i >= 0, "Index must be >= 0 to encode: " << i); + ThrowAssertMsg(i <= MAX_VALID_MORTON3D_INDEX, "Index must be <= " << MAX_VALID_MORTON3D_INDEX << " to encode: " << i); + uint64_t m = i & 0x1fffff; + m = (m | m << 32) & 0x001f00000000ffff; + m = (m | m << 16) & 0x001f0000ff0000ff; + m = (m | m << 8) & 0x100f00f00f00f00f; + m = (m | m << 4) & 0x10c30c30c30c30c3; + m = (m | m << 2) & 0x1249249249249249; + return m; +} + +int morton3d_decode_index(uint64_t m) +{ + m &= 0x1249249249249249; + m = (m ^ (m >> 2)) & 0x10c30c30c30c30c3; + m = (m ^ (m >> 4)) & 0x100f00f00f00f00f; + m = (m ^ (m >> 8)) & 0x001f0000ff0000ff; + m = (m ^ (m >> 16)) & 0x001f00000000ffff; + m = (m ^ (m >> 32)) & 0x1fffff; + return static_cast(m); +} + +uint64_t morton3d_encode_indices(const std::array & indices) +{ + return morton3d_encode_index(indices[2]) * 4 + morton3d_encode_index(indices[1]) * 2 + morton3d_encode_index(indices[0]); +} + +std::array morton3d_decode_indices(const uint64_t m) +{ + const int ix = morton3d_decode_index(m); + const int iy = morton3d_decode_index(m >> 1); + const int iz = morton3d_decode_index(m >> 2); + return {{ix,iy,iz}}; +} + +} // namespace krino + +#endif // Akri_MortonIndex_h diff --git a/packages/krino/krino/krino_lib/Akri_NodeToCapturedDomains.cpp b/packages/krino/krino/krino_lib/Akri_NodeToCapturedDomains.cpp new file mode 100644 index 000000000000..6f1f5d091d5c --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_NodeToCapturedDomains.cpp @@ -0,0 +1,121 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include + +namespace krino { + +static void pack_node_captured_domains_for_sharing_or_ghosting_procs(const stk::mesh::BulkData & mesh, + const stk::mesh::Entity node, + const std::vector & nodeCapturedDomains, + std::vector procsThatNeedToKnowAboutNode, + stk::CommSparse &commSparse) +{ + fill_procs_owning_or_sharing_or_ghosting_node(mesh, node, procsThatNeedToKnowAboutNode); + for (int procId : procsThatNeedToKnowAboutNode) + { + if (procId != mesh.parallel_rank()) + { + commSparse.send_buffer(procId).pack(mesh.identifier(node)); + commSparse.send_buffer(procId).pack(nodeCapturedDomains.size()); + for (int domain : nodeCapturedDomains) + commSparse.send_buffer(procId).pack(domain); + } + } +} + +static +void pack_all_node_captured_domains_for_sharing_or_ghosting_procs(const stk::mesh::BulkData & mesh, + const NodeToCapturedDomainsMap & nodesToCapturedDomains, + stk::CommSparse &commSparse) +{ + std::vector scratchVec; + stk::pack_and_communicate(commSparse,[&]() + { + for (auto && entry : nodesToCapturedDomains) + { + stk::mesh::Entity node = entry.first; + if (mesh.parallel_owner_rank(node) == mesh.parallel_rank()) + { + const auto & nodeCapturedDomains = entry.second; + pack_node_captured_domains_for_sharing_or_ghosting_procs(mesh, node, nodeCapturedDomains, scratchVec, commSparse); + } + } + }); +} + +static +void pack_node_captured_domains_of_given_nodes_for_sharing_or_ghosting_procs(const stk::mesh::BulkData & mesh, + const std::vector & nodes, + const NodeToCapturedDomainsMap & nodesToCapturedDomains, + stk::CommSparse &commSparse) +{ + std::vector scratchVec; + stk::pack_and_communicate(commSparse,[&]() + { + for (auto node : nodes) + { + if (mesh.parallel_owner_rank(node) == mesh.parallel_rank()) + { + const auto nodeCapturedDomains = nodesToCapturedDomains.at(node); + pack_node_captured_domains_for_sharing_or_ghosting_procs(mesh, node, nodeCapturedDomains, scratchVec, commSparse); + } + } + }); +} + +static +void unpack_snap_node_domains(const stk::mesh::BulkData & mesh, + NodeToCapturedDomainsMap & nodesToSnappedDomains, + stk::CommSparse &commSparse) +{ + std::vector nodeSnappedDomains; + + stk::unpack_communications(commSparse, [&](int procId) + { + stk::CommBuffer & buffer = commSparse.recv_buffer(procId); + + while ( buffer.remaining() ) + { + stk::mesh::EntityId nodeId; + commSparse.recv_buffer(procId).unpack(nodeId); + size_t numNodes; + commSparse.recv_buffer(procId).unpack(numNodes); + nodeSnappedDomains.resize(numNodes); + for (auto && domain : nodeSnappedDomains) + commSparse.recv_buffer(procId).unpack(domain); + + stk::mesh::Entity snapNode = mesh.get_entity(stk::topology::NODE_RANK, nodeId); + nodesToSnappedDomains[snapNode] = nodeSnappedDomains; + } + }); +} + +void communicate_node_captured_domains_for_given_nodes(const stk::mesh::BulkData & mesh, + const std::vector & nodes, + NodeToCapturedDomainsMap & nodesToCapturedDomains) +{ + stk::CommSparse commSparse(mesh.parallel()); + pack_node_captured_domains_of_given_nodes_for_sharing_or_ghosting_procs(mesh, nodes, nodesToCapturedDomains, commSparse); + unpack_snap_node_domains(mesh, nodesToCapturedDomains, commSparse); +} + +void communicate_node_captured_domains_for_all_nodes(const stk::mesh::BulkData & mesh, + NodeToCapturedDomainsMap & nodesToCapturedDomains) +{ + stk::CommSparse commSparse(mesh.parallel()); + pack_all_node_captured_domains_for_sharing_or_ghosting_procs(mesh, nodesToCapturedDomains, commSparse); + unpack_snap_node_domains(mesh, nodesToCapturedDomains, commSparse); +} + +} + + diff --git a/packages/krino/krino/krino_lib/Akri_NodeToCapturedDomains.hpp b/packages/krino/krino/krino_lib/Akri_NodeToCapturedDomains.hpp new file mode 100644 index 000000000000..7245a6e9398c --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_NodeToCapturedDomains.hpp @@ -0,0 +1,31 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_NODETOCAPTUREDDOMAINS_H_ +#define KRINO_INCLUDE_AKRI_NODETOCAPTUREDDOMAINS_H_ +#include +#include +#include +#include + +namespace krino { + +typedef std::map> NodeToCapturedDomainsMap; + +void communicate_node_captured_domains_for_given_nodes(const stk::mesh::BulkData & mesh, + const std::vector & nodes, + NodeToCapturedDomainsMap & nodesToCapturedDomains); + +void communicate_node_captured_domains_for_all_nodes(const stk::mesh::BulkData & mesh, + NodeToCapturedDomainsMap & nodesToCapturedDomains); + +typedef std::map ElementToDomainMap; + +} + +#endif /* KRINO_INCLUDE_AKRI_NODETOCAPTUREDDOMAINS_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_OrderedIdPair.hpp b/packages/krino/krino/krino_lib/Akri_OrderedIdPair.hpp new file mode 100644 index 000000000000..9f89f71a210a --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_OrderedIdPair.hpp @@ -0,0 +1,50 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef AKRI_ORDEREDIDPAIR_H_ +#define AKRI_ORDEREDIDPAIR_H_ + +#include +#include + +namespace krino { + +class OrderedIdPair { +public: + OrderedIdPair(stk::mesh::EntityId id0, stk::mesh::EntityId id1) + : my_ids(id0 < id1 ? id0 : id1, id0 < id1 ? id1 : id0) {} + + stk::mesh::EntityId first() const { return my_ids.first; } + stk::mesh::EntityId second() const { return my_ids.second; } + +private: + std::pair my_ids; +}; + +inline bool operator<(const OrderedIdPair lhs, const OrderedIdPair rhs) +{ + if(lhs.first() < rhs.first()) return true; + if(rhs.first() < lhs.first()) return false; + if(lhs.second() < rhs.second()) return true; + return false; +} + +inline bool operator==(const OrderedIdPair lhs, const OrderedIdPair rhs) +{ + return (lhs.first() == rhs.first()) && (lhs.second() == rhs.second()); +} + +inline bool operator!=(const OrderedIdPair lhs, const OrderedIdPair rhs) +{ + return !(lhs == rhs); +} + +} + + +#endif /* AKRI_ORDEREDIDPAIR_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_ParallelCommHelpers.hpp b/packages/krino/krino/krino_lib/Akri_ParallelCommHelpers.hpp new file mode 100644 index 000000000000..273e4ab441b7 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_ParallelCommHelpers.hpp @@ -0,0 +1,77 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_PARALLELCOMMHELPERS_H_ +#define KRINO_INCLUDE_AKRI_PARALLELCOMMHELPERS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino { + +// PACK_OP shoudl have an operator()(stk::CommBuffer & buffer, const stk::mesh::EntityKey & entity_key) +// UNPACK_OP should have an operator()(CommBuffer & buffer) +template +void communicate_with_shared_procs(const stk::mesh::BulkData& mesh, + const std::vector & local_entities, + PACK_OP pack_op, + UNPACK_OP unpack_op) +{ + // The input vector, local_entities, must only contain entities that are locally_owned or globally_shared (or both). + + stk::CommSparse comm_spec(mesh.parallel()); + std::vector sharing_procs; + + for (int phase=0;phase<2;++phase) + { + for (auto&& local_entity : local_entities) + { + ThrowAssert(mesh.bucket(local_entity).owned() || mesh.bucket(local_entity).shared()); + mesh.comm_shared_procs(local_entity, sharing_procs); + for (auto&& sharing_proc : sharing_procs) + { + if (sharing_proc != mesh.parallel_rank()) + { + pack_op(comm_spec.send_buffer(sharing_proc), mesh.entity_key(local_entity)); + } + } + } + + if ( phase == 0 ) + { + comm_spec.allocate_buffers(); + } + else + { + comm_spec.communicate(); + } + } + + for(int i = 0; i < mesh.parallel_size(); ++i) + { + if(i != mesh.parallel_rank()) + { + auto & recv_buffer = comm_spec.recv_buffer(i); + while(recv_buffer.remaining()) + { + unpack_op(recv_buffer); + } + } + } +} + +} + +#endif /* KRINO_INCLUDE_AKRI_PARALLELCOMMHELPERS_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_ParallelErrorMessage.cpp b/packages/krino/krino/krino_lib/Akri_ParallelErrorMessage.cpp new file mode 100644 index 000000000000..4881453ec8a3 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_ParallelErrorMessage.cpp @@ -0,0 +1,30 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include + +namespace krino +{ + +ParallelErrorMessage::ParallelErrorMessage(const stk::Parallel & c) : comm(c) {} + +std::pair ParallelErrorMessage::gather_message() const +{ + const std::string & local_str = local_message.str(); + + std::ostringstream result; + stk::all_write_string(comm.parallel(), result, local_str); + + int err = result.str().size(); + int global_err; + stk::all_reduce_sum(comm.parallel(), &err, &global_err, 1); + + return make_pair(static_cast(global_err), result.str()); +} +} diff --git a/packages/krino/krino/krino_lib/Akri_ParallelErrorMessage.hpp b/packages/krino/krino/krino_lib/Akri_ParallelErrorMessage.hpp new file mode 100644 index 000000000000..b2b76e3a4ee0 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_ParallelErrorMessage.hpp @@ -0,0 +1,43 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_PARALLELERRORMESSAGE_H_ +#define KRINO_INCLUDE_AKRI_PARALLELERRORMESSAGE_H_ + +#include +#include +#include + +namespace krino +{ + +class ParallelErrorMessage +{ +public: + ParallelErrorMessage(const stk::Parallel & c); + + template ParallelErrorMessage & operator<<(const T & t) + { + local_message << t; + return *this; + } + + // This will gather the messages from all ranks to rank 0 where it can be output. + // and additionally return a parallel-consistent bool for whether or not any errors + // were logged. + std::pair gather_message() const; + + bool have_local_error() const { return !local_message.str().empty(); } + +private: + const stk::Parallel comm; + std::ostringstream local_message; +}; +} + +#endif /* KRINO_INCLUDE_AKRI_PARALLELERRORMESSAGE_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_ParentsToChildMapper.cpp b/packages/krino/krino/krino_lib/Akri_ParentsToChildMapper.cpp new file mode 100644 index 000000000000..6f217818262f --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_ParentsToChildMapper.cpp @@ -0,0 +1,188 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino { + +static bool determine_if_mesh_has_higher_order_midside_nodes_with_level_set_locally(const stk::mesh::BulkData & mesh, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport) +{ + // Assume we can check master element on one bucket and get the right answer + + for ( auto && bucket_ptr : mesh.get_buckets(stk::topology::ELEMENT_RANK, phaseSupport.get_all_decomposed_blocks_selector()) ) + { + if (bucket_ptr->topology() != bucket_ptr->topology().base()) + { + for (int ls_index = 0; ls_index < cdfemSupport.num_ls_fields(); ++ls_index) + { + stk::topology fieldTopology = MasterElementDeterminer::get_field_topology(*bucket_ptr, cdfemSupport.ls_field(ls_index).isovar); + if (fieldTopology != fieldTopology.base()) + return true; + } + } + } + return false; +} + +void fill_edge_nodes( + const stk::mesh::BulkData & mesh, + const stk::mesh::Entity node0, + const stk::mesh::Entity node1, + const ParentsToChildMapper & parentToChildMapper, + std::vector & edgeNodes) +{ + stk::mesh::Entity child = parentToChildMapper.get_child(mesh, node0, node1); + + if (mesh.is_valid(child)) + { + ThrowRequire(child != node0); + ThrowRequire(child != node1); + fill_edge_nodes(mesh, node0,child, parentToChildMapper, edgeNodes); + fill_edge_nodes(mesh, child,node1, parentToChildMapper, edgeNodes); + } + else + { + if (edgeNodes.empty()) + { + edgeNodes.push_back(node0); + } + edgeNodes.push_back(node1); + } +} + +static void fill_edge_nodes_and_positions( + const double pos0, stk::mesh::Entity node0, + const double pos1, stk::mesh::Entity node1, + std::vector & edgeNodes, + std::vector & edgeNodePositions) +{ + if (edgeNodePositions.empty()) + { + edgeNodePositions.push_back(pos0); + edgeNodes.push_back(node0); + } + edgeNodePositions.push_back(pos1); + edgeNodes.push_back(node1); +} + +static void fill_edge_nodes_and_positions( + const stk::mesh::BulkData & mesh, + const double pos0, stk::mesh::Entity node0, + const double pos1, stk::mesh::Entity node1, + const ParentsToChildMapper & parent_child_mapper, + std::vector & edgeNodes, + std::vector & edgeNodePositions) +{ + stk::mesh::Entity child = parent_child_mapper.get_child(mesh, node0, node1); + + if (mesh.is_valid(child)) + { + ThrowRequire(child != node0); + ThrowRequire(child != node1); + const double child_rel_pos = compute_child_position(mesh, child, node0, node1); + const double child_abs_pos = pos0 * (1.-child_rel_pos) + pos1 * child_rel_pos; + fill_edge_nodes_and_positions(mesh,pos0,node0,child_abs_pos,child, parent_child_mapper, edgeNodes, edgeNodePositions); + fill_edge_nodes_and_positions(mesh,child_abs_pos,child,pos1,node1, parent_child_mapper, edgeNodes, edgeNodePositions); + } + else + { + fill_edge_nodes_and_positions(pos0, node0, pos1, node1, edgeNodes, edgeNodePositions); + } +} + +void fill_edge_nodes_and_positions(const stk::mesh::BulkData & mesh, + stk::mesh::Entity node0, stk::mesh::Entity node1, + const ParentsToChildMapper & parent_child_mapper, + std::vector & edgeNodes, + std::vector & edgeNodePositions) +{ + edgeNodes.clear(); + edgeNodePositions.clear(); + fill_edge_nodes_and_positions(mesh, 0.0, node0, 1.0, node1, parent_child_mapper, edgeNodes, edgeNodePositions); +} + +void fill_linear_edge_nodes_and_positions(stk::mesh::Entity node0, stk::mesh::Entity node1, + std::vector & edgeNodes, + std::vector & edgeNodePositions) +{ + edgeNodes.clear(); + edgeNodePositions.clear(); + fill_edge_nodes_and_positions(0.0, node0, 1.0, node1, edgeNodes, edgeNodePositions); +} + + +void ParentsToChildMapper::build_map(const stk::mesh::BulkData & mesh, + const stk::mesh::Part & activePart, + const CDFEM_Support & cdfemSupport, + const Phase_Support & phaseSupport) +{ + const bool addHigherOrderMidsideNodes = stk::is_true_on_any_proc(mesh.parallel(), determine_if_mesh_has_higher_order_midside_nodes_with_level_set_locally(mesh, cdfemSupport, phaseSupport)); + + stk::mesh::Selector elementSelectorForParentChildMapper = activePart & mesh.mesh_meta_data().locally_owned_part(); + + build_map(mesh, cdfemSupport.get_parent_node_ids_field(), elementSelectorForParentChildMapper, addHigherOrderMidsideNodes); +} + +void ParentsToChildMapper::build_map(const stk::mesh::BulkData & mesh, const FieldRef & parent_ids_field, const stk::mesh::Selector & elementSelector, bool add_higher_order_midside_nodes) +{ + my_parents_to_child_map.clear(); + + if (parent_ids_field.valid()) + { + const auto & field_selector = stk::mesh::selectField(parent_ids_field); + + ThrowRequire(!mesh.in_modifiable_state()); + + const auto & buckets = mesh.get_buckets(stk::topology::NODE_RANK, field_selector); + for(auto && b_ptr : buckets) + { + for(unsigned i=0; i < b_ptr->size(); ++i) + { + const stk::mesh::Entity child_node = (*b_ptr)[i]; + const auto parent_ids = get_edge_node_parent_ids(mesh, parent_ids_field, child_node); + my_parents_to_child_map[{parent_ids[0], parent_ids[1]}] = mesh.identifier(child_node); + } + } + } + + if (add_higher_order_midside_nodes) + { + // Add higher order edge "children" + for(auto && b_ptr : mesh.get_buckets(stk::topology::ELEMENT_RANK, elementSelector)) + { + const stk::topology elem_topology = b_ptr->topology(); + if (elem_topology == stk::topology::TRIANGLE_6_2D || elem_topology == stk::topology::TETRAHEDRON_10) + { + for (auto elem : *b_ptr) + { + const stk::mesh::Entity * elem_nodes = mesh.begin_nodes(elem); + for (unsigned edge_i=0; edge_i + +#include +#include +#include + +namespace krino { class FieldRef; } + +namespace krino { + +class CDFEM_Support; +class Phase_Support; + +class ParentsToChildMapper +{ +public: + ParentsToChildMapper() = default; + + void build_map(const stk::mesh::BulkData & mesh, const stk::mesh::Part & activePart, const CDFEM_Support & cdfemSupport, const Phase_Support & phaseSupport); + void build_map(const stk::mesh::BulkData & mesh, const FieldRef & parent_ids_field, const stk::mesh::Selector & elementSelector, bool add_higher_order_midside_nodes); + + bool get_child_id(const stk::mesh::EntityId parent0, + const stk::mesh::EntityId parent1, + stk::mesh::EntityId & child) const + { + const auto it = my_parents_to_child_map.find({parent0, parent1}); + if(it == my_parents_to_child_map.end()) return false; + child = it->second; + return true; + } + + stk::mesh::Entity get_child(const stk::mesh::BulkData & mesh, const stk::mesh::Entity parent0, + const stk::mesh::Entity parent1) const + { + const auto it = + my_parents_to_child_map.find({mesh.identifier(parent0), mesh.identifier(parent1)}); + if(it == my_parents_to_child_map.end()) return stk::mesh::Entity(); + return mesh.get_entity(stk::topology::NODE_RANK, it->second); + } + +private: + typedef OrderedIdPair ParentsToChildKey; + typedef std::map ParentsToChildMap; + ParentsToChildMap my_parents_to_child_map; +}; + +void fill_edge_nodes( + const stk::mesh::BulkData & mesh, + const stk::mesh::Entity node0, + const stk::mesh::Entity node1, + const ParentsToChildMapper & parentToChildMapper, + std::vector & edgeNodes); + +void fill_edge_nodes_and_positions(const stk::mesh::BulkData & mesh, + stk::mesh::Entity node0, stk::mesh::Entity node1, + const ParentsToChildMapper & parent_child_mapper, + std::vector & edgeNodes, + std::vector & edgeNodePositions); + +void fill_linear_edge_nodes_and_positions(stk::mesh::Entity node0, + stk::mesh::Entity node1, + std::vector & edgeNodes, + std::vector & edgeNodePositions); + +} // namespace krino + +#endif /* AKRI_PARENTSTOCHILDMAPPER_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_PhaseTag.cpp b/packages/krino/krino/krino_lib/Akri_PhaseTag.cpp new file mode 100644 index 000000000000..5f029c295be4 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_PhaseTag.cpp @@ -0,0 +1,112 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +namespace krino{ + +std::map LS_SideTag::the_composite_ls_map; + +void LS_SideTag::declare_composite(const LevelSet_Identifier constituent, const LevelSet_Identifier composite) +{ + LS_SideTag constituent_tag(constituent,0); + LS_SideTag composite_tag(composite,0); + + // verbose version of operator[] to avoid needing default constructor for LevelSet_Interface + auto existing_entry = the_composite_ls_map.find(constituent_tag.my_ls_identifier); + if (existing_entry != the_composite_ls_map.end()) + { + existing_entry->second = composite_tag.my_ls_identifier; + } + else + { + the_composite_ls_map.insert(std::make_pair(constituent_tag.my_ls_identifier, composite_tag.my_ls_identifier)); + } + +} + +std::ostream& +operator<<(std::ostream & os, const LS_SideTag & ls_side) +{ + /* %TRACE[NONE]% */ /* %TRACE% */ + os << "(" << ls_side.my_ls_identifier << "," << ls_side.my_ls_sign << ")"; + return os; +} + +std::ostream& +operator<<(std::ostream & os, const PhaseTag & phase) +{ + /* %TRACE[NONE]% */ /* %TRACE% */ + os << " with ls sides: "; + const std::set & ls_sides = phase.ls_sides(); + for (std::set::const_iterator it = ls_sides.begin(); it != ls_sides.end(); ++it) + { + os << *it << " "; + } + return os; +} + +bool PhaseTag::contain(const PhaseTag & phase) const +{ + // This phase is considered to contain "phase" if every phase in "phase" is contained. + // Example: "this": (LS1,-1), (LS2,-1) + // "phase": (LS1,-1) + // Then "this" contains "phase", but "phase" doesn't contain "this" + for (std::set::const_iterator it=phase.my_ls_sides.begin(); it != phase.my_ls_sides.end(); ++it) + { + if (!contain(*it)) return false; + } + return true; +} + +bool +PhaseTag::is_nonconformal() const +{ + for (std::set::const_iterator it=my_ls_sides.begin(); it != my_ls_sides.end(); ++it) + { + const LS_SideTag & ls_side = *it; + + if (ls_side.is_interface()) + { + return true; + } + } + return false; +} + +bool +PhaseTag::operator== (const PhaseTag & rhs) const +{ + if (my_ls_sides.size() != rhs.my_ls_sides.size()) return false; + for (std::set::const_iterator it=my_ls_sides.begin(); it != my_ls_sides.end(); ++it) + { + const LS_SideTag & ls_side = *it; + + if (!rhs.contain(ls_side)) return false; + } + return true; +} + +PhaseTag & +PhaseTag::operator &= ( const PhaseTag & rhs ) +{ + for (std::set::const_iterator it=my_ls_sides.begin(); it != my_ls_sides.end();) + { + if (!rhs.contain(*it)) + { + my_ls_sides.erase(it++); + } + else + { + ++it; + } + } + return *this; +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_PhaseTag.hpp b/packages/krino/krino/krino_lib/Akri_PhaseTag.hpp new file mode 100644 index 000000000000..94fa63b58f18 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_PhaseTag.hpp @@ -0,0 +1,171 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_PhaseTag_h +#define Akri_PhaseTag_h + +#include +#include + +#include +#include +#include + +namespace krino { + +class LS_SideTag { +public: + LS_SideTag(const LevelSet_Identifier ls_identifier, const int ls_sign) : my_ls_identifier(ls_identifier), my_ls_sign(ls_sign) {} + LS_SideTag(const LS_SideTag & orig, const int ls_sign) : my_ls_identifier(orig.my_ls_identifier), my_ls_sign(ls_sign) {} + ~LS_SideTag() {} +public: + static void declare_composite(const LevelSet_Identifier constituent, const LevelSet_Identifier composite); + LS_SideTag opposite_side() const {return LS_SideTag(my_ls_identifier,-1*my_ls_sign);} + LS_SideTag composite() const + { + auto it = the_composite_ls_map.find(my_ls_identifier); + if (it != the_composite_ls_map.end()) + { + return LS_SideTag(it->second, my_ls_sign); + } + else + { + return *this; + } + } + bool is_interface() const {return (0 == my_ls_sign);} + int get_ls_sign() const {return my_ls_sign;} + LevelSet_Identifier get_ls_identifier() const {return my_ls_identifier;} + bool operator < ( const LS_SideTag & RHS ) const { return my_ls_identifier < RHS.my_ls_identifier || (my_ls_identifier == RHS.my_ls_identifier && my_ls_sign < RHS.my_ls_sign); } + bool operator == ( const LS_SideTag & RHS ) const { return (my_ls_identifier == RHS.my_ls_identifier && my_ls_sign == RHS.my_ls_sign); } + bool operator != ( const LS_SideTag & RHS ) const { return (my_ls_identifier != RHS.my_ls_identifier || my_ls_sign != RHS.my_ls_sign); } + friend std::ostream& operator<<(std::ostream & os, const LS_SideTag & phase); +protected: + const LevelSet_Identifier my_ls_identifier; + const int my_ls_sign; + static std::map the_composite_ls_map; +}; + +class PhaseTag { +public: + PhaseTag() {} + ~PhaseTag() {} +public: + bool empty() const { return my_ls_sides.empty(); } + void clear() { my_ls_sides.clear(); } + const std::set & ls_sides() const { return my_ls_sides; } + void add(const LevelSet_Identifier ls_identifier, const int ls_sign) { add(LS_SideTag(ls_identifier, ls_sign)); } + void add(const PhaseTag & phase) { for (auto && ls_side : phase.my_ls_sides) add(ls_side); } + void add_opposite_sides(const PhaseTag & phase) { for (auto && ls_side : phase.my_ls_sides) add(ls_side.opposite_side()); } + void add(const LS_SideTag & ls_side) + { + const LS_SideTag ls_side_composite = ls_side.composite(); + my_ls_sides.insert(ls_side_composite); + if (ls_side_composite != ls_side) + { + // Apply priority rule. -1 (inside) beats +1 (outside) + if (my_ls_sides.count(LS_SideTag(ls_side_composite.get_ls_identifier(),-1))) + { + my_ls_sides.erase(LS_SideTag(ls_side_composite.get_ls_identifier(),+1)); + } + } + } + void remove(const LevelSet_Identifier ls_identifier, const int ls_sign){ remove(LS_SideTag(ls_identifier,ls_sign)); } + void remove(const LS_SideTag & ls_side) { my_ls_sides.erase(ls_side.composite()); } + + bool contain(const PhaseTag & phase) const; + bool contain(const LevelSet_Identifier ls_identifier, const int ls_sign) const { return contain(LS_SideTag(ls_identifier,ls_sign)); } + bool contain(const LS_SideTag & ls_side) const { return my_ls_sides.count(ls_side.composite()); } + + bool is_nonconformal() const; + bool operator!= (const PhaseTag & rhs) const { return !operator==(rhs); } + bool operator== (const PhaseTag & rhs) const; + bool operator < ( const PhaseTag & RHS ) const { return (my_ls_sides < RHS.my_ls_sides); } + friend std::ostream& operator<<(std::ostream & os, const PhaseTag & phase); + /** \brief Intersection: this = this INTERSECT ( expression ) */ + PhaseTag & operator &= ( const PhaseTag & rhs); + + void get_data(std::vector & phase_data) const + { + for (std::set::const_iterator side_it = ls_sides().begin(); side_it != ls_sides().end(); ++side_it) + { + phase_data.push_back(side_it->get_ls_identifier().get()); + phase_data.push_back(side_it->get_ls_sign()); + } + } + void set_from_data(const std::vector & phase_data) + { + clear(); + ThrowAssert(phase_data.size() % 2 == 0); + const unsigned num_phases = phase_data.size() / 2; + for (unsigned phase_index=0; phase_index my_ls_sides; +}; + +class NamedPhase +{ +public: + NamedPhase(std::string in_name) : my_name(in_name) {} + NamedPhase(std::string in_name, const PhaseTag & in_tag) : my_name(in_name), my_tag(in_tag) {} + const std::string & name() const { return my_name; } + const PhaseTag & tag() const { return my_tag; } + PhaseTag & tag() { return my_tag; } +protected: + std::string my_name; + PhaseTag my_tag; +}; +typedef std::vector< NamedPhase > PhaseVec; + +class PhasePartTag { +public: + // For non-interface phase parts + PhasePartTag(const unsigned & conformal_part_ordinal, const unsigned & nonconformal_part_ordinal, const unsigned & original_part_ordinal, const PhaseTag & vol_phase) + : my_conformal_part_ordinal(conformal_part_ordinal), + my_nonconformal_part_ordinal(nonconformal_part_ordinal), + my_original_part_ordinal(original_part_ordinal), + my_touching_vol_phase(vol_phase) { ThrowRequire(!vol_phase.empty()); } + // For interface phase parts defined as intersection of touching_vol_phase and opposite_vol_phase + PhasePartTag(const unsigned & conformal_part_ordinal, const unsigned & nonconformal_part_ordinal, const unsigned & original_part_ordinal, const PhaseTag & touching_vol_phase, const PhaseTag & opposite_vol_phase) + : my_conformal_part_ordinal(conformal_part_ordinal), + my_nonconformal_part_ordinal(nonconformal_part_ordinal), + my_original_part_ordinal(original_part_ordinal), + my_touching_vol_phase(touching_vol_phase), + my_opposite_vol_phase(opposite_vol_phase) { ThrowRequire(!touching_vol_phase.empty() && !opposite_vol_phase.empty()); } + ~PhasePartTag() {} +public: + unsigned get_conformal_part_ordinal() const { return my_conformal_part_ordinal; } + unsigned get_nonconformal_part_ordinal() const { return my_nonconformal_part_ordinal; } + unsigned get_original_part_ordinal() const { return my_original_part_ordinal; } + bool is_interface() const { return !my_opposite_vol_phase.empty(); } + const PhaseTag & get_phase() const { ThrowRequire(!is_interface()); return my_touching_vol_phase; } + const PhaseTag & get_touching_phase() const { ThrowRequire(is_interface()); return my_touching_vol_phase; } + const PhaseTag & get_opposite_phase() const { ThrowRequire(is_interface()); return my_opposite_vol_phase; } + bool operator<(PhasePartTag rhs) const + { + // There must be a 1-1 mapping between conformal parts and PhasePartTags + // therefore the conformal part ordinal must be a unique identifier for the PhasePartTag and + // we can sort on just it. + return my_conformal_part_ordinal < rhs.my_conformal_part_ordinal; + } +protected: + unsigned my_conformal_part_ordinal; + unsigned my_nonconformal_part_ordinal; + unsigned my_original_part_ordinal; + PhaseTag my_touching_vol_phase; + PhaseTag my_opposite_vol_phase; +}; +typedef std::set< PhasePartTag > PhasePartSet; + +} // namespace krino + +#endif // Akri_PhaseTag_h diff --git a/packages/krino/krino/krino_lib/Akri_Phase_Support.cpp b/packages/krino/krino/krino_lib/Akri_Phase_Support.cpp new file mode 100644 index 000000000000..e611f2649c5e --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Phase_Support.cpp @@ -0,0 +1,961 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace krino{ + +bool Phase_Support::oneLevelSetPerPhase = false; +std::map Phase_Support::the_mesh_phases; +std::map Phase_Support::the_mesh_block_phases_by_name; + +void Phase_Support::check_phase_parts() const +{ + const LevelSetManager & region_ls = LevelSetManager::get(my_meta); + bool error = false; + for (auto&& phase_part : my_phase_parts) + { + const PhaseTag & part_phase = (phase_part.is_interface()) ? phase_part.get_touching_phase() : phase_part.get_phase(); + const std::set & phase_ls_sides = part_phase.ls_sides(); + for (std::set::const_iterator side_it = phase_ls_sides.begin(); side_it != phase_ls_sides.end(); ++side_it) + { + const std::string phase_ls_name = LevelSet::get_name(side_it->get_ls_identifier()); + if (!region_ls.has_levelSet(phase_ls_name)) + { + error = true; + stk::mesh::Part & conformal_part = my_meta.get_part(phase_part.get_conformal_part_ordinal()); + krinolog << "Error: Phase \"" << conformal_part.name() << "\" uses level set \"" << phase_ls_name << "\" but no such level set exists." << stk::diag::dendl; + } + } + } + ThrowErrorMsgIf(error, "Error: Phases are not defined correctly."); +} + +Phase_Support::Phase_Support(stk::mesh::MetaData & meta) +: my_meta(meta), + my_aux_meta(AuxMetaData::get(meta)), + nonconformalLsMapsAreFilled_(false) {} + +Phase_Support & +Phase_Support::get(stk::mesh::MetaData & meta) +{ + Phase_Support * support = const_cast(meta.get_attribute()); + if (nullptr == support) + { + support = new Phase_Support(meta); + ThrowAssert(nullptr != support); + meta.declare_attribute_with_delete(support); + } + return *support; +} + +Phase_Support & +Phase_Support::get(const stk::mesh::MetaData & meta) +{ + Phase_Support * support = const_cast(meta.get_attribute()); + ThrowRequireMsg(nullptr != support, "No Phase_Support found for MetaData."); + return *support; +} + +bool +Phase_Support::exists_and_has_phases_defined(const stk::mesh::MetaData & meta) +{ + Phase_Support * support = const_cast(meta.get_attribute()); + return support != nullptr && support->phases_defined(); +} + +stk::mesh::Selector Phase_Support::get_all_conformal_surfaces_selector() const +{ + std::vector surfaceParts; + for (auto && part : get_conformal_parts()) + if(is_interface(part)) + surfaceParts.push_back(part); + return selectUnion(surfaceParts); +} + +void +Phase_Support::addPhasePart(stk::mesh::Part & io_part, PhasePartSet & phase_parts, const NamedPhase & ls_phase) +{ + const std::string & phase_name = ls_phase.name(); + std::string phase_part_name = io_part.name(); + if(phase_name != "") // The alive phase for death problems just uses the io part as the conformal part + { + phase_part_name += "_" + phase_name; + } + + stk::mesh::Part & conformal_io_part = my_aux_meta.declare_io_part(phase_part_name, io_part.primary_entity_rank()); + ThrowAssert(phase_name != "" || conformal_io_part.mesh_meta_data_ordinal() == io_part.mesh_meta_data_ordinal()); + std::string topology_name = "INVALID_TOPOLOGY"; + if (stk::topology::INVALID_TOPOLOGY != io_part.topology()) + { + stk::mesh::set_topology(conformal_io_part, io_part.topology()); + topology_name = conformal_io_part.topology().name(); + } + + const std::string nonconf_name = io_part.name() + "_nonconformal"; + if(krinolog.shouldPrint(LOG_PARTS)) + { + krinolog << "Created conformal IO part " << conformal_io_part.name() << " with topology " << topology_name << stk::diag::dendl; + krinolog << " Adding phase part: conformal_part_name=" << conformal_io_part.name() + << ", nonconformal_part_name=" << nonconf_name + << ", original_mesh_part_name=" << io_part.name(); + krinolog << " " << ls_phase.tag(); + krinolog << stk::diag::dendl; + } + + + stk::mesh::Part & nonconf_part = my_aux_meta.get_part(nonconf_name); + phase_parts.insert(PhasePartTag(conformal_io_part.mesh_meta_data_ordinal(), nonconf_part.mesh_meta_data_ordinal(), io_part.mesh_meta_data_ordinal(), ls_phase.tag())); + + all_decomposed_blocks_selector |= conformal_io_part; + part_is_conformal_map[&conformal_io_part] = true; + part_to_nonconformal_part_map[&conformal_io_part] = &nonconf_part; + part_to_phase_map[&conformal_io_part] = ls_phase.tag(); + PhaseTagToPartMap & phase_to_conformal_map = nonconformal_to_phase_conformal_map[&nonconf_part]; + phase_to_conformal_map[ls_phase.tag()] = &conformal_io_part; +} + +void +Phase_Support::create_nonconformal_parts(const PartSet & decomposed_ioparts) +{ + const std::string nonconformal_part_suffix = "_nonconformal"; + for(PartSet::const_iterator it = decomposed_ioparts.begin(); it != decomposed_ioparts.end(); ++it) + { + const stk::mesh::Part & iopart = my_aux_meta.get_part((*it)->name()); + const std::string nonconformal_part_name = iopart.name() + nonconformal_part_suffix; + + if(!my_aux_meta.has_part(nonconformal_part_name)) + { + const bool restartOnlyIOPart = true; + stk::mesh::Part & nonconformal_part = + (iopart.topology() == stk::topology::INVALID_TOPOLOGY) ? + my_aux_meta.declare_io_part(nonconformal_part_name, iopart.primary_entity_rank(), restartOnlyIOPart) : + my_aux_meta.declare_io_part_with_topology(nonconformal_part_name, iopart.topology(), restartOnlyIOPart); + if(krinolog.shouldPrint(LOG_PARTS)) krinolog << "Created nonconformal part " << nonconformal_part_name << stk::diag::dendl; + + all_decomposed_blocks_selector |= iopart; + all_decomposed_blocks_selector |= nonconformal_part; + part_to_nonconformal_part_map[&iopart] = &nonconformal_part; + part_is_nonconformal_map[&nonconformal_part] = true; + } + } +} + +stk::mesh::PartVector +Phase_Support::get_nonconformal_parts() const +{ + stk::mesh::PartVector result; + for(const auto & entry : part_is_nonconformal_map) + { + if(entry.second) result.push_back(const_cast(entry.first)); + } + return result; +} + +stk::mesh::PartVector +Phase_Support::get_conformal_parts() const +{ + stk::mesh::PartVector result; + for(const auto & entry : part_is_conformal_map) + { + if(entry.second) result.push_back(const_cast(entry.first)); + } + return result; +} + +std::vector +Phase_Support::get_level_set_phases(const PhaseVec & mesh_phases, const LevelSet & levelSet) +{ + // gather up the set of phases that depend on this levelSet + PhaseTag ls_neg_phase; + PhaseTag ls_pos_phase; + ls_neg_phase.add(levelSet.get_identifier(),-1); + ls_pos_phase.add(levelSet.get_identifier(),+1); + std::vector ls_phases; + for(unsigned phase_index=0; phase_index touching_surface_ordinals; + get_input_surfaces_touching_block(input_block_surface_info, block_ptr->mesh_meta_data_ordinal(), touching_surface_ordinals); + for (auto && surf_ordinal : touching_surface_ordinals) + { + stk::mesh::Part & surf_part = mesh_meta.get_part(surf_ordinal); + blocks_and_touching_sides.insert(&surf_part); + + // Add all subset parts to the set of parts + if (!(surf_part.subsets().empty())) + { + for (stk::mesh::PartVector::const_iterator subset = surf_part.subsets().begin(); subset != surf_part.subsets().end(); ++subset) + { + blocks_and_touching_sides.insert(*subset); + } + } + } + } + return blocks_and_touching_sides; +} + +void +Phase_Support::create_phase_parts(const PhaseVec& ls_phases, const PartSet& decomposed_ioparts) +{ + for (auto && ls_phase : ls_phases) + { + for (auto && io_part : decomposed_ioparts) + { + addPhasePart(*io_part, my_phase_parts, ls_phase); + } + } +} + +void +Phase_Support::subset_and_alias_surface_phase_parts(const PhaseVec& ls_phases, + const PartSet& decomposed_ioparts) +{ + std::set touching_block_ordinals; + + for (auto && io_part : decomposed_ioparts) + { + if (!(io_part->subsets().empty())) + { + for (auto && ls_phase_entry : ls_phases) + { + const PhaseTag & ls_phase = ls_phase_entry.tag(); + //FIXME: remove const_cast's + stk::mesh::Part * conformal_iopart = const_cast(find_conformal_io_part(*io_part, ls_phase)); + ThrowRequire(NULL != conformal_iopart); + + stk::mesh::Part * nonconformal_iopart = const_cast(find_nonconformal_part(*io_part)); + ThrowRequire(NULL != nonconformal_iopart); + + for (stk::mesh::PartVector::const_iterator subset = io_part->subsets().begin(); + subset != io_part->subsets().end(); ++subset) + { + stk::mesh::Part * io_part_subset = *subset; + ThrowRequire(NULL != io_part_subset); + + stk::mesh::Part * conformal_iopart_subset = const_cast(find_conformal_io_part(*io_part_subset, ls_phase)); + ThrowRequire(NULL != conformal_iopart_subset); + + if(krinolog.shouldPrint(LOG_PARTS)) krinolog << "Adding " << conformal_iopart_subset->name() << " as subset of " << conformal_iopart->name() << stk::diag::dendl; + my_meta.declare_part_subset(*conformal_iopart, *conformal_iopart_subset); + + stk::mesh::Part * nonconformal_iopart_subset = const_cast(find_nonconformal_part(*io_part_subset)); + ThrowRequire(NULL != nonconformal_iopart_subset); + + if(krinolog.shouldPrint(LOG_PARTS)) krinolog << "Adding " << nonconformal_iopart_subset->name() << " as subset of " << nonconformal_iopart->name() << stk::diag::dendl; + my_meta.declare_part_subset(*nonconformal_iopart, *nonconformal_iopart_subset); + + get_input_blocks_touching_surface(my_input_block_surface_connectivity, io_part_subset->mesh_meta_data_ordinal(), touching_block_ordinals); + for (auto && touching_block_ordinal : touching_block_ordinals) + { + const std::string conformal_part_alias = conformal_iopart->name() + "_" + my_meta.get_part(touching_block_ordinal).name(); + if(krinolog.shouldPrint(LOG_PARTS)) krinolog << "Adding alias " << conformal_part_alias << " for conformal part " << conformal_iopart_subset->name() << stk::diag::dendl; + my_aux_meta.define_part_alias(*conformal_iopart_subset, conformal_part_alias); + } + } + } + } + } +} + +void +Phase_Support::build_decomposed_block_surface_connectivity() +{ + for (auto && part : my_meta.get_mesh_parts()) + { + if (part->primary_entity_rank() != my_meta.side_rank()) continue; + const PhasePartTag * phase_part = find_conformal_phase_part(*part); + if (nullptr == phase_part) continue; + stk::mesh::Part & orig_part = my_meta.get_part(phase_part->get_original_part_ordinal()); + if (orig_part == my_meta.universal_part()) continue; + + if (phase_part->is_interface()) + { + const stk::mesh::Part * conformal_touching_block = find_conformal_io_part(orig_part, phase_part->get_touching_phase()); + ThrowRequire(conformal_touching_block); + if(krinolog.shouldPrint(LOG_PARTS)) krinolog << "Surface " << part->name() << " touches block " << conformal_touching_block->name() << "\n"; + std::vector touching_blocks = my_meta.get_blocks_touching_surface(part); + if (std::find(touching_blocks.begin(), touching_blocks.end(), conformal_touching_block) == touching_blocks.end()) + { + touching_blocks.push_back(conformal_touching_block); + } + my_meta.set_surface_to_block_mapping(part, touching_blocks); + } + else + { + std::set touching_block_ordinals; + get_input_blocks_touching_surface(my_input_block_surface_connectivity, orig_part.mesh_meta_data_ordinal(), touching_block_ordinals); + + for (auto && touching_block_ordinal : touching_block_ordinals) + { + stk::mesh::Part & touching_block = my_meta.get_part(touching_block_ordinal); + const stk::mesh::Part * conformal_touching_block = find_conformal_io_part(touching_block, phase_part->get_phase()); + ThrowRequire(conformal_touching_block); + if(krinolog.shouldPrint(LOG_PARTS)) krinolog << "Surface " << part->name() << " touches block " << conformal_touching_block->name() << "\n"; + std::vector touching_blocks = my_meta.get_blocks_touching_surface(part); + if (std::find(touching_blocks.begin(), touching_blocks.end(), conformal_touching_block) == touching_blocks.end()) + { + touching_blocks.push_back(conformal_touching_block); + } + my_meta.set_surface_to_block_mapping(part, touching_blocks); + } + } + } +} + +void +Phase_Support::create_interface_phase_parts( + const PhaseVec& ls_phases, + const PartSet& decomposed_ioparts, + const Interface_Name_Generator& interface_name_gen) +{ + const int num_ls_phases = ls_phases.size(); + if (num_ls_phases > 1) + { + const int num_interfaces = num_ls_phases * (num_ls_phases - 1) / 2; + std::vector > interface_phases; + interface_phases.reserve(num_interfaces); + for (int i = 0; i < num_ls_phases; ++i) + { + for (int j = i+1; j < num_ls_phases; ++j) + { + std::pair phase_pair(i, j); + interface_phases.push_back(phase_pair); + } + } + ThrowRequire((int) interface_phases.size() == num_interfaces); + + for (int i = 0; i < num_interfaces; ++i) + { + const int name_compare = ls_phases[interface_phases[i].first].name().compare(ls_phases[interface_phases[i].second].name()); + ThrowRequire(name_compare != 0); + // form phase intersection + const auto & phase0 = (name_compare < 0) ? ls_phases[interface_phases[i].first] : ls_phases[interface_phases[i].second]; + const auto & phase1 = (name_compare < 0) ? ls_phases[interface_phases[i].second] : ls_phases[interface_phases[i].first]; + std::string superset_interface_phase_name = phase0.name() + "_" + phase1.name(); + const std::string superset_phase_part_name = interface_name_gen.interface_superset_name(superset_interface_phase_name); + + // assumes all sides have same topology + stk::mesh::Part &superset_phase_part = my_aux_meta.declare_io_part(superset_phase_part_name, my_meta.side_rank()); + + if(krinolog.shouldPrint(LOG_PARTS)) krinolog << "Adding interfacial phase part: conformal_part_name=" << superset_phase_part.name() << "\n"; + + for (auto && phase_pair : {std::make_pair(phase0,phase1), std::make_pair(phase1,phase0)}) + { + const auto & touching_phase = phase_pair.first.tag(); + const auto & opposite_phase = phase_pair.second.tag(); + + my_phase_parts.insert(PhasePartTag( + superset_phase_part.mesh_meta_data_ordinal(), my_meta.universal_part().mesh_meta_data_ordinal(), + my_meta.universal_part().mesh_meta_data_ordinal(), touching_phase, + opposite_phase)); + + for (auto * io_part : decomposed_ioparts) + { + ThrowRequire(NULL != io_part); + // only handle blocks for now -> creating interfacial surface "phases" + if (io_part->primary_entity_rank() == stk::topology::ELEMENT_RANK) + { + std::string subset_interface_phase_name = phase_pair.first.name() + "_" + phase_pair.second.name(); + const std::string subset_phase_part_name = interface_name_gen.interface_subset_name(io_part->name(), subset_interface_phase_name); + const bool restartOnlyIOPart = true; // Subset part is only output for restart + stk::mesh::Part &subset_phase_part = my_aux_meta.declare_io_part_with_topology(subset_phase_part_name, io_part->topology().side_topology(), restartOnlyIOPart); + my_meta.declare_part_subset(superset_phase_part, subset_phase_part); + + const stk::mesh::Part * const nonconf_part = find_nonconformal_part(*io_part); + ThrowRequire(NULL != nonconf_part); + + if(krinolog.shouldPrint(LOG_PARTS)) + { + krinolog << " Adding subset interfacial phase part: conformal_part_name=" << subset_phase_part.name() + << ", nonconformal_part_name=" << nonconf_part->name(); + krinolog << ", touching_phase=" << touching_phase + << ", opposite_phase=" << opposite_phase; + krinolog << "\n"; + } + + my_phase_parts.insert(PhasePartTag( + subset_phase_part.mesh_meta_data_ordinal(), nonconf_part->mesh_meta_data_ordinal(), + io_part->mesh_meta_data_ordinal(), touching_phase, + opposite_phase)); + + all_decomposed_blocks_selector |= subset_phase_part; + part_is_conformal_map[&subset_phase_part] = true; + part_to_nonconformal_part_map[&subset_phase_part] = nonconf_part; + auto & phase_to_conformal_map = nonconformal_to_phase_conformal_map[nonconf_part]; + auto & touching_phase_part = phase_to_conformal_map[touching_phase]; + auto & opposite_phase_part = phase_to_conformal_map[opposite_phase]; + auto key = std::make_pair(touching_phase_part, opposite_phase_part); + volume_to_interface_parts_map[key] = &subset_phase_part; + } + } + } + } + } +} + +void +Phase_Support::decompose_blocks( + const stk::mesh::PartVector& blocks_decomposed_by_ls, + const PhaseVec & ls_phases, + const Interface_Name_Generator & interface_name_gen) +{ + if(ls_phases.empty()) return; + + stk::topology topo; + for(auto && decompose_block : blocks_decomposed_by_ls) + { + topo = decompose_block->topology(); + if(topo != stk::topology::TET_4 && topo != stk::topology::TET_10 + && topo != stk::topology::TRI_3_2D && topo != stk::topology::TRI_6_2D) + { + stk::RuntimeDoomedAdHoc() << "Currently only Tetrahedron 4 or 10 (in 3D) and Triangle 3 or 6 (in 2D) element types are supported when using CDFEM.\n"; + return; + } + } + + PartSet decomposed_ioparts = get_blocks_and_touching_surfaces(my_meta, blocks_decomposed_by_ls, my_input_block_surface_connectivity); + + create_nonconformal_parts(decomposed_ioparts); + + create_phase_parts(ls_phases, decomposed_ioparts); + + subset_and_alias_surface_phase_parts(ls_phases, decomposed_ioparts); + + create_interface_phase_parts(ls_phases, decomposed_ioparts, interface_name_gen); +} + +//-------------------------------------------------------------------------------- + +void +Phase_Support::determine_block_phases(const std::set & FEmodel_block_names, const krino::PhaseVec & FEmodel_phases) +{ + if (FEmodel_phases.empty()) return; + + ThrowAssertMsg(my_mesh_block_phases.empty(), "determine_block_phases should only be called once per mesh"); + + for (auto && part : my_meta.get_parts()) + { + if (part->primary_entity_rank() == stk::topology::ELEMENT_RANK) + { + auto & block_phases = my_mesh_block_phases[part->mesh_meta_data_ordinal()]; + for(unsigned phase_index=0; phase_indexname() + "_" + phase_name; + std::transform(phase_part_name.begin(), phase_part_name.end(), phase_part_name.begin(), ::tolower); + if (FEmodel_block_names.find(phase_part_name) != FEmodel_block_names.end() ) + { + block_phases.insert(phase_index); + } + } + } + } +} + +//-------------------------------------------------------------------------------- + +void +Phase_Support::determine_block_phases(const PhaseVec & FEmodel_phases, const PartnamePhasenameMap & FEmodel_block_phase_names) +{ + if (FEmodel_phases.empty()) return; + + ThrowAssertMsg(my_mesh_block_phases.empty(), "determine_block_phases should only be called once per mesh"); + + if (FEmodel_block_phase_names.empty()) + { + stk::RuntimeDoomedAdHoc() << "Failure in determine_block_phases. Were the subdomains specified for the decomposed blocks in the finite element model specification?\n"; + return; + } + + std::map phase_name_to_index_map; + for(unsigned phase_index=0; phase_indexsecond); + } + } + } + else + { + stk::RuntimeDoomedAdHoc() << "The block " << map_entry.first << " is listed in the finite element model specification, but is not in the input mesh.\n"; + } + } +} + +//-------------------------------------------------------------------------------- + +std::vector +Phase_Support::get_blocks_decomposed_by_levelset(const std::vector ls_phases) const +{ + std::vector blocks_decomposed_by_ls; + for (auto && map_it : my_mesh_block_phases) + { + stk::mesh::Part & FEmodel_block = my_meta.get_part(map_it.first); + const auto & block_phases = map_it.second; + for(auto ls_phase : ls_phases) + { + if (block_phases.find(ls_phase) != block_phases.end()) + { + blocks_decomposed_by_ls.push_back(&FEmodel_block); + break; + } + } + } + + return blocks_decomposed_by_ls; +} + +//-------------------------------------------------------------------------------- + +void +Phase_Support::setup_phases(const krino::PhaseVec & FEmodel_phases) +{ + for (auto && map_it : my_mesh_block_phases) + { + stk::mesh::Part & FEmodel_block = my_meta.get_part(map_it.first); + const auto & block_phase_ordinals = map_it.second; + + PhaseVec block_phases; + for(auto && block_phase_ordinal : block_phase_ordinals) + { + block_phases.push_back(FEmodel_phases[block_phase_ordinal]); + } + + LS_Name_Generator interface_name_gen; + decompose_blocks({&FEmodel_block}, block_phases, interface_name_gen); + } + + build_decomposed_block_surface_connectivity(); +} + +//-------------------------------------------------------------------------------- + +void +Phase_Support::get_iopart_roots(const stk::mesh::Part & iopart, std::vector & subsets) +{ /* %TRACE% */ /* %TRACE% */ + if ( iopart.subsets().empty() ) + { + subsets.push_back(&iopart); + } + else + { + for (stk::mesh::PartVector::const_iterator subset = iopart.subsets().begin(); subset != iopart.subsets().end() ; ++subset ) + { + ThrowRequire(stk::io::is_part_io_part(**subset)); + get_iopart_roots(**subset, subsets); + } + } +} +//-------------------------------------------------------------------------------- +void +Phase_Support::get_blocks_touching_surface(const std::string & surface_name, std::vector & block_names) +{ /* %TRACE% */ /* %TRACE% */ + + std::set block_ordinal_set; + for (auto&& block_ordinal : block_ordinal_set) + { + block_names.push_back(my_meta.get_part(block_ordinal).name()); + } +} +//-------------------------------------------------------------------------------- +void +Phase_Support::get_input_surfaces_touching_block(const Block_Surface_Connectivity & input_block_surface_connectivity, + const stk::mesh::PartOrdinal block_ordinal, std::set & surface_ordinals) +{ + input_block_surface_connectivity.get_surfaces_touching_block(block_ordinal, surface_ordinals); +} + +//-------------------------------------------------------------------------------- +void +Phase_Support::get_input_blocks_touching_surface(const Block_Surface_Connectivity & input_block_surface_connectivity, + const stk::mesh::PartOrdinal surfaceOrdinal, std::set & blockOrdinals) +{ + input_block_surface_connectivity.get_blocks_touching_surface(surfaceOrdinal, blockOrdinals); +} +//-------------------------------------------------------------------------------- +const stk::mesh::Part * +Phase_Support::find_conformal_io_part(const stk::mesh::Part & io_part, const PhaseTag & phase) const +{ + const stk::mesh::Part * const nonconf_part = find_nonconformal_part(io_part); + auto entry = nonconformal_to_phase_conformal_map.find(nonconf_part); + if(entry == nonconformal_to_phase_conformal_map.end()) return &io_part; + PhaseTagToPartMap & tag_to_conformal_map = (*entry).second; + + const auto find_part = tag_to_conformal_map.find(phase); + if(find_part != tag_to_conformal_map.end()) + { + auto & tag_part_pair = *find_part; + return tag_part_pair.second; + } + + // This search is to handle the case where the phase passed in contains additional LS side tags that the + // conformal part phase tag doesn't care about. For example if the conformal phase is defined as "where LS1 is negative" + // it should match a phase tag that has (0,1) (1,-1) even though the tags are not == + // TODO: If we knew about all the LS's in the problem when decompose_blocks was called we could populate all the necessary entries + // then and wouldn't need this search. + for(auto && tag_part_pair : tag_to_conformal_map) + { + if(phase.contain(tag_part_pair.first)) + { + tag_to_conformal_map[phase] = tag_part_pair.second; + return tag_part_pair.second; + } + } + + return &io_part; +} + +//-------------------------------------------------------------------------------- + +bool +Phase_Support::is_conformal(const stk::mesh::Part * io_part) const +{ + PartToBoolMap::const_iterator entry = part_is_conformal_map.find(io_part); + if (entry != part_is_conformal_map.end()) return (entry->second); + return false; +} + +//-------------------------------------------------------------------------------- + +bool +Phase_Support::is_nonconformal(const stk::mesh::Part * io_part) const +{ + PartToBoolMap::const_iterator entry = part_is_nonconformal_map.find(io_part); + if (entry != part_is_nonconformal_map.end()) return (entry->second); + return false; +} + +//-------------------------------------------------------------------------------- + +bool +Phase_Support::is_interface(const stk::mesh::Part * io_part) const +{ + ThrowAssert(io_part); + auto phase_part = find_conformal_phase_part(*io_part); + return phase_part && phase_part->is_interface(); +} + +//-------------------------------------------------------------------------------- + +const stk::mesh::Part * +Phase_Support::find_nonconformal_part(const stk::mesh::Part & io_part) const +{ + PartToPartMap::const_iterator entry = part_to_nonconformal_part_map.find(&io_part); + if (entry != part_to_nonconformal_part_map.end()) return (entry->second); + return &io_part; +} + +//-------------------------------------------------------------------------------- + +const stk::mesh::Part * +Phase_Support::find_interface_part(const stk::mesh::Part & vol0, const stk::mesh::Part & vol1) const +{ + auto key = std::make_pair(&vol0, &vol1); + auto find_it = volume_to_interface_parts_map.find(key); + if(find_it != volume_to_interface_parts_map.end()) return find_it->second; + + return nullptr; +} + +//-------------------------------------------------------------------------------- + +const PhaseTag & +Phase_Support::get_iopart_phase(const stk::mesh::Part & io_part) const +{ + PartToPhaseTagMap::const_iterator entry = part_to_phase_map.find(&io_part); + if (entry != part_to_phase_map.end()) return (entry->second); + + static const PhaseTag empty_phase; + return empty_phase; +} + +//-------------------------------------------------------------------------------- + +void Phase_Support::register_blocks_for_level_set(LevelSet * levelSet, + const std::vector & blocks_decomposed_by_ls) +{ + nonconformalLsMapsAreFilled_ = false; + + // Loop over block names + for (auto && block_ptr : blocks_decomposed_by_ls) + { + // Add direct relationship between this block and level set + lsUsedByParts_[levelSet].insert(block_ptr); + + // Now get surfaces touching this block + std::set surfaceOrdinals; + get_input_surfaces_touching_block(my_input_block_surface_connectivity, block_ptr->mesh_meta_data_ordinal(), surfaceOrdinals); + for (auto && surfaceOrdinal : surfaceOrdinals) + { + // For each surface, add IO Part/Level Set pairing to maps + stk::mesh::Part & surfaceIOPart = my_meta.get_part(surfaceOrdinal); + lsUsedByParts_[levelSet].insert(&surfaceIOPart); + } + } +} + + +//-------------------------------------------------------------------------------- +bool Phase_Support::level_set_is_used_by_nonconformal_part(const LevelSet * levelSet, + const stk::mesh::Part * const ioPart) const +{ + if (!nonconformalLsMapsAreFilled_) + fill_nonconformal_level_set_maps(); + + IOPartSet & ioPartSet = lsUsedByNonconformalParts_[levelSet]; + bool contains = (ioPartSet.find(ioPart) != ioPartSet.end()); + return contains; +} + +//-------------------------------------------------------------------------------- +void Phase_Support::fill_nonconformal_level_set_maps() const +{ + if (nonconformalLsMapsAreFilled_ == true) return; + nonconformalLsMapsAreFilled_ = true; + + // In this method we duplicate the level-set-to-part maps, but use + // the nonconformal versions of each IOPart instead + + // lsUsedByNonconformalParts_ + lsUsedByNonconformalParts_.clear(); + std::map::const_iterator iterLSMap; + for (iterLSMap = lsUsedByParts_.begin(); iterLSMap != lsUsedByParts_.end(); ++iterLSMap) + { + const LevelSet * levelSet = iterLSMap->first; + const IOPartSet & ioPartSet = iterLSMap->second; + IOPartSet::const_iterator iterPart; + for (iterPart = ioPartSet.begin(); iterPart != ioPartSet.end(); ++iterPart) + { + const stk::mesh::Part * ioPart = *iterPart; + const stk::mesh::Part * nonconformalIOPart = find_nonconformal_part(*ioPart); + lsUsedByNonconformalParts_[levelSet].insert(nonconformalIOPart); + } + } +} + +const PhasePartTag * Phase_Support::find_conformal_phase_part(const stk::mesh::Part & conformal_part) const +{ + const PhasePartTag * result = NULL; + + const unsigned conformal_part_ordinal = conformal_part.mesh_meta_data_ordinal(); + + // Take advantage of the fact that PhasePartTags are uniquely identified by the conformal_part_ordinal + // and therefore the set of phase parts is sorted only based on that to use the binary search + // my_phase_parts.find(). + PhaseTag dummy_tag; + dummy_tag.add(LevelSet_Identifier(0), 1); + PhasePartTag search_tag(conformal_part_ordinal, 0, 0, dummy_tag); + + auto it = my_phase_parts.find(search_tag); + if(it != my_phase_parts.end()) result = &(*it); + + return result; +} + +//-------------------------------------------------------------------------------- +CDFEM_Irreversible_Phase_Support & +CDFEM_Irreversible_Phase_Support::get(stk::mesh::MetaData & meta) +{ + CDFEM_Irreversible_Phase_Support * support = const_cast(meta.get_attribute()); + if (NULL == support) + { + support = new CDFEM_Irreversible_Phase_Support(); + meta.declare_attribute_with_delete(support); + } + return *support; +} +//-------------------------------------------------------------------------- +CDFEM_Inequality_Spec * CDFEM_Irreversible_Phase_Support::add_death_spec(const std::string & death_name, bool is_death) +{ + // CDFEM death needs the decomposition to occur at the end of the time step, irreversible phase change + // needs it to occur at the start of the time step. For now throw if the user tries to do both in the same problem. + if(is_death) + { + has_death = true; + } + else + { + has_irreversible_phase_change = true; + } + ThrowInvalidArgMsgIf(has_death && has_irreversible_phase_change, + "Cannot have both CDFEM death and CDFEM irreversible phase change in the same problem."); + + for (auto&& death_spec : my_death_specs) + { + ThrowInvalidArgMsgIf(death_spec.name() == death_name, + "Only one CDFEM death specification with the same name is allowed per mesh (" << death_name << "). "); + } + my_death_specs.push_back(CDFEM_Inequality_Spec(death_name)); + return &my_death_specs.back(); +} + +//-------------------------------------------------------------------------- + +CDFEM_Inequality_Spec::CDFEM_Inequality_Spec(const std::string & name_) + : my_name(name_), + my_criterion_compare_type(INEQUALITY_CRITERION_TYPE_UNDEFINED), + my_ls(NULL) +{} + +CDFEM_Inequality_Spec::InequalityCriterionType +CDFEM_Inequality_Spec::int_to_inequality_criterion_type(const int inequality_criterion) +{ + /* %TRACE[ON]% */ Trace trace__("krino::CDFEM_Death_Spec::int_to_death_criterion_compare_type(const int death_criterion_compare)"); /* %TRACE% */ + + switch (inequality_criterion) + { + case int(INEQUALITY_CRITERION_TYPE_UNDEFINED): + return INEQUALITY_CRITERION_TYPE_UNDEFINED ; + case int(LESS_THAN ): + return LESS_THAN ; + case int(GREATER_THAN ): + return GREATER_THAN ; + default: + ThrowRuntimeError("Bad integer passed. Could not convert passed in integer " + << "death_criterion_compare = " << inequality_criterion + << " into a CDFEM_Death_Spec::DeathCriterionCompareType enum. " + << "The code and the Sierra XML database may be out of sync." << std::endl + << StackTrace); + } +} + +void CDFEM_Inequality_Spec::add_element_volume_name (const std::string & a_element_volume_name) +{ + /* %TRACE[NONE]% */ /* %TRACE% */ + + // Note: It is ok to add multiple volume names to vector, thus no return status given + my_element_volume_names.push_back(a_element_volume_name); +} + +bool CDFEM_Inequality_Spec::set_threshold_variable_name (const std::string & a_threshold_variable_name) +{ + /* %TRACE[ON]% */ Trace trace__("krino::CDFEM_Death_Spec::set_death_variable_name(const std::string & a_death_variable_name)"); /* %TRACE% */ + + // Set return status to false if value has already been set + bool return_status = (0 == my_threshold_variable_name.length()); + + if (return_status) + my_threshold_variable_name = a_threshold_variable_name; + + return return_status; +} + +bool CDFEM_Inequality_Spec::set_threshold_value (const double a_threshold_value) +{ + /* %TRACE[ON]% */ Trace trace__("krino::CDFEM_Death_Spec::set_threshold_value(const double a_threshold_value)"); /* %TRACE% */ + + // Set return status to false if value has already been set + bool return_status = (std::numeric_limits::max() != a_threshold_value); + + if (return_status) + my_threshold_value = a_threshold_value; + + return return_status; +} + +bool CDFEM_Inequality_Spec::set_criterion_compare_type + (const CDFEM_Inequality_Spec::InequalityCriterionType & a_criterion_compare_type) +{ + /* %TRACE[ON]% */ Trace trace__("krino::CDFEM_Death_Spec::set_criterion_compare_type(const CDFEM_Death_Spec::DeathCriterionCompareType & a_criterion_compare_type)"); /* %TRACE% */ + + // Set return status to false if value has already been set + bool return_status = (CDFEM_Inequality_Spec::INEQUALITY_CRITERION_TYPE_UNDEFINED == my_criterion_compare_type); + + if (return_status) + my_criterion_compare_type = a_criterion_compare_type; + + return return_status; +} + +void CDFEM_Inequality_Spec::sanity_check(const std::vector mesh_elem_blocks) const +{ + /* %TRACE[ON]% */ Trace trace__("krino::CDFEM_Death_Spec::sanity_check()"); /* %TRACE% */ + + ThrowErrorMsgIf(CDFEM_Inequality_Spec::INEQUALITY_CRITERION_TYPE_UNDEFINED == my_criterion_compare_type, + "CDFEM Death is not properly setup. Was the death criterion specified?"); + + const std::vector & volume_names = get_element_volume_names(); + + for (auto && block_name : volume_names) + { + ThrowErrorMsgIf( + std::find(mesh_elem_blocks.begin(), mesh_elem_blocks.end(), block_name) == mesh_elem_blocks.end(), + "Could not find an element volume named '" + << block_name << "' that was specified in the " + << "cdfem death command block for the cdfem death named '" + << name() << "'."); + } +} + +void CDFEM_Inequality_Spec::create_levelset(stk::mesh::MetaData & meta, stk::diag::Timer & parent_timer) +{ + /* %TRACE[ON]% */ Trace trace__("krino::CDFEM_Death_Spec::create_levelset()"); /* %TRACE% */ + + ThrowInvalidArgMsgIf( + LevelSetManager::get(meta).has_levelSet(my_name), + "Region already has a LevelSet named " << my_name); + + my_ls = &LevelSet::build(meta, my_name, parent_timer); + my_ls->set_isovar(get_threshold_variable_name(), get_threshold_value()); + + int deactivated_phase_sign = ( get_criterion_compare_type() == CDFEM_Inequality_Spec::LESS_THAN ) ? -1 : 1; + my_deactivated_phase.add(my_ls->get_identifier(), deactivated_phase_sign); + my_active_phase.add(my_ls->get_identifier(), -1 * deactivated_phase_sign); +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_Phase_Support.hpp b/packages/krino/krino/krino_lib/Akri_Phase_Support.hpp new file mode 100644 index 000000000000..05eb9443e4bf --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Phase_Support.hpp @@ -0,0 +1,212 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Phase_Support_h +#define Akri_Phase_Support_h +// +#include +#include + +#include +#include +#include + +#include + +namespace stk { namespace mesh { class BulkData; } } +namespace stk { namespace mesh { class MetaData; } } +namespace stk { namespace diag { class Timer; } } + +namespace krino { + +class AuxMetaData; +class LevelSet; +struct LS_Field; + +typedef std::set< const stk::mesh::Part * > IOPartSet; +typedef std::set< LevelSet * > LevelSetSet; +typedef std::map> PartPhaseMap; +typedef std::map> PartnamePhasenameMap; + +class Phase_Support { +public: + typedef std::set PartSet; + + static bool exists_and_has_phases_defined(const stk::mesh::MetaData & meta); + static Phase_Support & get(stk::mesh::MetaData & meta); + static Phase_Support & get(const stk::mesh::MetaData & meta); + + void get_blocks_touching_surface(const std::string & surface_name, std::vector & block_names); + static void get_input_surfaces_touching_block(const Block_Surface_Connectivity & input_block_surface_info, + const stk::mesh::PartOrdinal block_ordinal, std::set & surface_ordinals); + void get_input_blocks_touching_surface(const Block_Surface_Connectivity & input_block_surface_info, + const stk::mesh::PartOrdinal surfaceOrdinal, std::set & blockOrdinals); + + void check_phase_parts() const; + + bool phases_defined() const { return !my_phase_parts.empty(); } + + void decompose_blocks(const stk::mesh::PartVector& blocks_decomposed_by_ls, + const PhaseVec & ls_phases, const Interface_Name_Generator & interface_name_gen); + std::vector get_blocks_decomposed_by_levelset(const std::vector ls_phases) const; + void setup_phases(const krino::PhaseVec & FEmodel_phases); + void set_input_block_surface_connectivity(const Block_Surface_Connectivity & input_block_surface_info) { my_input_block_surface_connectivity = input_block_surface_info; } + void build_decomposed_block_surface_connectivity(); + + bool is_nonconformal(const stk::mesh::Part * io_part) const; + bool is_conformal(const stk::mesh::Part * io_part) const; + bool is_interface(const stk::mesh::Part * io_part) const; + const stk::mesh::Part * find_conformal_io_part(const stk::mesh::Part & io_part, const PhaseTag & phase) const; + const stk::mesh::Part * find_nonconformal_part(const stk::mesh::Part & io_part) const; + const stk::mesh::Part * find_interface_part(const stk::mesh::Part & vol0, const stk::mesh::Part & vol1) const; + const PhaseTag & get_iopart_phase(const stk::mesh::Part & io_part) const; + + void add_decomposed_part(const stk::mesh::Part & part) { all_decomposed_blocks_selector |= part; } + const stk::mesh::Selector & get_all_decomposed_blocks_selector() const { return all_decomposed_blocks_selector; } + + stk::mesh::Selector get_interface_part_selector(const LS_Field & ls_field); + void register_blocks_for_level_set(LevelSet * levelSet, + const std::vector & blocks_decomposed_by_ls); + stk::mesh::Selector get_all_conformal_surfaces_selector() const; + + bool level_set_is_used_by_nonconformal_part(const LevelSet * levelSet, const stk::mesh::Part * const ioPart) const; + + static bool has_one_levelset_per_phase() { return oneLevelSetPerPhase; } + static void set_one_levelset_per_phase(const bool val) { oneLevelSetPerPhase = val; } + + stk::mesh::PartVector get_nonconformal_parts() const; + stk::mesh::PartVector get_conformal_parts() const; + + void determine_block_phases(const std::set & FEmodel_block_names, const PhaseVec & FEmodel_phases); + void determine_block_phases(const PhaseVec & FEmodel_phases, const PartnamePhasenameMap & FEmodel_block_phase_names); + static PartSet get_blocks_and_touching_surfaces(const stk::mesh::MetaData & mesh_meta, const stk::mesh::PartVector& input_blocks, const Block_Surface_Connectivity & input_block_surface_info); + + static bool has_phases(const std::string & mesh_name) {std::map::const_iterator pos=the_mesh_phases.find(mesh_name); return (pos != the_mesh_phases.end());} + static PhaseVec & get_phases(const std::string & mesh_name) {return the_mesh_phases[mesh_name];} + static std::vector get_level_set_phases(const PhaseVec & mesh_phases, const LevelSet & levelSet); + static PartnamePhasenameMap & get_block_phases_by_name(const std::string & mesh_name) {return the_mesh_block_phases_by_name[mesh_name];} + +private: + Phase_Support(stk::mesh::MetaData & meta); + + const PhasePartTag * find_conformal_phase_part(const stk::mesh::Part & conformal_part) const; + void create_nonconformal_parts(const PartSet & decomposed_ioparts); + void addPhasePart(stk::mesh::Part & io_part, PhasePartSet & phase_parts, const NamedPhase & ls_phase); + void create_phase_parts(const PhaseVec& ls_phases, const PartSet& decomposed_ioparts); + void subset_and_alias_surface_phase_parts(const PhaseVec& ls_phases, const PartSet& decomposed_ioparts); + void create_interface_phase_parts(const PhaseVec& ls_phases, const PartSet& decomposed_ioparts, const Interface_Name_Generator& interface_name_gen); + void get_iopart_roots(const stk::mesh::Part & iopart, std::vector & subsets); + void fill_nonconformal_level_set_maps() const; + +private: + stk::mesh::MetaData & my_meta; + AuxMetaData & my_aux_meta; + PhasePartSet my_phase_parts; + Block_Surface_Connectivity my_input_block_surface_connectivity; + PartPhaseMap my_mesh_block_phases; + std::map lsUsedByParts_; + mutable bool nonconformalLsMapsAreFilled_; + + mutable std::map lsUsedByNonconformalParts_; + static bool oneLevelSetPerPhase; + static std::map the_mesh_phases; + static std::map the_mesh_block_phases_by_name; + + typedef std::map< const stk::mesh::Part *, bool, stk::mesh::PartLess> PartToBoolMap; + typedef std::map< const stk::mesh::Part *, const stk::mesh::Part *, stk::mesh::PartLess > PartToPartMap; + typedef std::map PhaseTagToPartMap; + typedef std::map PartToPhaseTagToPartMap; + typedef std::map PartToPhaseTagMap; + typedef std::map, + const stk::mesh::Part *> VolumePartsToInterfacePartMap; + mutable PartToPhaseTagToPartMap nonconformal_to_phase_conformal_map; + VolumePartsToInterfacePartMap volume_to_interface_parts_map; + PartToBoolMap part_is_conformal_map; + PartToBoolMap part_is_nonconformal_map; + PartToPartMap part_to_nonconformal_part_map; + PartToPhaseTagMap part_to_phase_map; + + stk::mesh::Selector all_decomposed_blocks_selector; +}; + +class CDFEM_Inequality_Spec { +public: + + CDFEM_Inequality_Spec(const std::string & name_); + ~CDFEM_Inequality_Spec() {} + const std::string & name() const {return my_name;} + + enum InequalityCriterionType + { + INEQUALITY_CRITERION_TYPE_UNDEFINED = 0, + LESS_THAN , + GREATER_THAN + }; + + static InequalityCriterionType int_to_inequality_criterion_type(const int inequality_criterion); + + void add_element_volume_name(const std::string & a_element_volue_name); + bool set_threshold_variable_name(const std::string & a_threshold_variable_name); + bool set_threshold_value(const double a_threshold_value); + bool set_criterion_compare_type(const InequalityCriterionType & a_criterion_compare_type); + + void sanity_check(const std::vector mesh_elem_blocks) const; + const std::vector & get_element_volume_names() const { return my_element_volume_names; } + const std::string & get_threshold_variable_name () const { return my_threshold_variable_name; } + double get_threshold_value () const { return my_threshold_value; } + InequalityCriterionType get_criterion_compare_type() const { return my_criterion_compare_type; } + const PhaseTag & get_deactivated_phase() const { return my_deactivated_phase; } + const PhaseTag & get_active_phase() const { return my_active_phase; } + void create_levelset(stk::mesh::MetaData & meta, stk::diag::Timer & parent_timer); + const LevelSet & get_levelset() const { ThrowRequire(my_ls != NULL); return *my_ls; } + LevelSet & get_levelset() { ThrowRequire(my_ls != NULL); return *my_ls; } + + // for unit testing + void set_phases(const PhaseTag & active_phase, const PhaseTag & inactive_phase) + { + my_active_phase = active_phase; + my_deactivated_phase = inactive_phase; + } +protected: + std::string my_name; + std::vector my_element_volume_names; + std::string my_threshold_variable_name; + double my_threshold_value; + InequalityCriterionType my_criterion_compare_type; + LevelSet * my_ls; + PhaseTag my_deactivated_phase; + PhaseTag my_active_phase; +}; +typedef std::vector< CDFEM_Inequality_Spec > CDFEM_Inequality_Spec_Vec; + +class CDFEM_Irreversible_Phase_Support { +public: + ~CDFEM_Irreversible_Phase_Support() {} + + static CDFEM_Irreversible_Phase_Support & get(stk::mesh::MetaData & meta); + + CDFEM_Inequality_Spec * add_death_spec(const std::string & death_name, bool is_death); + + bool is_active() const {return has_death || has_irreversible_phase_change;} + + const CDFEM_Inequality_Spec_Vec & get_death_specs() const {return my_death_specs;} + + // For irreversible phase changes we decompose at the start of the time step, for death at the end. + bool decompose_at_start_of_time_step() { ThrowAssert(has_irreversible_phase_change == !has_death); return has_irreversible_phase_change; } + +private: + CDFEM_Irreversible_Phase_Support() : has_death(false), has_irreversible_phase_change(false) {} +private: + bool has_death; + bool has_irreversible_phase_change; + CDFEM_Inequality_Spec_Vec my_death_specs; +}; + +} // namespace krino + +#endif // Akri_Phase_Support_h diff --git a/packages/krino/krino/krino_lib/Akri_Plane.hpp b/packages/krino/krino/krino_lib/Akri_Plane.hpp new file mode 100644 index 000000000000..b2c7773a50f6 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Plane.hpp @@ -0,0 +1,20 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Plane3d_h +#define Akri_Plane3d_h + +#include + +namespace krino { + +typedef stk::math::Plane3 Plane3d; + +} // namespace krino + +#endif // Akri_Plane3d_h diff --git a/packages/krino/krino/krino_lib/Akri_Plane_Intersections.cpp b/packages/krino/krino/krino_lib/Akri_Plane_Intersections.cpp new file mode 100644 index 000000000000..ce4e72be2807 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Plane_Intersections.cpp @@ -0,0 +1,144 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include + +namespace krino +{ + +bool +find_intersection_of_three_planes(const Plane3d & plane0, const Plane3d & plane1, const Plane3d & plane2, Vector3d & point) +{ + const Vector3d & normal0 = plane0.normal(); + const Vector3d & normal1 = plane1.normal(); + const Vector3d & normal2 = plane2.normal(); + + const double a00 = normal0[0]; + const double a01 = normal0[1]; + const double a02 = normal0[2]; + const double a10 = normal1[0]; + const double a11 = normal1[1]; + const double a12 = normal1[2]; + const double a20 = normal2[0]; + const double a21 = normal2[1]; + const double a22 = normal2[2]; + const double b0 = plane0.constant(); + const double b1 = plane1.constant(); + const double b2 = plane2.constant(); + const double det = a00*(a22*a11-a21*a12)-a10*(a22*a01-a21*a02)+a20*(a12*a01-a11*a02); + + if (det == 0) + return false; + + const double x =( b0*(a22*a11-a21*a12)-b1*(a22*a01-a21*a02)+b2*(a12*a01-a11*a02))/det; + const double y =(-b0*(a22*a10-a20*a12)+b1*(a22*a00-a20*a02)-b2*(a12*a00-a10*a02))/det; + const double z =( b0*(a21*a10-a20*a11)-b1*(a21*a00-a20*a01)+b2*(a11*a00-a10*a01))/det; + + point = Vector3d{x,y,z}; + return true; +} + +Vector3d +triangle_parametric_coordinates_of_projected_point(const std::array & triCoords, const Vector3d & pt) +{ + const Vector3d v1 = triCoords[1] - triCoords[0]; + const Vector3d v2 = triCoords[2] - triCoords[0]; + const Vector3d normal = Cross(v1,v2); + const double invNormalMag2 = 1./normal.length_squared(); + const Vector3d diff = pt - triCoords[0]; + Vector3d paramPt; + paramPt[0] = Dot(Cross(diff,v2),normal) * invNormalMag2; + paramPt[1] = Dot(Cross(v1,diff),normal) * invNormalMag2; + paramPt[2] = 0.; + return paramPt; +} + +static bool within_tet_bounds(const Vector3d & pt) +{ + const double tol = 1.e-13; + return (pt[0] > -tol) && + (pt[1] > -tol) && + (pt[2] > -tol) && + ((1.-pt[0]-pt[1]-pt[2]) > -tol); +} + +bool find_intersection_of_three_planes_within_tet(const Plane3d & plane0, const Plane3d & plane1, const Plane3d & plane2, Vector3d & intersectionPoint) +{ + return find_intersection_of_three_planes(plane0, plane1, plane2, intersectionPoint) && within_tet_bounds(intersectionPoint); +} + +static bool +find_intersection_of_two_planes_and_coordinate_plane(const int coordinatePlane, const Plane3d & plane0, const Plane3d & plane1, Vector3d & point) +{ + const Vector3d & normal0 = plane0.normal(); + const Vector3d & normal1 = plane1.normal(); + + const std::array,3> activeCoordsByPlane = {{ {{1,2}}, {{0,2}}, {{0,1}} }}; + const int i0 = activeCoordsByPlane[coordinatePlane][0]; + const int i1 = activeCoordsByPlane[coordinatePlane][1]; + + const double a00 = normal0[i0]; + const double a01 = normal0[i1]; + const double a10 = normal1[i0]; + const double a11 = normal1[i1]; + const double b0 = plane0.constant(); + const double b1 = plane1.constant(); + const double det = a00*a11-a10*a01; + + if (det == 0) + return false; + + point[i0] = (b0*a11-b1*a01)/det; + point[i1] = (b1*a00-b0*a10)/det; + point[coordinatePlane] = 0.; + + return true; +} + +bool find_intersection_of_two_planes_and_side_of_tet(const int side, const Plane3d & plane0, const Plane3d & plane1, Vector3d & intersectionPoint) +{ + bool intersectsPlaneThatCoincidesWithSideOfTet = false; + if (side == 1) + { + const Plane3d side1Plane{Vector3d{1.,0.,0.},Vector3d{0.,0.,1.},Vector3d{0.,1.,0.}}; + intersectsPlaneThatCoincidesWithSideOfTet = find_intersection_of_three_planes(plane0, plane1, side1Plane, intersectionPoint); + } + else + { + const int coordinatePlane = (side == 0) ? 1 : ((side == 2) ? 0 : 2); + intersectsPlaneThatCoincidesWithSideOfTet = find_intersection_of_two_planes_and_coordinate_plane(coordinatePlane, plane0, plane1, intersectionPoint); + } + + return intersectsPlaneThatCoincidesWithSideOfTet && within_tet_bounds(intersectionPoint); + +} + +bool find_intersection_of_two_2D_planes(const Plane3d & plane0, const Plane3d & plane1, Vector3d & intersectionPoint) +{ + return find_intersection_of_two_planes_and_coordinate_plane(2, plane0, plane1, intersectionPoint); +} + +bool within_tri_bounds(const Vector3d & triangleParamCoords) +{ + const double tol = 1.e-13; + return (triangleParamCoords[0] > -tol) && + (triangleParamCoords[1] > -tol) && + ((1.-triangleParamCoords[0]-triangleParamCoords[1]) > -tol); +} + +bool find_intersection_of_two_2D_planes_within_tri(const Plane3d & plane0, const Plane3d & plane1, Vector3d & intersectionPoint) +{ + return find_intersection_of_two_planes_and_coordinate_plane(2, plane0, plane1, intersectionPoint) && within_tri_bounds(intersectionPoint); +} + +} // namespace krino + + + diff --git a/packages/krino/krino/krino_lib/Akri_Plane_Intersections.hpp b/packages/krino/krino/krino_lib/Akri_Plane_Intersections.hpp new file mode 100644 index 000000000000..b7a20f805b00 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Plane_Intersections.hpp @@ -0,0 +1,31 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_PLANE_INTERSECTIONS_H_ +#define KRINO_INCLUDE_AKRI_PLANE_INTERSECTIONS_H_ +#include +#include + +namespace krino { + +bool find_intersection_of_three_planes(const Plane3d & plane0, const Plane3d & plane1, const Plane3d & plane2, Vector3d & point); + +bool find_intersection_of_three_planes_within_tet(const Plane3d & plane0, const Plane3d & plane1, const Plane3d & plane2, Vector3d & point); + +bool find_intersection_of_two_planes_and_side_of_tet(const int side, const Plane3d & plane0, const Plane3d & plane1, Vector3d & intersectionPoint); + +bool find_intersection_of_two_2D_planes(const Plane3d & plane0, const Plane3d & plane1, Vector3d & intersectionPoint); + +bool find_intersection_of_two_2D_planes_within_tri(const Plane3d & plane0, const Plane3d & plane1, Vector3d & intersectionPoint); + +Vector3d triangle_parametric_coordinates_of_projected_point(const std::array & triCoords, const Vector3d & pt); + +bool within_tri_bounds(const Vector3d & triangleParamCoords); +} + +#endif /* KRINO_INCLUDE_AKRI_PLANE_INTERSECTIONS_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_ProlongationData.cpp b/packages/krino/krino/krino_lib/Akri_ProlongationData.cpp new file mode 100644 index 000000000000..84a59e23926a --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_ProlongationData.cpp @@ -0,0 +1,659 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace krino{ + +ProlongationElementData::ProlongationElementData(const stk::mesh::BulkData& stk_mesh, const std::vector & children_data, const std::vector< std::vector > & children_intg_wts) +{ + ThrowAssert(!children_data.empty() && children_data.size() == children_intg_wts.size()); + + const unsigned num_intg_pts = children_intg_wts[0].size(); + + // homogenize fields + const stk::mesh::FieldVector & all_fields = stk_mesh.mesh_meta_data().get_fields(); + my_field_indices.resize(all_fields.size(), -1); + for ( stk::mesh::FieldVector::const_iterator it = all_fields.begin(); it != all_fields.end() ; ++it ) + { + const FieldRef field = **it; + + if( field.entity_rank()!=stk::topology::ELEMENT_RANK || !field.type_is() ) continue; + + bool any_child_has_field = false; + for (auto && child_data : children_data) + { + if (NULL != child_data->get_field_data(field)) + { + any_child_has_field = true; + break; + } + } + + if (any_child_has_field) + { + const unsigned field_length = field.length(); + const unsigned field_data_index = my_field_data.size(); + my_field_indices[field.field().mesh_meta_data_ordinal()] = field_data_index; + my_field_data.resize(field_data_index+field_length, 0.0); + + // TODO: Add a method to distinguish between vector fields and gauss point fields. + const bool data_is_gauss_pt_field = (num_intg_pts == field_length); + + if (data_is_gauss_pt_field) + { + double tot_child_sum = 0.; + double tot_child_vol = 0.; + + for (unsigned n = 0; n < children_data.size(); ++n) + { + const double * subelem_field_data = children_data[n]->get_field_data(field); + if(NULL == subelem_field_data) + { + continue; + } + + const std::vector & child_intg_wts = children_intg_wts[n]; + ThrowAssertMsg(child_intg_wts.size() == num_intg_pts, "Children have different integration rules."); + + for (unsigned j=0; jget_field_data(field); + if(NULL == subelem_field_data) + { + continue; + } + + const std::vector & child_intg_wts = children_intg_wts[n]; + // We could relax this assertion if we had another way to distinguish gauss point fields from vector fields + ThrowAssertMsg(child_intg_wts.size() == num_intg_pts, "Children have different integration rules."); + + double child_vol = 0.; + for (unsigned j=0; j() ) continue; + + double * val = field_data(field, entity); + const bool has_field = (NULL != val); + + if (has_field) + { + const unsigned field_length = field.length(); + my_field_indices[field.field().mesh_meta_data_ordinal()] = my_field_data.size(); + for (unsigned i=0; i() ) continue; + const unsigned field_length = field.length(); + + double * val = field_data(field, entity); + const double * prolong_field = get_field_data(field); + + if (nullptr == val) continue; + if(nullptr == prolong_field) + { + std::stringstream err_msg; + err_msg << "Missing prolongation field data when restoring fields on entity:\n"; + err_msg << stk_mesh.identifier(entity) << " of rank " << stk_mesh.entity_rank(entity); + err_msg << " with parts:\n "; + for(auto && part : stk_mesh.bucket(entity).supersets()) + { + err_msg << part->name() << ", "; + } + err_msg << "\n"; + err_msg << "Missing field data for field " << field.name() << "\n"; + throw std::runtime_error(err_msg.str()); + } + + for (unsigned i=0; i +ProlongationNodeData::get_fields_on_node(const stk::mesh::BulkData& mesh, stk::mesh::Entity entity) +{ + const stk::mesh::FieldVector & all_fields = mesh.mesh_meta_data().get_fields(); + std::vector entity_fields; + entity_fields.reserve(all_fields.size()); + for ( auto && fieldPtr : all_fields ) + { + const FieldRef field = *fieldPtr; + if (field.field().entity_rank() == stk::topology::NODE_RANK && nullptr != field_data(field, entity)) + entity_fields.push_back(field.field().mesh_meta_data_ordinal()); + } + + std::sort(entity_fields.begin(), entity_fields.end()); + return entity_fields; +} + +Vector3d +ProlongationNodeData::get_node_coordinates(const CDMesh & mesh, stk::mesh::Entity node) +{ + const double * coordsPtr = field_data(mesh.get_coords_field(), node); + ThrowAssert(coordsPtr); + Vector3d coords(coordsPtr, mesh.spatial_dim()); + FieldRef cdfemSnapDispField = mesh.get_cdfem_support().get_cdfem_snap_displacements_field(); + if (cdfemSnapDispField.valid()) + { + FieldRef oldCdfemSnapDispField = cdfemSnapDispField.field_state(stk::mesh::StateOld); + double * cdfemSnapDispPtr = field_data(cdfemSnapDispField, node); + double * oldCdfemSnapDispPtr = field_data(oldCdfemSnapDispField, node); + if (nullptr != cdfemSnapDispPtr) + coords += Vector3d(oldCdfemSnapDispPtr, mesh.spatial_dim()) - Vector3d(cdfemSnapDispPtr, mesh.spatial_dim()); + } + return coords; +} + +std::vector +ProlongationNodeData::get_node_io_parts(const stk::mesh::BulkData& stk_mesh, stk::mesh::Entity entity) +{ + // This list of parts is used to determine if a node needs to be ALE prolonged + stk::mesh::PartVector node_parts = filter_non_io_parts(stk_mesh.bucket(entity).supersets()); + std::vector node_part_ids; + node_part_ids.reserve(node_parts.size()); + for (auto * node_part : node_parts) + node_part_ids.push_back(node_part->mesh_meta_data_ordinal()); + std::sort(node_part_ids.begin(), node_part_ids.end()); + return node_part_ids; +} + +ProlongationNodeData::ProlongationNodeData(const CDMesh & mesh, stk::mesh::Entity node, bool communicate_me_to_all_sharers) + : ProlongationPointData(get_node_coordinates(mesh, node)), + my_entityId(mesh.stk_bulk().identifier(node)), + myCommunicateMeToAllSharersFlag(communicate_me_to_all_sharers) +{ + const stk::mesh::BulkData& stk_mesh = mesh.stk_bulk(); + + my_fields = get_fields_on_node(stk_mesh, node); + my_ioparts = get_node_io_parts(stk_mesh, node); + + save_fields(stk_mesh, node); +} + +ProlongationPointData::ProlongationPointData(const CDMesh & mesh, const FacetDistanceQuery & facet_dist_query, + const std::vector & facet_nodes) + : my_coordinates(facet_dist_query.closest_point()) +{ + const Vector3d node_wts = facet_dist_query.closest_point_weights(); + + // interpolate fields + const stk::mesh::FieldVector & all_fields = mesh.stk_meta().get_fields(); + my_field_indices.resize(all_fields.size(), -1); + for ( stk::mesh::FieldVector::const_iterator it = all_fields.begin(); it != all_fields.end() ; ++it ) + { + const FieldRef field = **it; + + if( field.entity_rank()!=stk::topology::NODE_RANK || !field.type_is() ) continue; + + const unsigned field_length = field.length(); + + bool any_node_has_field = false; + for (auto && facet_node : facet_nodes) + { + if (NULL != facet_node->get_field_data(field)) + { + any_node_has_field = true; + break; + } + } + + if (any_node_has_field) + { + const unsigned field_data_index = my_field_data.size(); + my_field_indices[field.field().mesh_meta_data_ordinal()] = field_data_index; + my_field_data.resize(field_data_index+field_length, 0.0); + + double node_wt_sum = 0.0; + for (unsigned n = 0; n < facet_nodes.size(); ++n) + { + const double * node_field_data = facet_nodes[n]->get_field_data(field); + + if(node_field_data) + { + node_wt_sum += node_wts[n]; + for (unsigned i=0; i nodeSharedProcs; + stk::pack_and_communicate(commSparse,[&]() + { + for (auto entry : proc_prolong_nodes) + { + const ProlongationNodeData & prolongNode = *entry.second; + if (prolongNode.communicate_me_to_all_sharers()) + { + stk::mesh::Entity node = mesh.get_entity(stk::topology::NODE_RANK, prolongNode.entityId()); + mesh.comm_shared_procs(node, nodeSharedProcs); + for (int procId : nodeSharedProcs) + prolongNode.pack_into_buffer(commSparse.send_buffer(procId)); + } + } + }); +} + +static +void pack_facet_prolong_nodes(const stk::mesh::BulkData & mesh, const ProlongFacetVec & proc_prolong_facets, const std::vector & proc_target_bboxes, stk::CommSparse &commSparse) +{ + stk::pack_and_communicate(commSparse,[&]() + { + for ( int procId=0; procIdpack_into_buffer(buffer); + } + }); +} + +static +void receive_and_build_prolong_nodes(const CDMesh & mesh, EntityProlongationNodeMap & proc_prolong_nodes, stk::CommSparse &commSparse) +{ + stk::unpack_communications(commSparse, [&](int procId) + { + stk::CommBuffer & buffer = commSparse.recv_buffer(procId); + + while ( buffer.remaining() ) + { + ProlongationNodeData * node = ProlongationNodeData::unpack_from_buffer( buffer, mesh.stk_meta() ); // This calls new to create a new ProlongationNodeData + EntityProlongationNodeMap::iterator it = proc_prolong_nodes.find(node->entityId()); + if( it == proc_prolong_nodes.end() || it->second == nullptr ) + { + proc_prolong_nodes[node->entityId()] = node; + } + else + { + delete node; + } + } + }); +} + +void ProlongationFacet::communicate_shared_nodes( const CDMesh & mesh, EntityProlongationNodeMap & proc_prolong_nodes ) +{ + stk::CommSparse commSparse(mesh.stk_bulk().parallel()); + pack_nodes_that_need_to_be_communicated_to_sharers(mesh.stk_bulk(), proc_prolong_nodes, commSparse); + receive_and_build_prolong_nodes(mesh, proc_prolong_nodes, commSparse); +} + + +void +ProlongationFacet::communicate_facet_nodes( const CDMesh & mesh, const ProlongFacetVec & proc_prolong_facets, EntityProlongationNodeMap & proc_prolong_nodes, const std::vector & proc_target_bboxes ) +{ /* %TRACE[ON]% */ Trace trace__("krino:ProlongationFacet::communicate_facet_nodes()"); /* %TRACE% */ + const int num_procs = mesh.stk_bulk().parallel_size(); + if ( num_procs == 1 ) return; // Don't talk to yourself, it's embarrassing + + stk::CommSparse commSparse(mesh.stk_bulk().parallel()); + pack_facet_prolong_nodes(mesh.stk_bulk(), proc_prolong_facets, proc_target_bboxes, commSparse); + receive_and_build_prolong_nodes(mesh, proc_prolong_nodes, commSparse); +} + +void +ProlongationNodeData::pack_into_buffer(stk::CommBuffer & b) const +{ + b.pack(my_entityId); + + b.pack(my_coordinates.data(),3); + + const size_t num_fields = my_fields.size(); + b.pack(num_fields); + b.pack(my_fields.data(), my_fields.size()); + + const size_t num_parts = my_ioparts.size(); + b.pack(num_parts); + b.pack(my_ioparts.data(), my_ioparts.size()); + + b.pack(my_field_indices.data(),my_field_indices.size()); + + const size_t num_field_data = my_field_data.size(); + b.pack(num_field_data); + b.pack(my_field_data.data(),my_field_data.size()); +} + +ProlongationNodeData * +ProlongationNodeData::unpack_from_buffer( stk::CommBuffer & b, const stk::mesh::MetaData & stk_meta ) +{ + stk::mesh::EntityId global_id; + b.unpack(global_id); + + Vector3d coords; + b.unpack(coords.data(),3); + + size_t num_fields = 0; + b.unpack(num_fields); + std::vector node_fields(num_fields); + b.unpack(node_fields.data(), num_fields); + + size_t num_parts = 0; + b.unpack(num_parts); + std::vector node_ioparts(num_parts); + b.unpack(node_ioparts.data(), num_parts); + + ProlongationNodeData * node = new ProlongationNodeData(global_id, coords, node_fields, node_ioparts); + + const size_t len_field_indices = stk_meta.get_fields().size(); + std::vector & field_indices = node->get_field_indices(); + field_indices.resize(len_field_indices); + b.unpack(field_indices.data(), len_field_indices); + + size_t num_field_data = 0; + b.unpack(num_field_data); + std::vector & field_data = node->get_field_data(); + field_data.resize(num_field_data); + b.unpack(field_data.data(), num_field_data); + + return node; +} + +std::string +ProlongationData::missing_prolongation_fields_for_entity( const CDMesh & mesh, const stk::mesh::Entity dst ) const +{ + std::string missing_fields; + const FieldSet & ale_prolongation_fields = mesh.get_ale_prolongation_fields(); + for ( auto&& field : ale_prolongation_fields ) + { + if( !field.type_is() || field.entity_rank() != mesh.stk_bulk().entity_rank(dst) ) continue; + + double * val = field_data(field, dst); + if (NULL != val && NULL == get_field_data(field)) + { + missing_fields = missing_fields + " " + field.name(); + } + } + + return missing_fields; +} + +void ProlongationFacet::compute_common_fields() +{ + my_common_fields.clear(); + for (unsigned side_node_index=0; side_node_index & node_fields = my_prolong_nodes[side_node_index]->get_fields(); + if (0 == side_node_index) + { + my_common_fields = node_fields; + } + else + { + std::vector working_set; + working_set.swap(my_common_fields); + std::set_intersection(working_set.begin(),working_set.end(),node_fields.begin(),node_fields.end(),std::back_inserter(my_common_fields)); + } + } +} + +ProlongationFacet::ProlongationFacet(const CDMesh & mesh, stk::mesh::Entity side) +: my_mesh(mesh) +{ + const stk::mesh::BulkData & stk_mesh = my_mesh.stk_bulk(); + + ThrowAssert(stk_mesh.num_elements(side) > 0); + stk::mesh::Entity elem0 = stk_mesh.begin_elements(side)[0]; + const PhaseTag elem0_phase = mesh.determine_entity_phase(elem0); + + const unsigned num_side_nodes = stk_mesh.bucket(side).topology().base().num_nodes(); + const stk::mesh::Entity* side_nodes = stk_mesh.begin_nodes(side); + my_prolong_nodes.resize(num_side_nodes); + + for (unsigned side_node_index=0; side_node_index( my_prolong_nodes[0]->get_coordinates(), my_prolong_nodes[1]->get_coordinates()); + } + else + { + ThrowAssert(3 == my_prolong_nodes.size()); + my_facet = std::make_unique( my_prolong_nodes[0]->get_coordinates(), my_prolong_nodes[1]->get_coordinates(), my_prolong_nodes[2]->get_coordinates()); + } +} + +ProlongationFacet::ProlongationFacet(const CDMesh & mesh, const std::vector & prolong_nodes, const std::vector & common_fields) +: my_mesh (mesh), + my_prolong_nodes(prolong_nodes), + my_common_fields(common_fields) +{ + ThrowAssert((int)my_prolong_nodes.size() == my_mesh.spatial_dim()); + if (2 == my_prolong_nodes.size()) + { + my_facet = std::make_unique( my_prolong_nodes[0]->get_coordinates(), my_prolong_nodes[1]->get_coordinates()); + } + else + { + ThrowAssert(3 == my_prolong_nodes.size()); + my_facet = std::make_unique( my_prolong_nodes[0]->get_coordinates(), my_prolong_nodes[1]->get_coordinates(), my_prolong_nodes[2]->get_coordinates()); + } +} + +void ProlongationFacet::update_prolongation_point_data(const FacetDistanceQuery & dist_query) const +{ + ThrowAssert(&dist_query.facet() == my_facet.get()); + my_prolongation_point_data = std::make_unique(my_mesh, dist_query, my_prolong_nodes); +} + +bool ProlongationFacet::communicate_me(const BoundingBox & proc_target_bbox) const +{ + for(auto && prolong_node : my_prolong_nodes) + { + if (!proc_target_bbox.contains(prolong_node->get_coordinates())) + { + return false; + } + } + return true; +} + +std::set +ProlongationFacet::get_facet_nodes_to_communicate( const ProlongFacetVec & proc_prolong_facets, const BoundingBox & proc_target_bbox ) +{ + std::set procFacetNodes; + + for ( auto && facet : proc_prolong_facets ) + if (facet->communicate_me(proc_target_bbox)) + for (auto node : facet->my_prolong_nodes) + procFacetNodes.insert(node); + + return procFacetNodes; +} + +void +ProlongationFacet::communicate( const CDMesh & mesh, ProlongFacetVec & proc_prolong_facets, EntityProlongationNodeMap & proc_prolong_nodes, const std::vector & proc_target_bboxes ) +{ /* %TRACE[ON]% */ Trace trace__("krino:ProlongationFacet::communicate()"); /* %TRACE% */ + communicate_shared_nodes(mesh, proc_prolong_nodes); + communicate_facet_nodes(mesh, proc_prolong_facets, proc_prolong_nodes, proc_target_bboxes); + communicate_facets(mesh, proc_prolong_facets, proc_target_bboxes); +} + +static +void pack_facets_within_proc_bboxes(const stk::mesh::BulkData & mesh, const ProlongFacetVec & proc_prolong_facets, const std::vector & proc_target_bboxes, stk::CommSparse &commSparse) +{ + stk::pack_and_communicate(commSparse,[&]() + { + for ( int procId=0; procId & proc_target_bboxes ) +{ + stk::CommSparse commSparse(mesh.stk_bulk().parallel()); + pack_facets_within_proc_bboxes(mesh.stk_bulk(), proc_prolong_facets, proc_target_bboxes, commSparse); + receive_and_build_prolong_facets(mesh, proc_prolong_facets, commSparse); +} + +void +ProlongationFacet::pack_into_buffer(stk::CommBuffer & b) const +{ + const size_t num_prolong_nodes = my_prolong_nodes.size(); + b.pack(num_prolong_nodes); + for(auto && prolong_node : my_prolong_nodes) + { + b.pack(prolong_node->entityId()); + } + + b.pack(my_common_fields.size()); + for (unsigned field : my_common_fields) + b.pack(field); +} + +ProlongationFacet * +ProlongationFacet::unpack_from_buffer(const CDMesh & mesh, stk::CommBuffer & b ) +{ + size_t num_prolong_nodes = 0; + b.unpack(num_prolong_nodes); + std::vector prolong_nodes(num_prolong_nodes); + + for(auto && prolong_node : prolong_nodes) + { + stk::mesh::EntityId node_id; + b.unpack(node_id); + prolong_node = mesh.fetch_prolong_node(node_id); + ThrowRequireMsg(prolong_node, "Communication error, missing prolongation node " << node_id << " on processor " << mesh.stk_bulk().parallel_rank()); + } + + size_t num_common_fields = 0; + b.unpack(num_common_fields); + + std::vector common_fields(num_common_fields); + for (size_t ifield=0; ifield +#include +#include + +#include +#include +#include +#include + +namespace krino { + +class ProlongationData { +public: + ProlongationData() {} + ~ProlongationData() {} + + const double * get_field_data( const stk::mesh::FieldBase& state_field ) const { const int field_index = my_field_indices[state_field.mesh_meta_data_ordinal()]; return (field_index < 0) ? nullptr : &my_field_data[field_index]; } + std::string missing_prolongation_fields_for_entity( const CDMesh & mesh, const stk::mesh::Entity dst ) const; + void restore_fields(const stk::mesh::BulkData& stk_mesh, stk::mesh::Entity entity) const; + +protected: + void save_fields(const stk::mesh::BulkData& stk_mesh, stk::mesh::Entity entity); + const std::vector & get_field_data() const { return my_field_data; } + std::vector & get_field_data() { return my_field_data; } + const std::vector & get_field_indices() const { return my_field_indices; } + std::vector & get_field_indices() { return my_field_indices; } + +protected: + mutable std::vector my_field_indices; + mutable std::vector my_field_data; +}; + +class ProlongationPointData : public ProlongationData { +public: + ProlongationPointData(const Vector3d & coordinates) : my_coordinates(coordinates) {} + ProlongationPointData(const CDMesh & mesh, const FacetDistanceQuery & facet_dist_query, const std::vector & facet_nodes); + ~ProlongationPointData() {} + + const Vector3d & get_coordinates() const { return my_coordinates; } + Vector3d & get_coordinates() { return my_coordinates; } + +protected: + Vector3d my_coordinates; +}; + +class ProlongationElementData : public ProlongationData { +public: + ProlongationElementData(const stk::mesh::BulkData& stk_mesh, stk::mesh::Entity element) : ProlongationData() { save_fields(stk_mesh, element); } + ProlongationElementData(const stk::mesh::BulkData& stk_mesh, const std::vector & children_data, const std::vector< std::vector > & children_intg_wts); + ~ProlongationElementData() {} + +private: + //: copy constructor not allowed + ProlongationElementData(const ProlongationElementData & copy); +}; + +class ProlongationNodeData : public ProlongationPointData { +public: + ProlongationNodeData(const CDMesh & mesh, stk::mesh::Entity node, bool communicate_me_to_all_sharers); + ProlongationNodeData(const stk::mesh::EntityId in_entityId, const Vector3d & coordinates, const std::vector & fields, const std::vector & ioparts) + : ProlongationPointData(coordinates), my_entityId(in_entityId), myCommunicateMeToAllSharersFlag(false), my_fields(fields), my_ioparts(ioparts) {} + + ~ProlongationNodeData() {} + + static std::vector get_node_io_parts(const stk::mesh::BulkData& stk_mesh, stk::mesh::Entity entity); + static std::vector get_fields_on_node(const stk::mesh::BulkData& stk_mesh, stk::mesh::Entity entity); + static Vector3d get_node_coordinates(const CDMesh & mesh, stk::mesh::Entity node); + + static ProlongationNodeData * unpack_from_buffer( stk::CommBuffer & b, const stk::mesh::MetaData & stk_meta ); // static method that builds surface from data in buffer for off-processor communication + void pack_into_buffer(stk::CommBuffer & b) const; + bool communicate_me_to_all_sharers() const { return myCommunicateMeToAllSharersFlag; } + + const std::vector & get_fields() const { ThrowRequireMsg(!my_fields.empty(), "Fields not set for prolongation node."); return my_fields; } + const std::vector & get_io_parts() const { ThrowRequireMsg(!my_ioparts.empty(), "IO Parts not set for prolongation node."); return my_ioparts; } + stk::mesh::EntityId entityId() const { return my_entityId; } + +protected: + const stk::mesh::EntityId my_entityId; + const bool myCommunicateMeToAllSharersFlag; + mutable std::vector my_fields; + mutable std::vector my_ioparts; + +private: + //: copy constructor not allowed + ProlongationNodeData(const ProlongationNodeData & copy); +}; + +class ProlongationFacet { +public: + ProlongationFacet(const CDMesh & mesh, const std::vector & prolong_nodes, const std::vector & common_fields); + ProlongationFacet(const CDMesh & mesh, stk::mesh::Entity side); + + static void communicate( const CDMesh & mesh, ProlongFacetVec & proc_prolong_facets, EntityProlongationNodeMap & proc_prolong_nodes, const std::vector & proc_target_bboxes ); + static std::set get_facet_nodes_to_communicate( const ProlongFacetVec & proc_prolong_facets, const BoundingBox & proc_target_bbox ); + + void pack_into_buffer(stk::CommBuffer & b) const; + static ProlongationFacet * unpack_from_buffer( const CDMesh & mesh, stk::CommBuffer & b ); // static method that builds surface from data in buffer for off-processor communication + void compute_common_fields(); + + static const BoundingBox & get_bounding_box(const ProlongationFacet * prolong_facet) { return prolong_facet->get_facet()->bounding_box(); } + + Facet * get_facet() const { return my_facet.get(); } + const std::vector & get_common_fields() const { return my_common_fields; } + const ProlongationPointData * get_prolongation_point_data(const FacetDistanceQuery & dist_query) const { update_prolongation_point_data(dist_query); return my_prolongation_point_data.get(); } + const std::vector & get_prolongation_nodes() const { return my_prolong_nodes; } + bool communicate_me(const BoundingBox & proc_target_bbox) const; + +protected: + void update_prolongation_point_data(const FacetDistanceQuery & dist_query) const; + +protected: + const CDMesh & my_mesh; + std::unique_ptr my_facet; + mutable std::unique_ptr my_prolongation_point_data; + mutable std::vector my_prolong_nodes; + mutable std::vector my_common_fields; + +private: + //: copy constructor not allowed + ProlongationFacet(const ProlongationFacet & copy); + static void communicate_shared_nodes( const CDMesh & mesh, EntityProlongationNodeMap & proc_prolong_nodes ); + static void communicate_facets( const CDMesh & mesh, ProlongFacetVec & proc_prolong_facets, const std::vector & proc_target_bboxes ); + static void communicate_facet_nodes( const CDMesh & mesh, const ProlongFacetVec & proc_prolong_facets, EntityProlongationNodeMap & proc_prolong_nodes, const std::vector & proc_target_bboxes ); +}; + +} // namespace krino + +#endif // Akri_ProlongationData_h diff --git a/packages/krino/krino/krino_lib/Akri_QualityMetric.cpp b/packages/krino/krino/krino_lib/Akri_QualityMetric.cpp new file mode 100644 index 000000000000..fcc6a98472ce --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_QualityMetric.cpp @@ -0,0 +1,127 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include + +namespace krino +{ + +double calculate_tet_volume_using_sides(const stk::math::Vector3d &side0, const stk::math::Vector3d &side2, const stk::math::Vector3d &side3) +{ + return Dot(side3, Cross(side2, side0))/6.0; +} + +double MeanRatioQualityMetric::tet_mean_ratio(const std::vector &nodeLocations) +{ + ThrowAssert(nodeLocations.size() == 4 || nodeLocations.size() == 10); + + const stk::math::Vector3d side0 = nodeLocations[1] - nodeLocations[0]; + const stk::math::Vector3d side2 = nodeLocations[0] - nodeLocations[2]; + const stk::math::Vector3d side3 = nodeLocations[3] - nodeLocations[0]; + + const double volumeMin = 1.0E-30; + const double tetVolume = calculate_tet_volume_using_sides(side0, side2, side3); + if( std::abs( tetVolume ) < volumeMin ) + return 0.0; + + const stk::math::Vector3d side1 = nodeLocations[2] - nodeLocations[1]; + const stk::math::Vector3d side4 = nodeLocations[3] - nodeLocations[1]; + const stk::math::Vector3d side5 = nodeLocations[3] - nodeLocations[2]; + + const double side0_length_squared = side0.length_squared(); + const double side1_length_squared = side1.length_squared(); + const double side2_length_squared = side2.length_squared(); + const double side3_length_squared = side3.length_squared(); + const double side4_length_squared = side4.length_squared(); + const double side5_length_squared = side5.length_squared(); + + const int sign = tetVolume < 0. ? -1 : 1; + return sign * 12. * std::pow(3.*std::abs(tetVolume), 2./3.) / (side0_length_squared + side1_length_squared + side2_length_squared + side3_length_squared + side4_length_squared + side5_length_squared); +} + +double ScaledJacobianQualityMetric::tet_scaled_jacobian(const std::vector &nodeLocations) +{ + ThrowAssert(nodeLocations.size() == 4 || nodeLocations.size() == 10); + + const stk::math::Vector3d side0 = nodeLocations[1] - nodeLocations[0]; + const stk::math::Vector3d side1 = nodeLocations[2] - nodeLocations[1]; + const stk::math::Vector3d side2 = nodeLocations[0] - nodeLocations[2]; + const stk::math::Vector3d side3 = nodeLocations[3] - nodeLocations[0]; + const stk::math::Vector3d side4 = nodeLocations[3] - nodeLocations[1]; + const stk::math::Vector3d side5 = nodeLocations[3] - nodeLocations[2]; + + const double jacobi = Dot(side3, Cross(side2, side0)); + + // products of lengths squared of each edge attached to a node. + const double side0_length_squared = side0.length_squared(); + const double side1_length_squared = side1.length_squared(); + const double side2_length_squared = side2.length_squared(); + const double side3_length_squared = side3.length_squared(); + const double side4_length_squared = side4.length_squared(); + const double side5_length_squared = side5.length_squared(); + + const double length_squared[4] = { + side0_length_squared * side2_length_squared * side3_length_squared, + side0_length_squared * side1_length_squared * side4_length_squared, + side1_length_squared * side2_length_squared * side5_length_squared, + side3_length_squared * side4_length_squared * side5_length_squared + }; + int which_node = 0; + if(length_squared[1] > length_squared[which_node]) + which_node = 1; + if(length_squared[2] > length_squared[which_node]) + which_node = 2; + if(length_squared[3] > length_squared[which_node]) + which_node = 3; + + double length_product = std::sqrt( length_squared[which_node] ); + if(length_product < std::abs(jacobi)) + length_product = std::abs(jacobi); + + const double lengthMin = 1.0E-30; + if( length_product < lengthMin ) + return 0.0; + + static const double root_of_2 = std::sqrt(2.0); + return root_of_2 * jacobi / length_product; +} + +double ScaledJacobianQualityMetric::tri2d_scaled_jacobian(const std::vector &nodeLocations) +{ + const double absScaledJacobian = tri3d_scaled_jacobian(nodeLocations); + const double normalZ = + (nodeLocations[1][0]-nodeLocations[0][0])*(nodeLocations[2][1]-nodeLocations[0][1]) - + (nodeLocations[1][1]-nodeLocations[0][1])*(nodeLocations[2][0]-nodeLocations[0][0]); + return (normalZ > 0.) ? absScaledJacobian : -absScaledJacobian; +} + +double ScaledJacobianQualityMetric::tri3d_scaled_jacobian(const std::vector &nodeLocations) +{ + ThrowAssert(nodeLocations.size() == 3 || nodeLocations.size() == 6); + + const stk::math::Vector3d edge0 = nodeLocations[1] - nodeLocations[0]; + const stk::math::Vector3d edge1 = nodeLocations[2] - nodeLocations[0]; + const stk::math::Vector3d edge2 = nodeLocations[2] - nodeLocations[1]; + + const double lenSqr0 = edge0.length_squared(); + const double lenSqr1 = edge1.length_squared(); + const double lenSqr2 = edge2.length_squared(); + + const double maxEdgeLengthProduct = std::sqrt( std::max(lenSqr0*lenSqr1, std::max(lenSqr1*lenSqr2, lenSqr0*lenSqr2)) ); + + const double lengthMin = 1.0E-30; + if( maxEdgeLengthProduct < lengthMin ) + return 0.0; + + static const double two_over_root_of_3 = 2./sqrt(3.0); + const double jacobian = Cross(edge0, edge1).length(); + return jacobian*two_over_root_of_3/maxEdgeLengthProduct; +} + +} diff --git a/packages/krino/krino/krino_lib/Akri_QualityMetric.hpp b/packages/krino/krino/krino_lib/Akri_QualityMetric.hpp new file mode 100644 index 000000000000..02599303f86f --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_QualityMetric.hpp @@ -0,0 +1,76 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef AKRI_QUALITY_METRIC_H +#define AKRI_QUALITY_METRIC_H +#include +#include + +namespace krino +{ + +class QualityMetric +{ +public: + virtual ~QualityMetric() {} + + virtual bool is_first_quality_metric_better_than_second(const double firstValue, const double secondValue) const = 0; + virtual double get_element_quality_metric(const std::vector &nodeLocations) const = 0; + virtual double get_best_value_for_metric() const = 0; + virtual double get_acceptable_value_for_metric() const = 0; +}; + +class MeanRatioQualityMetric : public QualityMetric +{ +public: + virtual ~MeanRatioQualityMetric() {} + + virtual double get_best_value_for_metric() const override { return 1.0; } + virtual bool is_first_quality_metric_better_than_second(const double firstValue, const double secondValue) const override + { + return static_cast(firstValue) > static_cast(secondValue); + } + + double get_element_quality_metric(const std::vector &nodeLocations) const override + { + return tet_mean_ratio(nodeLocations); + } + + static double tet_mean_ratio(const std::vector &nodeLocations); + + virtual double get_acceptable_value_for_metric() const override { return 0.2; } +}; + +class ScaledJacobianQualityMetric : public QualityMetric +{ +public: + ScaledJacobianQualityMetric() = default; + virtual ~ScaledJacobianQualityMetric() {} + + virtual double get_best_value_for_metric() const override { return 1.0; } + virtual bool is_first_quality_metric_better_than_second( + const double firstValue, const double secondValue) const override + { + return static_cast(firstValue) > static_cast(secondValue); + } + + double get_element_quality_metric(const std::vector &nodeLocations) const override + { + return (nodeLocations.size() == 4 || nodeLocations.size() == 10) ? tet_scaled_jacobian(nodeLocations) : tri2d_scaled_jacobian(nodeLocations); + } + + static double tet_scaled_jacobian(const std::vector &nodeLocations); + static double tri2d_scaled_jacobian(const std::vector &nodeLocations); + static double tri3d_scaled_jacobian(const std::vector &nodeLocations); + + virtual double get_acceptable_value_for_metric() const override { return 0.1; } +}; + +} + +#endif diff --git a/packages/krino/krino/krino_lib/Akri_RegionInterface.cpp b/packages/krino/krino/krino_lib/Akri_RegionInterface.cpp new file mode 100644 index 000000000000..f04136783043 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_RegionInterface.cpp @@ -0,0 +1,15 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +namespace krino{ + +std::unique_ptr RegionInterface::the_currently_parsed_region; + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_RegionInterface.hpp b/packages/krino/krino/krino_lib/Akri_RegionInterface.hpp new file mode 100644 index 000000000000..09248bed11a0 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_RegionInterface.hpp @@ -0,0 +1,65 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_RegionInterface_h +#define Akri_RegionInterface_h + +#include +#include + +namespace Ioss { class Region; } +namespace stk { namespace diag { class Timer; } } +namespace stk { namespace mesh { class MetaData; } } + +namespace krino { + +class RegionInterface { + + public: + static RegionInterface & set_currently_parsed_region(stk::mesh::MetaData & meta, + const std::string & region_name, + stk::diag::Timer & region_timer, + const std::string & name_of_input_mesh, + Ioss::Region * ioss_region) + { + the_currently_parsed_region = std::make_unique(meta, region_name, region_timer, name_of_input_mesh, ioss_region); + return *the_currently_parsed_region; + } + static void clear_currently_parsed_region() { the_currently_parsed_region.reset(); } + static RegionInterface & get_currently_parsed_region() { ThrowAssert(nullptr != the_currently_parsed_region); return *the_currently_parsed_region; } + + stk::mesh::MetaData & get_stk_mesh_meta_data() { return my_meta; } + const std::string & name() { return my_region_name; } + stk::diag::Timer & getRegionTimer() const { return my_region_timer; } + const std::string & name_of_input_mesh() const { return my_name_of_input_mesh; } + Ioss::Region * get_input_io_region() { return my_ioss_region; } + + // must be public to be used by make_unique + RegionInterface(stk::mesh::MetaData & meta, + const std::string & region_name, + stk::diag::Timer & region_timer, + const std::string & input_mesh, + Ioss::Region * ioss_region) + : my_meta(meta), + my_region_name(region_name), + my_region_timer(region_timer), + my_name_of_input_mesh(input_mesh), + my_ioss_region(ioss_region) {} + +private: + static std::unique_ptr the_currently_parsed_region; + stk::mesh::MetaData & my_meta; + const std::string my_region_name; + stk::diag::Timer & my_region_timer; + const std::string my_name_of_input_mesh; + Ioss::Region * my_ioss_region; +}; + +} // end krino namespace + +#endif /* Akri_RegionInterface_h */ diff --git a/packages/krino/krino/krino_lib/Akri_ReportHandler.hpp b/packages/krino/krino/krino_lib/Akri_ReportHandler.hpp new file mode 100644 index 000000000000..9c3c57c4f4d9 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_ReportHandler.hpp @@ -0,0 +1,21 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_REPORTHANDLER_H_ +#define KRINO_INCLUDE_AKRI_REPORTHANDLER_H_ + +#include +#include + +#define ParallelThrowRequire(parallel,expr) ThrowRequire(stk::is_true_on_all_procs(parallel,expr)) +#define ParallelThrowRequireMsg(parallel,expr,message) ThrowRequireMsg(stk::is_true_on_all_procs(parallel,expr),message) +#define ParallelThrowAssert(parallel,expr) ThrowAssert(stk::is_true_on_all_procs(parallel,expr)) +#define ParallelThrowAssertMsg(parallel,expr,message) ThrowAssertMsg(stk::is_true_on_all_procs(parallel,expr),message) + + +#endif /* KRINO_INCLUDE_AKRI_REPORTHANDLER_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_SearchTree.cpp b/packages/krino/krino/krino_lib/Akri_SearchTree.cpp new file mode 100644 index 000000000000..ffd80f22bb40 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_SearchTree.cpp @@ -0,0 +1,257 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include + +#include + +namespace krino{ + +template +SearchTree::SearchTree( std::vector & entities ) +{ /* %TRACE[ON]% */ Trace trace__("krino::SearchTree::SearchTree( std::vector & entities )"); /* %TRACE% */ + + if (!entities.empty()) + { + my_nodes.resize(2*entities.size() - 1); + const size_t tree_size = build( entities.begin(), entities.end() ); + ThrowRequire(tree_size == my_nodes.size()); + } +} + +template +SearchTree::SearchTree( const std::vector & entities, const std::function & get_bbox ) +{ /* %TRACE[ON]% */ Trace trace__("krino::SearchTree::SearchTree( EntityVec & entities, const std::function & get_bbox )"); /* %TRACE% */ + + if (!entities.empty()) + { + std::vector bbox_entities; + bbox_entities.reserve(entities.size()); + for (auto && entity : entities) + { + bbox_entities.emplace_back(get_bbox(entity), entity); + } + my_nodes.resize(2*entities.size() - 1); + const size_t tree_size = build( bbox_entities.begin(), bbox_entities.end() ); + ThrowRequire(tree_size == my_nodes.size()); + } +} + +template +void +SearchTree::find_closest_entities( const Vector3d & search_point, std::vector & return_vec, const Real max_search_radius ) +{ + return_vec.clear(); + + // Algorithm requires tol > std::numeric_limits::epsilon(), here just pick multiplier + const Real tol = 100.*std::numeric_limits::epsilon(); + + // if there are no entities at all return empty list + if ( empty() ) return; + + const VecType search_pt(search_point.data()); + + // first find upper bound for distance + // the very first estimate for this is the upper bound for the base of the tree + Real upperBnd2 = my_nodes[0].boxUpperBnd2( search_pt ); + Real max_search_radius2 = max_search_radius * max_search_radius; + + if ( upperBnd2 > max_search_radius2 && max_search_radius2 > 0.0 ) + { + upperBnd2 = max_search_radius2; + } + + // now descend relevant parts of tree updating the bounds as we go + Real lowerBnd2 = upperBnd2; + treeBounds(search_pt, lowerBnd2, upperBnd2); + + // exit now if no entities within narrow_band + if ( lowerBnd2 > max_search_radius2 && max_search_radius2 > 0.0 ) + { + return; + } + + // with the upper bound known, find all entities within the tolerance + find_closest_entities( search_pt, upperBnd2, tol, return_vec ); + + return; +} + +template +void +SearchTree::find_closest_entities( + const VecType & search_point, + const Real maxDist2, + const Real tol, + std::vector & nearest, + const size_t index) +{ /* %TRACE% */ /* %TRACE% */ + const SearchTreeNode & node = my_nodes[index]; + if ( node.have_children() ) + { + const size_t Lindex = index + 1; + const Real L_lowerBnd2 = my_nodes[Lindex].boxLowerBnd2( search_point ); + const Real L_eps = tol*(maxDist2+my_nodes[Lindex].boxSize2()); + if ( L_lowerBnd2 < maxDist2+L_eps ) + find_closest_entities( search_point, maxDist2, tol, nearest, Lindex ); + + const size_t Rindex = node.get_right_index(); + const Real R_lowerBnd2 = my_nodes[Rindex].boxLowerBnd2( search_point ); + const Real R_eps = tol*(maxDist2+my_nodes[Rindex].boxSize2()); + if ( R_lowerBnd2 < maxDist2+R_eps ) + find_closest_entities( search_point, maxDist2, tol, nearest, Rindex ); + } + else + { + EntityType entity = node.get_leaf_entity(); + nearest.push_back( entity ); + } +} + +template +void +SearchTree::treeBounds( const VecType & search_point, + Real & lowerBnd2, + Real & upperBnd2, + const size_t index) +{ /* %TRACE% */ /* %TRACE% */ + const SearchTreeNode & node = my_nodes[index]; + if (!node.have_children()) + { + if (0 == index) + { + // Special case of a single node. Otherwise, this node will already have been handled by parent. + compute_and_update_bounds(search_point, lowerBnd2, upperBnd2, index); + } + return; + } + + const size_t Lindex = index + 1; + const Real L_lowerBnd2 = compute_and_update_bounds(search_point, lowerBnd2, upperBnd2, Lindex); + + const size_t Rindex = node.get_right_index(); + const Real R_lowerBnd2 = compute_and_update_bounds(search_point, lowerBnd2, upperBnd2, Rindex); + + if ( L_lowerBnd2 < R_lowerBnd2 ) + { + if ( L_lowerBnd2 < upperBnd2 ) + treeBounds( search_point, lowerBnd2, upperBnd2, Lindex ); + if ( R_lowerBnd2 < upperBnd2 ) + treeBounds( search_point, lowerBnd2, upperBnd2, Rindex ); + } + else + { + if ( R_lowerBnd2 < upperBnd2 ) + treeBounds( search_point, lowerBnd2, upperBnd2, Rindex ); + if ( L_lowerBnd2 < upperBnd2 ) + treeBounds( search_point, lowerBnd2, upperBnd2, Lindex ); + } +} + +template +size_t +SearchTree::build( typename std::vector::iterator entity_begin, typename std::vector::iterator entity_end, const size_t index ) +{ + const size_t entity_size = std::distance(entity_begin, entity_end); + + // determine bounding box + BoundingBox & node_bbox = my_nodes[index].bounding_box(); + for ( auto entity_it = entity_begin; entity_it != entity_end; ++entity_it ) + { + const BoundingBox & entity_bbox = entity_it->first; + node_bbox.accommodate( entity_bbox ); + } + + if ( entity_size > 1 ) + { + // determine axis to split tree on + int max_spread_dim = node_bbox.max_span_direction(); + + auto compare = [max_spread_dim](const BoundingBoxEntity & lhs, const BoundingBoxEntity & rhs) { return lhs.first.center()[max_spread_dim] < rhs.first.center()[max_spread_dim]; }; + const size_t mid_pt = entity_size / 2; + + // This works around a bug in some versions of nth_element +#ifdef CRAY_LWK + std::partial_sort( entity_begin, entity_begin + mid_pt, entity_end, compare ); +#else + std::nth_element( entity_begin, entity_begin + mid_pt, entity_end, compare ); +#endif + + const size_t Lindex = index + 1; + const size_t Rindex = build( entity_begin, entity_begin + mid_pt, Lindex ); + my_nodes[index].set_right_index(Rindex); + const size_t next_index = build( entity_begin + mid_pt, entity_end, Rindex ); + return next_index; + } + else + { + ThrowAssert(1 == entity_size); + my_nodes[index].set_leaf_entity(entity_begin->second); + return index + 1; + } +} + +template +void +SearchTree::get_intersecting_entities( const BoundingBoxType& bbox, std::vector& return_vec, const size_t index ) +{ + const SearchTreeNode & node = my_nodes[index]; + if (!node.bounding_box().intersects(bbox)) return; + + if ( node.have_children() ) + { + if ( bbox.contains(node.bounding_box()) ) + { + add_descendants(return_vec, index); + } + else + { + const size_t Lindex = index + 1; + get_intersecting_entities( bbox, return_vec, Lindex ); + + const size_t Rindex = node.get_right_index(); + get_intersecting_entities( bbox, return_vec, Rindex ); + } + } + else + { + EntityType entity = node.get_leaf_entity(); + return_vec.push_back( entity ); + } +} + +template +void +SearchTree::add_descendants( std::vector& return_vec, const size_t index ) +{ + const SearchTreeNode & node = my_nodes[index]; + + if ( node.have_children() ) + { + const size_t Lindex = index + 1; + add_descendants( return_vec, Lindex ); + + const size_t Rindex = node.get_right_index(); + add_descendants( return_vec, Rindex ); + } + else + { + EntityType entity = node.get_leaf_entity(); + return_vec.push_back( entity ); + } +} + +// Explicit template instantiation +template class SearchTree; +template class SearchTree; + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_SearchTree.hpp b/packages/krino/krino/krino_lib/Akri_SearchTree.hpp new file mode 100644 index 000000000000..4e7b5cf3094f --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_SearchTree.hpp @@ -0,0 +1,119 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_SearchTree_h +#define Akri_SearchTree_h + +#include + +#include +#include + +namespace krino { + +template +class SearchTreeNode { +public: + typedef ENTITY EntityType; + typedef BoundingBox BoundingBoxType; + typedef BoundingBoxType::Real Real; + typedef BoundingBoxType::VecType VecType; + + SearchTreeNode() : m_entity(0), m_right_index(0) {} + + const BoundingBoxType & bounding_box() const { return m_bbox; } + BoundingBoxType & bounding_box() { return m_bbox; } + Real boxLowerBnd2( const VecType & x ) const { return ( m_bbox.SqrDistLowerBnd(x) ); } + Real boxUpperBnd2( const VecType & x ) const { return ( m_bbox.SqrDistUpperBnd(x) ); } + Real boxSize2() const { return m_bbox.SqrSize(); } + + bool have_children() const { return (0 != m_right_index); } + void set_right_index(size_t right_index) { m_right_index = right_index; } + size_t get_right_index() const { return m_right_index; } + void set_leaf_entity(EntityType leaf_entity) { m_entity = leaf_entity; } + EntityType get_leaf_entity() const { return m_entity; } + +private: + BoundingBoxType m_bbox; + EntityType m_entity; + size_t m_right_index; +}; + +template +class SearchTree { +public: + typedef ENTITY EntityType; + typedef BoundingBox BoundingBoxType; + typedef std::pair BoundingBoxEntity; + typedef BoundingBoxType::Real Real; + typedef BoundingBoxType::VecType VecType; + + SearchTree( std::vector & entities ); // vector is modified by sorting + SearchTree( const std::vector & entities, const std::function & get_bbox ); + SearchTree() = delete; + + // All entities within the max_search_radius that may be the closest to the given point will be returned. + // If max_search_radius = 0, then all entities that may be the closest to the given point will be returned. + void find_closest_entities( const Vector3d& search_point, std::vector& return_vec, const Real max_search_radius = 0.0 ); + + void get_intersecting_entities( const BoundingBoxType& bbox, std::vector& return_vec) + { + return_vec.clear(); + if (!empty()) + { + get_intersecting_entities( bbox, return_vec, 0); + } + } + + bool empty() const { return my_nodes.empty(); } + size_t storage_size() const { return my_nodes.size() * sizeof(SearchTreeNode); } + +private: + size_t build( typename std::vector::iterator entity_begin, typename std::vector::iterator entity_end, const size_t index = 0 ); + double compute_and_update_bounds( const VecType & x, + Real & lowerBnd2, + Real & upperBnd2, + const size_t index = 0 ); + void treeBounds( const VecType & x, + Real & lowerBnd2, + Real & upperBnd2, + const size_t index = 0 ); + void find_closest_entities( const VecType & x, + const Real maxDist2, + const Real tol, + std::vector & nearest, + const size_t index = 0 ); + void get_intersecting_entities( const BoundingBoxType& bbox, std::vector& return_vec, const size_t index ); + void add_descendants( std::vector& return_vec, const size_t index ); +private: + std::vector> my_nodes; +}; + +template +inline double +SearchTree::compute_and_update_bounds( const VecType & search_point, + Real & lowerBnd2, + Real & upperBnd2, + const size_t index) +{ /* %TRACE% */ /* %TRACE% */ + const SearchTreeNode & node = my_nodes[index]; + const Real node_lowerBnd2 = node.boxLowerBnd2( search_point ); + if ( node_lowerBnd2 < upperBnd2 ) + { + const Real node_upperBnd2 = node.boxUpperBnd2( search_point ); + if ( node_upperBnd2 < upperBnd2 ) + upperBnd2 = node_upperBnd2; + if (!node.have_children() && node_lowerBnd2 < lowerBnd2) + lowerBnd2 = node_lowerBnd2; + } + return node_lowerBnd2; +} + +} // namespace krino + +#endif // Akri_SearchTree_h diff --git a/packages/krino/krino/krino_lib/Akri_Segment.hpp b/packages/krino/krino/krino_lib/Akri_Segment.hpp new file mode 100644 index 000000000000..df9a117e7c5d --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Segment.hpp @@ -0,0 +1,83 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Segment_h +#define Akri_Segment_h + +#include +#include +#include +#include + +namespace krino { + +template +class Segment3 { + public: + typedef REAL Real; + Segment3() : nodes{} {} ///< Default all coords zero + Segment3(const Vec& n0, const Vec& n1) : nodes{{n0, n1}} {} ///< Explicit set two end points + Segment3(MemberInit type) {/* type == NONE */} + + Real Length() const { return (nodes[1]-nodes[0]).length(); } + Real LengthSquared() const { return (nodes[1]-nodes[0]).length_squared(); } + + const Vec& GetNode(const int index) const { return nodes[index]; } + Vec& GetNode(const int index) { return nodes[index]; } + + const std::array,2>& GetNodes() const { return nodes; } + std::array,2>& GetNodes() { return nodes; } + + // + // Find the closest point projection between point and the face. + // + Real closest_projection(const Vec &point) const { + Vec edge_dir(nodes[1]-nodes[0]); + Real dotValA = Dot(edge_dir,(point-nodes[0])); + if(dotValA <= 0.0) { return 0.0; } + Real dotValB = Dot(edge_dir,(point-nodes[1])); + if(dotValB >= 0.0) { return 1.0; } + Real lenSquared = edge_dir.length_squared(); + if(lenSquared == 0.0) { return 0.0; } + return dotValA / lenSquared; + } + Real closest_projection(const Vec &point, Vec &proj_point) const { + const Real location = closest_projection(point); + proj_point = (1.0-location)*GetNode(0) + location*GetNode(1); + return location; + } + + Real DistanceSquared(const Vec& x, Real & parametric_coord) const { + Vector3d closest_pt(MemberInit::NONE); + parametric_coord = closest_projection(x, closest_pt); + return (x - closest_pt).length_squared(); + } + Real DistanceSquared(const Vec& x) const { + Vector3d closest_pt(MemberInit::NONE); + closest_projection(x, closest_pt); + return (x - closest_pt).length_squared(); + } + + friend std::ostream& operator<<( std::ostream& out, const Segment3& seg ) + { + out << "Segment3:" + << " Node0= " << seg.GetNode(0)[0] << ", " << seg.GetNode(0)[1] << ", " << seg.GetNode(0)[2] + << " Node1= " << seg.GetNode(1)[0] << ", " << seg.GetNode(1)[1] << ", " << seg.GetNode(1)[2] + << "\n"; + return out; + } + + private: + std::array,2> nodes; +}; + +typedef Segment3 Segment3d; + +} // namespace krino + +#endif // Akri_Segment_h diff --git a/packages/krino/krino/krino_lib/Akri_Snap.cpp b/packages/krino/krino/krino_lib/Akri_Snap.cpp new file mode 100644 index 000000000000..d182524bf69a --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Snap.cpp @@ -0,0 +1,633 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../interface_geometry_interface/Akri_InterfaceGeometry.hpp" + +namespace krino +{ + +static stk::math::Vector3d compute_snap_location(const std::vector & nodeLocations, const std::vector & weights) +{ + stk::math::Vector3d snapLocation{stk::math::Vector3d::ZERO}; + for (size_t i=0; i & nodes, std::vector & nodeLocations) +{ + nodeLocations.clear(); + for (auto node : nodes) + nodeLocations.emplace_back(field_data(coordsField, node), dim); +} + +static void fill_global_ids_of_elements_using_node(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & elementSelector, + stk::mesh::Entity node, + std::vector & globalIdsOfSnapNodeElems) +{ + globalIdsOfSnapNodeElems.clear(); + for (auto elem : StkMeshEntities{mesh.begin_elements(node), mesh.end_elements(node)}) + if (elementSelector(mesh.bucket(elem))) + globalIdsOfSnapNodeElems.push_back(mesh.identifier(elem)); +} + +static double compute_quality_if_node_is_snapped_terminating_early_if_below_threshold(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & elementSelector, + const FieldRef coordsField, + stk::mesh::Entity node, + const stk::math::Vector3d & snapLocation, + const QualityMetric &qualityMetric, + const double qualityThreshold) +{ + const int dim = mesh.mesh_meta_data().spatial_dimension(); + + double qualityAfterSnap = qualityMetric.get_best_value_for_metric(); + std::vector nodeLocations; + + for (auto elem : StkMeshEntities{mesh.begin_elements(node), mesh.end_elements(node)}) + { + if (elementSelector(mesh.bucket(elem))) + { + nodeLocations.clear(); + for (auto elemNode : StkMeshEntities{mesh.begin_nodes(elem), mesh.end_nodes(elem)}) + { + if (elemNode == node) + nodeLocations.push_back(snapLocation); + else + nodeLocations.emplace_back(field_data(coordsField, elemNode), dim); + } + + const double elemQualityAfterSnap = qualityMetric.get_element_quality_metric(nodeLocations); + + if (qualityMetric.is_first_quality_metric_better_than_second(qualityAfterSnap, elemQualityAfterSnap)) + { + qualityAfterSnap = elemQualityAfterSnap; + if (qualityMetric.is_first_quality_metric_better_than_second(qualityThreshold, qualityAfterSnap)) + return qualityAfterSnap; + } + } + } + return qualityAfterSnap; +} + +static stk::math::Vector3d compute_intersection_point_location(const int dim, const FieldRef coordsField, const IntersectionPoint & intersectionPoint) +{ + std::vector intPtNodeLocations; + fill_node_locations(dim, coordsField, intersectionPoint.get_nodes(), intPtNodeLocations); + return compute_snap_location(intPtNodeLocations, intersectionPoint.get_weights()); +} + +static bool element_has_all_nodes(const std::vector & elemNodes, const std::vector & nodesToFind) +{ + for (auto && nodeToFind : nodesToFind) + if (std::find(elemNodes.begin(), elemNodes.end(), nodeToFind) == elemNodes.end()) + return false; + return true; +} + +static double estimate_quality_of_cutting_intersection_points(const stk::mesh::BulkData & mesh, + const FieldRef coordsField, + const std::vector & elemNodes, + const std::vector & elemNodeCoords, + const std::vector intersectionPointIndices, + const std::vector & intersectionPoints, + const QualityMetric &qualityMetric) +{ + if (intersectionPointIndices.empty()) + return std::max(0., qualityMetric.get_element_quality_metric(elemNodeCoords)); + + // apply the front intersection point to element and recursively call with remaining intersection points + const IntersectionPoint & intPtToApply = intersectionPoints[*intersectionPointIndices.begin()]; + const std::vector remainingIntersectionPointIndices(intersectionPointIndices.begin()+1, intersectionPointIndices.end()); + + const stk::math::Vector3d intPtLocation = compute_intersection_point_location(mesh.mesh_meta_data().spatial_dimension(), coordsField, intPtToApply); + + const auto & intPtNodes = intPtToApply.get_nodes(); + double qualityAfterCut = qualityMetric.get_best_value_for_metric(); + if (element_has_all_nodes(elemNodes, intPtNodes)) + { + std::vector cutElemNodes; + std::vector cutElemNodeCoords; + + for (auto intPtNode : intPtNodes) + { + cutElemNodes.clear(); + cutElemNodeCoords.clear(); + for (size_t nodeIndex=0; nodeIndex & interpNodes) +{ + for (auto && interpNode : interpNodes) + if (interpNode != node && !parts_are_compatible_for_snapping_when_ignoring_phase(mesh, auxMeta, phaseSupport, node, interpNode)) + return false; + return true; +} + +static double get_node_intersection_point_weight(const IntersectionPoint & intersectionPoint, stk::mesh::Entity node) +{ + const std::vector & nodes = intersectionPoint.get_nodes(); + const auto iter = std::find(nodes.begin(), nodes.end(), node); + ThrowRequire(iter != nodes.end()); + const auto index = std::distance(nodes.begin(), iter); + return intersectionPoint.get_weights()[index]; +} + +std::vector get_sorted_node_ids(const stk::mesh::BulkData & mesh, const std::vector & nodes) +{ + std::vector nodeIds; + nodeIds.reserve(nodes.size()); + for (auto && node : nodes) nodeIds.push_back(mesh.identifier(node)); + std::sort(nodeIds.begin(), nodeIds.end()); + return nodeIds; +} + +static void sort_intersection_points_for_cutting(const stk::mesh::BulkData & mesh, + const FieldRef coordsField, + const std::vector & intersectionPoints, + const stk::mesh::Entity node, + const bool globalIDsAreParallelConsistent, + std::vector & sortedIntersectionPointIndices) +{ + // This sorter is designed to match the priority used by the cutting algorithm with CUT_QUADS_BY_MINIMIZING_ANGLES, especially in 3D + auto sorter = [&intersectionPoints, &mesh, &coordsField, node, globalIDsAreParallelConsistent](const size_t intPtIndex0, const size_t intPtIndex1) + { + const IntersectionPoint & intPt0 = intersectionPoints[intPtIndex0]; + const IntersectionPoint & intPt1 = intersectionPoints[intPtIndex1]; + const size_t numDomains0 = intPt0.get_sorted_domains().size(); + const size_t numDomains1 = intPt1.get_sorted_domains().size(); + if (numDomains0 != numDomains1) + return numDomains0 > numDomains1; + // reduce precision to float to handle "ties" + const float wt0 = get_node_intersection_point_weight(intPt0, node); + const float wt1 = get_node_intersection_point_weight(intPt1, node); + if (wt0 != wt1) + return wt0 < wt1; + + if (globalIDsAreParallelConsistent) + { + const std::vector sortedNodes0 = get_sorted_node_ids(mesh, intPt0.get_nodes()); + const std::vector sortedNodes1 = get_sorted_node_ids(mesh, intPt1.get_nodes()); + return sortedNodes1 < sortedNodes0; + } + + const stk::math::Vector3d x0 = compute_intersection_point_location(mesh.mesh_meta_data().spatial_dimension(), coordsField, intPt0); + const stk::math::Vector3d x1 = compute_intersection_point_location(mesh.mesh_meta_data().spatial_dimension(), coordsField, intPt1); + return is_less_than_in_x_then_y_then_z(x0, x1); + }; + std::sort(sortedIntersectionPointIndices.begin(), sortedIntersectionPointIndices.end(), sorter); +} + +static void fill_sorted_intersection_point_indices_for_node_for_domains(const stk::mesh::BulkData & mesh, + const FieldRef coordsField, + const std::vector & intersectionPoints, + const std::vector & candidatesIntersectionPointIndices, + const stk::mesh::Entity node, + const std::vector & domains, + const bool globalIDsAreParallelConsistent, + std::vector & sortedIntersectionPointIndices) +{ + sortedIntersectionPointIndices.clear(); + for (auto && intPtIndex : candidatesIntersectionPointIndices) + { + if (first_sorted_vector_of_domains_contains_all_domains_in_second_vector(domains, intersectionPoints[intPtIndex].get_sorted_domains())) + sortedIntersectionPointIndices.push_back(intPtIndex); + } + + sort_intersection_points_for_cutting(mesh, coordsField, intersectionPoints, node, globalIDsAreParallelConsistent, sortedIntersectionPointIndices); +} + +static std::set get_intersected_elements(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & elementSelector, + const std::vector & intersectionPoints, + const std::vector & intersectionPointIndices) +{ + std::vector cutElems; + std::set intersectedElements; + for (size_t intPtIndex : intersectionPointIndices) + { + stk::mesh::get_entities_through_relations(mesh, intersectionPoints[intPtIndex].get_nodes(), stk::topology::ELEMENT_RANK, cutElems); + for (auto && cutElem : cutElems) + if (elementSelector(mesh.bucket(cutElem))) + intersectedElements.insert(cutElem); + } + return intersectedElements; +} + +static std::map> get_node_to_intersection_point_indices(const stk::mesh::BulkData & mesh, + const std::vector & intersectionPoints) +{ + std::map> nodeToInsersectionPointIndices; + for (size_t intersectionPointIndex=0; intersectionPointIndex, std::map> determine_quality_per_node_per_domain(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & elementSelector, + const FieldRef coordsField, + const std::vector & intersectionPoints, + const QualityMetric &qualityMetric, + const bool globalIDsAreParallelConsistent) +{ + const int dim = mesh.mesh_meta_data().spatial_dimension(); + + const auto nodeToInsersectionPointIndices = get_node_to_intersection_point_indices(mesh, intersectionPoints); + + std::vector sortedIntersectionPointIndices; + std::vector elemNodes; + std::vector elemNodeCoords; + + std::map, std::map> domainsToNodesToQuality; + for (auto entry : nodeToInsersectionPointIndices) + { + stk::mesh::Entity node = entry.first; + const auto nodeIntersectionPointIndices = entry.second; + + std::set> nodeIntPtDomains; + for (auto && intPtIndex : nodeIntersectionPointIndices) + nodeIntPtDomains.insert(intersectionPoints[intPtIndex].get_sorted_domains()); + + for (auto && intPtDomains : nodeIntPtDomains) + { + fill_sorted_intersection_point_indices_for_node_for_domains(mesh, coordsField, intersectionPoints, nodeIntersectionPointIndices, node, intPtDomains, globalIDsAreParallelConsistent, sortedIntersectionPointIndices); + const std::set intersectedElements = get_intersected_elements(mesh, elementSelector, intersectionPoints, sortedIntersectionPointIndices); + + double qualityAfterCut = qualityMetric.get_best_value_for_metric(); + for (auto && elem : intersectedElements) + { + elemNodes.assign(mesh.begin_nodes(elem), mesh.end_nodes(elem)); + fill_node_locations(dim, coordsField, elemNodes, elemNodeCoords); + const double elemQualityAfterCuts = estimate_quality_of_cutting_intersection_points(mesh, coordsField, elemNodes, elemNodeCoords, sortedIntersectionPointIndices, intersectionPoints, qualityMetric); + + if (qualityMetric.is_first_quality_metric_better_than_second(qualityAfterCut, elemQualityAfterCuts)) + qualityAfterCut = elemQualityAfterCuts; + } + + domainsToNodesToQuality[intPtDomains][mesh.identifier(node)] = qualityAfterCut; + } + } + + return domainsToNodesToQuality; +} + +std::vector +build_snap_infos_from_intersection_points(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & elementSelector, + const NodeToCapturedDomainsMap & nodesToCapturedDomains, + const std::vector & intersectionPoints, + const QualityMetric &qualityMetric, + const bool globalIDsAreParallelConsistent) +{ + std::vector snapInfos; + + const AuxMetaData & auxMeta = AuxMetaData::get(mesh.mesh_meta_data()); + const Phase_Support phaseSupport = Phase_Support::get(mesh.mesh_meta_data()); + const FieldRef coordsField(mesh.mesh_meta_data().coordinate_field()); + const int dim = mesh.mesh_meta_data().spatial_dimension(); + std::vector nodeLocations; + std::vector procsThatNeedToKnowAboutThisInfo; + std::vector globalIdsOfSnapNodeElems; + + int owner = mesh.parallel_rank(); + + const auto domainsToNodesToQuality = determine_quality_per_node_per_domain(mesh, elementSelector, coordsField, intersectionPoints, qualityMetric, globalIDsAreParallelConsistent); + + for (size_t intersectionPointIndex=0; intersectionPointIndexsecond; + const double cutQualityEstimate = nodesToQuality.at(mesh.identifier(node)); + + // For face and volume cuts, allow quality to go down to acceptable_value_for_metric because estimate is not that good + //const double minAcceptableQuality = (nodes.size() == 2) ? cutQualityEstimate : std::min(qualityMetric.get_acceptable_value_for_metric(), cutQualityEstimate); + const double minAcceptableQuality = cutQualityEstimate; + + const double postSnapQuality = compute_quality_if_node_is_snapped_terminating_early_if_below_threshold(mesh, elementSelector, coordsField, node, snapLocation, qualityMetric, minAcceptableQuality); + if (qualityMetric.is_first_quality_metric_better_than_second(postSnapQuality, minAcceptableQuality)) + { + const size_t nodeGlobalId = mesh.identifier(node); + + fill_global_ids_of_elements_using_node(mesh, elementSelector, node, globalIdsOfSnapNodeElems); + fill_procs_owning_or_sharing_or_ghosting_node(mesh, node, procsThatNeedToKnowAboutThisInfo); + + snapInfos.emplace_back(nodeGlobalId, intersectionPointIndex, nodeLocations[nodeIndex], owner, procsThatNeedToKnowAboutThisInfo, globalIdsOfSnapNodeElems, postSnapQuality, snapLocation, nodes.size()); + } + else if (krinolog.shouldPrint(LOG_DEBUG)) + { + krinolog << "Skipping snap of " << mesh.identifier(node) << " to " << snapLocation << " at " << debug_output(mesh, intersectionPoint) << " with snap quality at or below " << postSnapQuality << " and estimated cut quality " << cutQualityEstimate << stk::diag::dendl; + } + } + } + } + + return snapInfos; +} + +void interpolate_nodal_field(const stk::mesh::BulkData & mesh, + stk::mesh::Entity node, const FieldRef field, + const std::vector & interpNodes, + const std::vector & interpWeights, + std::vector & scratch) +{ + const unsigned fieldLength = field.length(); + + double * val = field_data(field, node); + if (nullptr == val) return; + + scratch.assign(fieldLength, 0.0); + + for (size_t iNode=0; iNode(field, interpNodes[iNode]); + if (nullptr == nodeVal) + { + krinolog << "When snapping node " << mesh.identifier(node) << ", the field " << field.name() << " is missing on interpolating node " << mesh.identifier(interpNodes[iNode]) << stk::diag::dendl; + krinolog << "Should the field " << field.name() << " be an interpolation field?" << stk::diag::dendl; + ThrowRequireMsg(false, "Interpolation field missing on interpolation node " << mesh.identifier(interpNodes[iNode])); + } + + for (unsigned i=0; i & intersectionPoints, + const std::vector & snapInfos, + NodeToCapturedDomainsMap & nodesToCapturedDomains ) +{ + ThrowRequire(mesh.parallel_size() == 1 || mesh.is_automatic_aura_on()); + + std::vector< const stk::mesh::FieldBase *> interpFieldVec; + for(auto && field : interpolationFieldSet) + interpFieldVec.push_back(&field.field()); + stk::mesh::communicate_field_data(mesh, interpFieldVec); + + std::vector scratch; + std::vector snapNodes; + snapNodes.reserve(snapInfos.size()); + + for (auto && snapInfo : snapInfos) + { + if (snapInfo.get_owner() == mesh.parallel_rank()) + { + const size_t intersectionPointIndex = snapInfo.get_intersection_point_index(); + stk::mesh::Entity snapNode = mesh.get_entity(stk::topology::NODE_RANK, snapInfo.get_node_global_id()); + snapNodes.push_back(snapNode); + const IntersectionPoint & intersectionPoint = intersectionPoints[intersectionPointIndex]; + + nodesToCapturedDomains[snapNode] = intersectionPoint.get_sorted_domains(); + + const auto & nodes = intersectionPoint.get_nodes(); + const auto & weights = intersectionPoint.get_weights(); + + if (krinolog.shouldPrint(LOG_DEBUG)) + { + krinolog << "Snapping node " << snapInfo.get_node_global_id() << " to " << debug_output(mesh, intersectionPoint) << stk::diag::dendl; + } + + for(auto && field : interpolationFieldSet) + interpolate_nodal_field(mesh, snapNode, field, nodes, weights, scratch); + } + } + + stk::mesh::communicate_field_data(mesh, interpFieldVec); + + communicate_node_captured_domains_for_given_nodes(mesh, snapNodes, nodesToCapturedDomains); +} + +template +size_t get_global_num_infos(const std::vector & infos, stk::ParallelMachine comm) +{ + size_t numInfos = 0; + int rank{stk::parallel_machine_rank(comm)}; + for (const auto &info : infos) + if (info.get_owner() == rank) + ++numInfos; + const size_t localNumInfos = numInfos; + stk::all_reduce_sum(comm, &localNumInfos, &numInfos, 1); + return numInfos; +} + +void pack_owned_snap_infos_that_other_procs_need_to_know_about(std::vector &snapInfos, stk::CommSparse &commSparse) +{ + stk::pack_and_communicate(commSparse,[&]() + { + for(const auto &snapInfo : snapInfos) + { + if ( snapInfo.get_owner() == commSparse.parallel_rank() ) + { + for ( const int procId : snapInfo.get_procs_that_need_to_know_about_this_info()) + { + if ( procId != commSparse.parallel_rank()) + { + commSparse.send_buffer(procId).pack(snapInfo.get_node_global_id()); + commSparse.send_buffer(procId).pack(snapInfo.get_intersection_point_index()); + stk::pack_vector_to_proc(commSparse, snapInfo.get_procs_that_need_to_know_about_this_info(), procId); + stk::pack_vector_to_proc(commSparse, snapInfo.get_conflicting_ids(), procId); + commSparse.send_buffer(procId).pack(snapInfo.get_post_worst_quality()); + commSparse.send_buffer(procId).pack(snapInfo.get_node_location()); + commSparse.send_buffer(procId).pack(snapInfo.get_snap_location()); + commSparse.send_buffer(procId).pack(snapInfo.get_snap_rank()); + } + } + } + } + }); +} + +void receive_snap_infos_that_this_proc_need_to_know_about_and_ghost(std::vector &snapInfos, stk::CommSparse &commSparse) +{ + stk::unpack_communications(commSparse, [&commSparse, &snapInfos](int procId) + { + size_t globalNodeId{0}; + commSparse.recv_buffer(procId).unpack(globalNodeId); + + size_t intersectionPointIndex{0}; + commSparse.recv_buffer(procId).unpack(intersectionPointIndex); + + std::vector procsThatNeedToKnowAboutThisInfo; + std::vector globalIdsOfSnapNodeElems; + stk::unpack_vector_from_proc(commSparse, procsThatNeedToKnowAboutThisInfo, procId); + stk::unpack_vector_from_proc(commSparse, globalIdsOfSnapNodeElems, procId); + + double postSnapQuality{0}; + commSparse.recv_buffer(procId).unpack(postSnapQuality); + + stk::math::Vector3d nodeLocation; + commSparse.recv_buffer(procId).unpack(nodeLocation); + + stk::math::Vector3d snapLocation; + commSparse.recv_buffer(procId).unpack(snapLocation); + + int snapRank; + commSparse.recv_buffer(procId).unpack(snapRank); + + snapInfos.emplace_back(globalNodeId, + intersectionPointIndex, + nodeLocation, + procId, + procsThatNeedToKnowAboutThisInfo, + globalIdsOfSnapNodeElems, + postSnapQuality, + snapLocation, + snapRank); + }); +} + +void communicate_snap_infos_that_other_procs_need_to_know_about(std::vector &snapInfos, stk::ParallelMachine comm) +{ + stk::CommSparse commSparse(comm); + + pack_owned_snap_infos_that_other_procs_need_to_know_about(snapInfos, commSparse); + receive_snap_infos_that_this_proc_need_to_know_about_and_ghost(snapInfos, commSparse); +} + +std::vector get_sorted_nodes_modified_in_current_snapping_iteration(const stk::mesh::BulkData & mesh, const std::vector & iterationSnapInfos) +{ + std::vector sortedSnappedNodes; + for (auto && snapInfo : iterationSnapInfos) + sortedSnappedNodes.push_back(mesh.get_entity(stk::topology::NODE_RANK, snapInfo.get_node_global_id())); + std::sort(sortedSnappedNodes.begin(), sortedSnappedNodes.end()); + return sortedSnappedNodes; +} + +double determine_quality(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & elementSelector, + const QualityMetric &qualityMetric) +{ + const FieldRef coordsField(mesh.mesh_meta_data().coordinate_field()); + std::vector nodeLocations; + + double quality = qualityMetric.get_best_value_for_metric(); + for (auto && bucket : mesh.get_buckets(stk::topology::ELEMENT_RANK, elementSelector)) + { + if (bucket->topology().base() == stk::topology::TETRAHEDRON_4 || bucket->topology().base() == stk::topology::TRIANGLE_3_2D) + { + for (auto && element : *bucket) + { + fill_element_node_coordinates(mesh, element, coordsField, nodeLocations); + const double elementQuality = qualityMetric.get_element_quality_metric(nodeLocations); + quality = std::min(quality, elementQuality); + } + } + } + + const double localQuality = quality; + stk::all_reduce_min(mesh.parallel(), &localQuality, &quality, 1); + + return quality; +} + +NodeToCapturedDomainsMap snap_as_much_as_possible_while_maintaining_quality(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & elementSelector, + const FieldSet & interpolationFields, + const InterfaceGeometry & geometry, + const bool globalIDsAreParallelConsistent) +{/* %TRACE[ON]% */ Trace trace__("krino::snap_as_much_as_possible_while_maintaining_quality()"); /* %TRACE% */ + + const ScaledJacobianQualityMetric qualityMetric; + size_t iteration{0}; + NodeToCapturedDomainsMap nodesToCapturedDomains; + stk::ParallelMachine comm = mesh.parallel(); + + std::vector intersectionPoints; + geometry.store_phase_for_uncut_elements(mesh); + intersectionPoints = build_all_intersection_points(mesh, geometry, nodesToCapturedDomains); + + while (true) + { + krinolog << "Snapping To Geometry Iteration " << std::to_string(++iteration) << stk::diag::dendl; + + std::vector snapInfos = build_snap_infos_from_intersection_points(mesh, elementSelector, nodesToCapturedDomains, intersectionPoints, qualityMetric, globalIDsAreParallelConsistent); + + bool done = stk::is_true_on_all_procs(comm, snapInfos.empty()); + if ( done ) + break; + + communicate_snap_infos_that_other_procs_need_to_know_about(snapInfos, comm); + + const std::vector independentSnapInfos = find_snap_info_independent_sets(snapInfos, qualityMetric, comm); + + krinolog << " Snapping " << get_global_num_infos(independentSnapInfos, comm) << " of " << get_global_num_infos(snapInfos, comm) << " snap candidates." << stk::diag::dendl; + + geometry.store_phase_for_elements_that_will_be_uncut_after_snapping(mesh, intersectionPoints, independentSnapInfos, nodesToCapturedDomains); + snap_nodes(mesh, interpolationFields, intersectionPoints, independentSnapInfos, nodesToCapturedDomains); + + const std::vector iterationSortedSnapNodes = get_sorted_nodes_modified_in_current_snapping_iteration(mesh, independentSnapInfos); + update_intersection_points_after_snap_iteration(mesh, geometry, iterationSortedSnapNodes, nodesToCapturedDomains, intersectionPoints); + } + + krinolog << "After snapping quality is " << determine_quality(mesh, elementSelector, qualityMetric) << stk::diag::dendl; + + return nodesToCapturedDomains; +} +} + + + diff --git a/packages/krino/krino/krino_lib/Akri_Snap.hpp b/packages/krino/krino/krino_lib/Akri_Snap.hpp new file mode 100644 index 000000000000..f56bc31ecd38 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Snap.hpp @@ -0,0 +1,35 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_SNAP_H_ +#define KRINO_INCLUDE_AKRI_SNAP_H_ +#include +#include +#include +#include + +namespace krino +{ +class InterfaceGeometry; +class QualityMetric; + +NodeToCapturedDomainsMap snap_as_much_as_possible_while_maintaining_quality(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & elementSelector, + const FieldSet & interpolationFields, + const InterfaceGeometry & geometry, + const bool globalIDsAreParallelConsistent); + +double determine_quality(const stk::mesh::BulkData & mesh, + const stk::mesh::Selector & elementSelector, + const QualityMetric &qualityMetric); + +} + + + +#endif /* KRINO_INCLUDE_AKRI_SNAP_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_SnapIndependentSetFinder.hpp b/packages/krino/krino/krino_lib/Akri_SnapIndependentSetFinder.hpp new file mode 100644 index 000000000000..4c2490eadee5 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_SnapIndependentSetFinder.hpp @@ -0,0 +1,34 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef AKRI_SNAPINDEPENDENTSETFINDER_H +#define AKRI_SNAPINDEPENDENTSETFINDER_H + +#include +#include +#include +#include + +namespace krino +{ + +typedef independent_set::IndependentSetFinder SnapInfoIndependentSetFinder; + +inline +std::vector find_snap_info_independent_sets(const std::vector &allSnapInfos, + const QualityMetric & qualityMetric, + stk::ParallelMachine comm) +{ + SnapInfo::Comparator comparator {qualityMetric}; + SnapInfo::ConflictFinder conflictFinder; + return SnapInfoIndependentSetFinder::find_independent_set(allSnapInfos, comparator, conflictFinder, false, comm); +} + +} + +#endif /* AKRI_SNAPINDEPENDENTSETFINDER_H */ diff --git a/packages/krino/krino/krino_lib/Akri_SnapInfo.cpp b/packages/krino/krino/krino_lib/Akri_SnapInfo.cpp new file mode 100644 index 000000000000..96d0abdefd56 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_SnapInfo.cpp @@ -0,0 +1,71 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include + +namespace krino +{ + +template +std::string +to_string(const Container & container) +{ + std::ostringstream os; + os << " {"; + for(auto data : container) + os << " " << data; + os << " }"; + return os.str(); +} + +bool SnapInfo::Comparator::is_first_higher_priority_than_second(const SnapInfo& snapInfoA,const SnapInfo& snapInfoB) const +{ + if( snapInfoA.get_snap_rank() > snapInfoB.get_snap_rank() ) + return true; + else if ( snapInfoA.get_snap_rank() < snapInfoB.get_snap_rank() ) + return false; + + if(mQualityMetric.is_first_quality_metric_better_than_second(snapInfoA.get_post_worst_quality(),snapInfoB.get_post_worst_quality())) + return true; + else if(mQualityMetric.is_first_quality_metric_better_than_second(snapInfoB.get_post_worst_quality(),snapInfoA.get_post_worst_quality())) + return false; + + if (snapInfoA.get_node_global_id() != snapInfoB.get_node_global_id()) + { + if ( is_less_than_in_x_then_y_then_z(snapInfoA.get_node_location(), snapInfoB.get_node_location()) ) + return true; + else if ( is_less_than_in_x_then_y_then_z(snapInfoB.get_node_location(), snapInfoA.get_node_location()) ) + return false; + } + + if ( is_less_than_in_x_then_y_then_z(snapInfoA.get_snap_location(), snapInfoB.get_snap_location()) ) + return true; + else if ( is_less_than_in_x_then_y_then_z(snapInfoB.get_snap_location(), snapInfoA.get_snap_location()) ) + return false; + + return false; +} + +std::ostream & operator<<(std::ostream & os, const SnapInfo& snapInfo) +{ + os << " Owner: " << snapInfo.get_owner() << std::endl; + os << "Snap Node: " << snapInfo.get_node_global_id() << std::endl; + os << "ConflictingIds: " << to_string(snapInfo.get_conflicting_ids()) << std::endl; + os << "SnapLocation: " << snapInfo.get_snap_location().to_string(16) << std::endl; + os << "PostSnapWorstQuality: " << snapInfo.get_post_worst_quality() << std::endl; + os << "GetProcsThatNeedToKnow: " << to_string(snapInfo.get_procs_that_need_to_know_about_this_info()) << std::endl; + os << "Snap Rank: " << snapInfo.get_snap_rank() << std::endl; + return os; +} + +} diff --git a/packages/krino/krino/krino_lib/Akri_SnapInfo.hpp b/packages/krino/krino/krino_lib/Akri_SnapInfo.hpp new file mode 100644 index 000000000000..889defbd38e9 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_SnapInfo.hpp @@ -0,0 +1,87 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef AKRI_SNAPINFO_H +#define AKRI_SNAPINFO_H + +#include +#include +#include + +namespace krino +{ + +class SnapInfo +{ +public: + typedef size_t ExclusionIdentifierType; + typedef std::array GlobalId; + + SnapInfo(const size_t nodeGlobalId, + const size_t intersectionPointIndex, + const stk::math::Vector3d & nodeLocation, + const int owner, + const std::vector & procsThatNeedToKnowAboutThisInfo, + const std::vector & globalIdsOfSnapNodeElems, + const double& postSnapQuality, + const stk::math::Vector3d & snapLocation, + const int snapRank) + : mUniqueId(make_unique_id(nodeGlobalId, intersectionPointIndex)), + mOwner{owner}, + mProcsThatNeedToKnowAboutThisInfo{procsThatNeedToKnowAboutThisInfo}, + mGlobalIdsOfSnapNodeElems{globalIdsOfSnapNodeElems}, + mPostWorstQuality(postSnapQuality), // () for intel 17 + mNodeLocation(nodeLocation), + mSnapLocation(snapLocation), + mSnapRank{snapRank} + { + } + + static GlobalId make_unique_id(const size_t globalNodeId, const size_t intersectionPointIndex) { return GlobalId{globalNodeId, intersectionPointIndex}; } + const GlobalId &get_unique_id() const { return mUniqueId; } + size_t get_node_global_id() const { return mUniqueId[0]; } + size_t get_intersection_point_index() const { return mUniqueId[1]; } + int get_owner() const { return mOwner; } + const std::vector &get_procs_that_need_to_know_about_this_info() const { return mProcsThatNeedToKnowAboutThisInfo; } + double get_post_worst_quality() const { return mPostWorstQuality; } + const stk::math::Vector3d & get_snap_location() const { return mSnapLocation; }; + const stk::math::Vector3d & get_node_location() const { return mNodeLocation; }; + int get_snap_rank() const { return mSnapRank; } + const std::vector &get_conflicting_ids() const { return mGlobalIdsOfSnapNodeElems; } + + class ConflictFinder + { + public: + std::vector get_other_conflicting_infos(const SnapInfo& info) const {return {};} + }; + class Comparator + { + public: + Comparator(const QualityMetric &qualityMetric) : mQualityMetric{qualityMetric}{} + bool is_first_higher_priority_than_second(const SnapInfo& tetSnapInfoA,const SnapInfo& tetSnapInfoB) const; + + private: + const QualityMetric &mQualityMetric; + }; + +private: + const GlobalId mUniqueId; + const int mOwner{0u}; + const std::vector mProcsThatNeedToKnowAboutThisInfo; + const std::vector mGlobalIdsOfSnapNodeElems; + const double mPostWorstQuality{0.0}; + const stk::math::Vector3d mNodeLocation{}; + const stk::math::Vector3d mSnapLocation{}; + const int mSnapRank; +}; + +std::ostream & operator<<(std::ostream & os, const SnapInfo& snapInfo); + +} + +#endif /* AKRI_SNAPINFO_H */ diff --git a/packages/krino/krino/krino_lib/Akri_SnapToNode.cpp b/packages/krino/krino/krino_lib/Akri_SnapToNode.cpp new file mode 100644 index 000000000000..84b437bdd092 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_SnapToNode.cpp @@ -0,0 +1,59 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include "../interface_geometry_interface/Akri_InterfaceGeometry.hpp" + +namespace krino { + +void determine_node_snapping_from_intersection_points(const stk::mesh::BulkData & mesh, + const std::vector & intersectionPoints, + const CDFEM_Snapper & snapper, + NodeToCapturedDomainsMap & nodesToCapturedDomains) +{ + const double snapTol = snapper.get_edge_tolerance(); + for (auto && intersectionPoint : intersectionPoints) + { + const auto & nodes = intersectionPoint.get_nodes(); + const auto & weights = intersectionPoint.get_weights(); + for (size_t nodeIndex=0; nodeIndex 1.-snapTol) + { + if (krinolog.shouldPrint(LOG_DEBUG)) + { + krinolog << "Snapping node " << debug_output(mesh, intersectionPoint) << " to " << mesh.identifier(nodes[nodeIndex]) << stk::diag::dendl; + } + + const auto & intersectionPointDomains = intersectionPoint.get_sorted_domains(); + auto & nodeCapturedDomains = nodesToCapturedDomains[nodes[nodeIndex]]; + nodeCapturedDomains.insert(nodeCapturedDomains.end(), intersectionPointDomains.begin(), intersectionPointDomains.end()); + } + } + } + + for (auto && nodeToCapturedDomains : nodesToCapturedDomains) + stk::util::sort_and_unique(nodeToCapturedDomains.second); +} + +void snap_to_node(const stk::mesh::BulkData & mesh, + const InterfaceGeometry & interfaceGeometry, + const CDFEM_Snapper & snapper, + NodeToCapturedDomainsMap & nodesToCapturedDomains) +{ + const std::vector intersectionPoints = build_uncaptured_intersection_points(mesh, interfaceGeometry, nodesToCapturedDomains); + + determine_node_snapping_from_intersection_points(mesh, intersectionPoints, snapper, nodesToCapturedDomains); + communicate_node_captured_domains_for_all_nodes(mesh, nodesToCapturedDomains); +} + +} + + diff --git a/packages/krino/krino/krino_lib/Akri_SnapToNode.hpp b/packages/krino/krino/krino_lib/Akri_SnapToNode.hpp new file mode 100644 index 000000000000..26fe17920793 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_SnapToNode.hpp @@ -0,0 +1,26 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_INCLUDE_AKRI_SNAPTONODE_H_ +#define KRINO_INCLUDE_AKRI_SNAPTONODE_H_ + +#include +#include + +namespace krino { + +class CDFEM_Support; +class Phase_Support; + +void snap_to_node(const stk::mesh::BulkData & mesh, + const InterfaceGeometry & interfaceGeometry, + const CDFEM_Snapper & snapper, + NodeToCapturedDomainsMap & nodesToCapturedDomains); + +} +#endif /* KRINO_INCLUDE_AKRI_SNAPTONODE_H_ */ diff --git a/packages/krino/krino/krino_lib/Akri_SubElement.cpp b/packages/krino/krino/krino_lib/Akri_SubElement.cpp new file mode 100644 index 000000000000..6a531e1abab2 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_SubElement.cpp @@ -0,0 +1,2833 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace krino{ + +bool +SubElementNode::on_common_edge(const SubElementNode * other) const +{ + NodeSet ancestors; + get_ancestors(ancestors); + if (ancestors.size() <= 2) + { + other->get_ancestors(ancestors); + } + if (ancestors.size() <= 2) + { + return true; + } + return false; +} + +SubElementMidSideNode::SubElementMidSideNode( const Mesh_Element * owner, + const SubElementNode *parent1, + const SubElementNode *parent2) + : SubElementNode(owner), + my_is_mesh_node(false), + my_parent1(parent1), + my_parent2(parent2) +{ + // fill base class data + my_cached_owner_coords = compute_owner_coords( owner ); + my_global_coords = 0.5*(my_parent1->coordinates()) + 0.5*(my_parent2->coordinates()); +} + +SubElementMidSideNode::SubElementMidSideNode( const Mesh_Element * owner, + const SubElementNode *parent1, + const SubElementNode *parent2, + stk::mesh::Entity meshNode, + stk::mesh::EntityId meshNodeId) + : SubElementMidSideNode(owner, parent1, parent2) +{ + my_is_mesh_node = true; + set_entity(meshNode, meshNodeId); +} + +SubElementChildNode::SubElementChildNode( const Mesh_Element * in_owner, + const NodeVec & parents, + const std::vector & weights ) + : SubElementNode(in_owner), + my_parents(parents), + my_weights(weights) +{ + // fill base class data + my_cached_owner_coords = compute_owner_coords( in_owner ); + my_global_coords = in_owner->coordinates( my_cached_owner_coords ); +} + +SubElementMeshNode::SubElementMeshNode( const Mesh_Element * in_owner, + stk::mesh::Entity nodeEntity, + stk::mesh::EntityId nodeEntityId, + const Vector3d & in_owner_coords, + const Vector3d & in_global_coords ) + : SubElementNode(in_owner) +{ + // fill base class data + set_entity(nodeEntity, nodeEntityId); + my_cached_owner_coords = in_owner_coords; + my_global_coords = in_global_coords; +} + +void +SubElementNode::get_parent_entities(std::vector & parent_entities) const +{ + NodeVec parents = get_parents(); + + const unsigned parent_size = parents.size(); + parent_entities.resize(parent_size); + + for (unsigned i=0; ientity(); + } +} + +static bool is_on_multiple_blocks(const stk::mesh::BulkData& mesh, stk::mesh::Entity node) +{ + bool foundVolumePart = false; + for (auto && part : mesh.bucket(node).supersets()) + { + if (part->primary_entity_rank() == stk::topology::ELEMENT_RANK && + !stk::mesh::is_auto_declared_part(*part) && + part->subsets().empty() && + part->name().compare(0,7,"refine_") != 0) + { + if (foundVolumePart) return true; + foundVolumePart = true; + } + } + return false; +} + +bool SubElementChildNode::needs_to_be_ale_prolonged(const CDMesh & mesh) const +{ + if (mesh.get_prolongation_model() == INTERPOLATION) + return false; + + const CDMesh* old_mesh = mesh.get_old_mesh(); + const bool is_initial_mesh = old_mesh->stash_step_count() < 0; + if (is_initial_mesh) + return false; + + if (is_on_multiple_blocks(mesh.stk_bulk(), entity())) + return true; + + // relatively unusual case of an edge node that is not on a block-block boundary. + // this is currently handled by using interpolation. This possibly needs further + // testing/development to treat these like mesh nodes where we see if they have + // changed phase. + return false; +} + +Vector3d SubElementChildNode::compute_owner_coords( const Mesh_Element * in_owner ) const +{ + Vector3d calcOwnerCoords{Vector3d::ZERO}; + ThrowAssert(my_parents.size() == my_weights.size()); + for (size_t i=0; iowner_coords(in_owner); + return calcOwnerCoords; +} + +void +SubElementChildNode::prolongate_fields(const CDMesh & mesh) const +{/* %TRACE[ON]% */ Trace trace__("SubElementEdgeNode::prolongate_fields() const"); /* %TRACE% */ + for (auto && parent : my_parents) + if (!parent->is_prolonged()) + parent->prolongate_fields(mesh); + + if (my_is_prolonged_flag) return; + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "SubElementEdgeNode::prolongate_fields for node#" << entityId() << "\n"; + my_is_prolonged_flag = true; + + const ProlongationPointData * prolong_node = needs_to_be_ale_prolonged(mesh) ? mesh.get_old_mesh()->find_prolongation_node(*this) : nullptr; + + prolong_cdfem_displacements(mesh, prolong_node); + + prolong_zeroed_fields(mesh, nullptr); + + prolong_ale_fields(mesh, prolong_node); + + prolong_interpolation_fields(mesh); +} + +bool SubElementMidSideNode::is_mesh_node_that_needs_to_be_prolonged(const CDMesh & mesh) const +{/* %TRACE[ON]% */ Trace trace__("SubElementMidSideNode::is_mesh_node_that_needs_to_be_prolonged() const"); /* %TRACE% */ + + ThrowRequire(my_is_mesh_node); + + const SubElementMeshNode * parent1 = dynamic_cast(my_parent1); + const SubElementMeshNode * parent2 = dynamic_cast(my_parent2); + const int num_ale_prolonged_parents = (parent1->needs_to_be_ale_prolonged(mesh) ? 1 : 0) + (parent2->needs_to_be_ale_prolonged(mesh) ? 1 : 0); + + if (num_ale_prolonged_parents == 0) return false; + if (num_ale_prolonged_parents == 2) return true; + + // 1 parent node needed to be ALE prolonged and this node is active (so the edge is not cut). + // This means the interface was cutting this edge, but now is not -> prolong OR + // the interface is passing through one of the parents of this uncut edge -> do not prolong. + + const bool have_or_did_have_interface = my_cached_owner->have_interface() || mesh.get_old_mesh()->find_mesh_element(my_cached_owner->entityId())->have_interface(); + + return have_or_did_have_interface && nullptr == mesh.get_old_mesh()->fetch_prolong_node(entityId()); +} + +void +SubElementMidSideNode::prolongate_fields(const CDMesh & mesh) const +{/* %TRACE[ON]% */ Trace trace__("SubElementMidSideNode::prolongate_fields() const"); /* %TRACE% */ + if (!my_parent1->is_prolonged()) + { + my_parent1->prolongate_fields(mesh); + } + if (!my_parent2->is_prolonged()) + { + my_parent2->prolongate_fields(mesh); + } + if (my_is_prolonged_flag) return; + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "SubElementMidSideNode::prolongate_fields for node#" << entityId() << "\n"; + my_is_prolonged_flag = true; + + const bool needs_to_be_prolonged = !my_is_mesh_node || is_mesh_node_that_needs_to_be_prolonged(mesh); + if (needs_to_be_prolonged) + { + // Note: CDFEM displacement is not present on midside nodes + prolong_zeroed_fields(mesh, nullptr); + + prolong_interpolation_fields(mesh); + + prolong_ale_fields(mesh); + } +} + +void +SubElementMidSideNode::prolong_interpolation_fields(const CDMesh & mesh) const +{ + const ElementObj * interp_elem = nullptr; + Vector3d interp_elem_p_coords; + const CDMesh* old_mesh = mesh.get_old_mesh(); + const Mesh_Element * old_owner = old_mesh->find_mesh_element(my_cached_owner->entityId()); + ThrowAssert(old_owner); + old_owner->find_child_coordinates_at_owner_coordinates(my_cached_owner_coords, interp_elem, interp_elem_p_coords); + + for(auto && field : mesh.get_interpolation_fields()) + { + const unsigned field_length = field.length(); + + double * val = field_data(field, my_entity); + if (NULL == val) continue; + + interp_elem->evaluate_prolongation_field(*old_mesh, field, field_length, interp_elem_p_coords, val); + } +} + +void +SubElementMidSideNode::prolong_ale_fields(const CDMesh & mesh) const +{ + // simply average parent nodes + for(auto && field : mesh.get_ale_prolongation_fields()) + { + const unsigned field_length = field.length(); + + double * val = field_data(field, my_entity); + if (nullptr != val) + { + double * val1 = field_data(field, my_parent1->entity()); + double * val2 = field_data(field, my_parent2->entity()); + ThrowRequire(val1 && val2); + for (unsigned i=0; iis_prolonged()) + { + parent->prolongate_fields(mesh); + } + } + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "SubElementInternalNode::prolongate_fields for node#" << entityId() << "\n"; + my_is_prolonged_flag = true; + + const stk::mesh::BulkData& stk_mesh = mesh.stk_bulk(); + const stk::mesh::FieldVector & all_fields = stk_mesh.mesh_meta_data().get_fields(); + for ( stk::mesh::FieldVector::const_iterator it = all_fields.begin(); it != all_fields.end() ; ++it ) + { + const FieldRef field = (const FieldRef)(**it); + + // Do not try to prolong non-real variables + if( field.entity_rank()!=stk::topology::NODE_RANK || !field.type_is() ) continue; + + const unsigned field_length = field.length(); + + double * val = field_data(field, my_entity); + if (NULL == val) continue; + + for (unsigned i=0; i(field, get_parents()[p]->entity()); + if (NULL == parent_val) + { + parent_error = true; + } + else + { + tot_wt += get_parent_weights()[p]; + for (unsigned i=0; ientity()) << stk::diag::dendl; + } + } + } +} + +bool on_interface_or_io_parts_have_changed(const stk::mesh::BulkData & mesh, const Phase_Support & phaseSupport, stk::mesh::Entity node, const ProlongationNodeData & oldProlongNode) +{ + const auto newParts = ProlongationNodeData::get_node_io_parts(mesh, node); + if (newParts != oldProlongNode.get_io_parts()) + return true; + for (auto && partOrdinal : newParts) + if (phaseSupport.is_interface(&mesh.mesh_meta_data().get_part(partOrdinal))) + return true; + return false; +} + +bool SubElementMeshNode::needs_to_be_ale_prolonged(const CDMesh & mesh) const +{/* %TRACE[ON]% */ Trace trace__("SubElementMeshNode::needs_to_be_ale_prolonged() const"); /* %TRACE% */ + const ProlongationNodeData * old_prolong_node = NULL; + const CDMesh* old_mesh = mesh.get_old_mesh(); + old_prolong_node = old_mesh->fetch_prolong_node(entityId()); + const bool is_initial_mesh = old_mesh->stash_step_count() < 0; + return !is_initial_mesh && nullptr != old_prolong_node && on_interface_or_io_parts_have_changed(mesh.stk_bulk(), mesh.get_phase_support(), entity(), *old_prolong_node); +} + +void +SubElementMeshNode::prolongate_fields(const CDMesh & mesh) const +{/* %TRACE[ON]% */ Trace trace__("SubElementMeshNode::prolongate_fields() const"); /* %TRACE% */ + + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "SubElementMeshNode::prolongate_fields for node#" << entityId() << "\n"; + my_is_prolonged_flag = true; + + const ProlongationPointData * prolong_data = NULL; + const ProlongationNodeData * old_prolong_node = NULL; + const CDMesh* old_mesh = mesh.get_old_mesh(); + old_prolong_node = old_mesh->fetch_prolong_node(entityId()); + + const bool needsToBeALEProlonged = needs_to_be_ale_prolonged(mesh); + if (mesh.get_prolongation_model() != INTERPOLATION && needsToBeALEProlonged) + { + prolong_data = old_mesh->find_prolongation_node(*this); + } + + if( !old_prolong_node && !prolong_data ) + { + return; + } + + prolong_cdfem_displacements(mesh, prolong_data, false); + + const ProlongationNodeData * nodeToExamineForPreExistingField = needsToBeALEProlonged ? nullptr : old_prolong_node; + prolong_zeroed_fields(mesh, nodeToExamineForPreExistingField); + + prolong_ale_fields(mesh, prolong_data, old_prolong_node); + + prolong_interpolation_fields(mesh, old_prolong_node); +} + +void +SubElementNode::prolong_zeroed_fields(const CDMesh & mesh, const ProlongationNodeData * nodeToExamineForPreExistingField) const +{ + const FieldSet & zeroed_fields = mesh.get_zeroed_fields(); + for(auto&& field : zeroed_fields) + { + if (!nodeToExamineForPreExistingField || !nodeToExamineForPreExistingField->get_field_data(field)) // If this node existed before and had this field, leave it alone + { + double * val = field_data(field, my_entity); + if (nullptr != val) std::fill(val, val+field.length(), 0.); + } + } +} + +void SubElementNode::prolong_cdfem_displacements(const CDMesh & mesh, + const ProlongationPointData * prolong_data, + const bool zero_if_no_prolong_data) const +{ + const FieldRef field = mesh.get_cdfem_displacements_field(); + if( !field.valid()) return; + + const unsigned field_length = field.length(); + for ( unsigned is = 0; is < field.number_of_states(); ++is ) + { + const stk::mesh::FieldState state = static_cast(is); + const FieldRef state_field = field.field_state(state); + + double * val = field_data(state_field, my_entity); + if(val == NULL) continue; + + if(!prolong_data) + { + if (zero_if_no_prolong_data) + { + std::fill(val, val+field_length, 0); + } + } + else + { + const double * prolong_field = prolong_data->get_field_data(state_field); + ThrowRequire(NULL != prolong_field); + std::copy(prolong_field, prolong_field+field_length, val); + + if (state == stk::mesh::StateNew) + { + const Vector3d & coords = coordinates(); + const Vector3d & old_coords = prolong_data->get_coordinates(); + for (unsigned i=0; i 0); + + for (auto && child : parents[0]->my_children) + { + if (child->get_num_parents() == numParents) + { + bool childOfAllParents = true; + for (size_t iParent=1; iParenthave_child(child)) + { + childOfAllParents = false; + break; + } + } + if (childOfAllParents) + return child; + } + } + return nullptr; +} + +bool SubElementNode::have_child() const +{ + return !my_children.empty(); +} + +bool SubElementNode::have_child(const SubElementNode* child) const +{ + return (std::find(my_children.begin(), my_children.end(), child) != my_children.end()); +} + +void +SubElementChildNode::prolong_ale_fields(const CDMesh & mesh, const ProlongationPointData * prolong_data) const +{ + const CDMesh* old_mesh = mesh.get_old_mesh(); + const ElementObj * interp_elem = nullptr; + Vector3d interp_elem_p_coords; + const FieldSet & ale_prolongation_fields = mesh.get_ale_prolongation_fields(); + for(FieldSet::const_iterator it = ale_prolongation_fields.begin(); it != ale_prolongation_fields.end(); ++it) + { + const FieldRef field = *it; + const unsigned field_length = field.length(); + + double * val = field_data(field, my_entity); + if (NULL == val) continue; + + if(prolong_data) + { + // this node has changed phase + // prolong based on prolong_node + const double * prolong_field = prolong_data->get_field_data(field); + // We cannot yet handle the case where a prolongation field is not defined on the prolongation node that + // was found. Throw here to avoid the possibility of not prolonging a prolongation field that then + // has its time derivative screwed up because it has a mesh velocity associated with it. + // We think this should only occur in problems with multiple different level sets. + ThrowRequire(prolong_field); + std::copy(prolong_field, prolong_field+field_length, val); + } + else + { + if (nullptr == interp_elem) + { + const Mesh_Element * old_owner = old_mesh->find_mesh_element(my_cached_owner->entityId()); + ThrowAssert(old_owner); + old_owner->find_child_coordinates_at_owner_coordinates(my_cached_owner_coords, interp_elem, interp_elem_p_coords); + } + + interp_elem->evaluate_prolongation_field(*old_mesh, field, field_length, interp_elem_p_coords, val); + } + } +} + +void +SubElementMeshNode::prolong_ale_fields(const CDMesh & mesh, + const ProlongationPointData * prolong_data, + const ProlongationNodeData * old_node) const +{ + const FieldSet & ale_prolongation_fields = mesh.get_ale_prolongation_fields(); + for(FieldSet::const_iterator it = ale_prolongation_fields.begin(); it != ale_prolongation_fields.end(); ++it) + { + const FieldRef field = *it; + const unsigned field_length = field.length(); + + double * val = field_data(field, my_entity); + if (NULL == val) continue; + + if(prolong_data) + { + // this node has changed phase + // prolong based on prolong_node + const double * prolong_field = prolong_data->get_field_data(field); + // We cannot yet handle the case where a prolongation field is not defined on the prolongation node that + // was found. Throw here to avoid the possibility of not prolonging a prolongation field that then + // has its time derivative screwed up because it has a mesh velocity associated with it. + // We think this should only occur in problems with multiple different level sets. + ThrowRequire(prolong_field); + std::copy(prolong_field, prolong_field+field_length, val); + } + else if(old_node) + { + const double * old_field = old_node->get_field_data(field); + if(!old_field) + { + const FieldRef initial_field = mesh.get_cdfem_support().get_initial_prolongation_field( field ); + if(initial_field.valid()) + { + const double * initial_data = old_node->get_field_data(initial_field); + std::copy(initial_data, initial_data+field_length, val); + } + else + { + std::fill(val, val+field_length, 0.); + } + } + } + } +} + +void +SubElementChildNode::prolong_interpolation_fields(const CDMesh & mesh) const +{ + const ElementObj * interp_elem = nullptr; + Vector3d interp_elem_p_coords; + const CDMesh* old_mesh = mesh.get_old_mesh(); + const Mesh_Element * old_owner = old_mesh->find_mesh_element(my_cached_owner->entityId()); + ThrowAssert(old_owner); + old_owner->find_child_coordinates_at_owner_coordinates(my_cached_owner_coords, interp_elem, interp_elem_p_coords); + + const FieldSet & interpolation_fields = mesh.get_interpolation_fields(); + for(FieldSet::const_iterator it = interpolation_fields.begin(); it != interpolation_fields.end(); ++it) + { + const FieldRef field = *it; + const unsigned field_length = field.length(); + + double * val = field_data(field, my_entity); + if (NULL == val) continue; + + interp_elem->evaluate_prolongation_field(*old_mesh, field, field_length, interp_elem_p_coords, val); + } +} + +void +SubElementMeshNode::prolong_interpolation_fields(const CDMesh & mesh, const ProlongationNodeData * old_node) const +{ + const FieldSet & interpolation_fields = mesh.get_interpolation_fields(); + for(FieldSet::const_iterator it = interpolation_fields.begin(); it != interpolation_fields.end(); ++it) + { + const FieldRef field = *it; + const unsigned field_length = field.length(); + + double * val = field_data(field, my_entity); + if (NULL == val) continue; + + if(old_node) + { + const double * old_field = old_node->get_field_data(field); + if(!old_field) + { + const FieldRef initial_field = mesh.get_cdfem_support().get_initial_prolongation_field( field ); + if(initial_field.valid()) + { + const double * initial_data = old_node->get_field_data(initial_field); + std::copy(initial_data, initial_data+field_length, val); + } + else + { + std::fill(val, val+field_length, 0.); + } + } + } + } +} + +void removeParts(stk::mesh::PartVector & parts, const stk::mesh::PartVector & parts_to_remove) +{ + stk::mesh::PartVector::iterator parts_begin = parts.begin(); + stk::mesh::PartVector::iterator begin_parts_to_erase = parts.end(); + for(stk::mesh::PartVector::const_iterator it = parts_to_remove.begin(); it != parts_to_remove.end(); ++it) + { + begin_parts_to_erase = std::remove(parts_begin, begin_parts_to_erase, *it); + } + parts.erase(begin_parts_to_erase, parts.end()); +} + +std::vector SubElementNode::prolongation_node_fields(const CDMesh & mesh) const +{ + const FieldSet & ale_prolongation_fields = mesh.get_ale_prolongation_fields(); + + std::vector ale_prolongation_fields_on_node; + ale_prolongation_fields_on_node.reserve(ale_prolongation_fields.size()); + for(auto && field : ale_prolongation_fields) + if (nullptr != field_data(field, my_entity)) + ale_prolongation_fields_on_node.push_back(field.field().mesh_meta_data_ordinal()); + + std::sort(ale_prolongation_fields_on_node.begin(), ale_prolongation_fields_on_node.end()); + return ale_prolongation_fields_on_node; +} + +static bool float_less(double a, double b) +{ + return static_cast(a) < static_cast(b); +} + +bool SubElementNode::higher_priority_by_score_then_ancestry(const SubElementNode & a, const SubElementNode & b, const bool globalIDsAreParallelConsistent) +{ + // higher score wins + if (float_less(b.get_node_score(), a.get_node_score())) return true; + if (float_less(a.get_node_score(), b.get_node_score())) return false; + + if (globalIDsAreParallelConsistent) + return SubElementNodeAncestry::compare(a.get_ancestry(), b.get_ancestry(), SubElementNode::less_by_entity_id); + return SubElementNodeAncestry::compare(a.get_ancestry(), b.get_ancestry(), SubElementNode::less_by_coordinates_then_by_entity_id); +} + +bool SubElementNode::less_by_entity_id(const SubElementNode & a, const SubElementNode & b) +{ + // lower id wins (this can be an issue because it is sensitive to the global ids provided by percept, which are dependent on the number of procs) + return a.entityId() < b.entityId(); +} + +bool SubElementNode::less_by_coordinates_then_by_entity_id(const SubElementNode & a, const SubElementNode & b) +{ + const Vector3d & aCoord = a.coordinates(); + const Vector3d & bCoord = b.coordinates(); + if (float_less(aCoord[0], bCoord[0])) return true; + if (float_less(bCoord[0], aCoord[0])) return false; + if (float_less(aCoord[1], bCoord[1])) return true; + if (float_less(bCoord[1], aCoord[1])) return false; + if (float_less(aCoord[2], bCoord[2])) return true; + if (float_less(bCoord[2], aCoord[2])) return false; + + // as a last resort, lower id wins (this can be an issue because it is sensitive to the global ids provided by percept, which are dependent on the number of procs) + return a.entityId() < b.entityId(); +} + +bool SubElementNode::captures_intersection_point_domains(const std::vector & intersectionPointDomains) const +{ + return first_sorted_vector_of_domains_contains_all_domains_in_second_vector(my_sorted_node_domains, intersectionPointDomains); +} + +bool SubElementNode::captures_interface(const InterfaceID & interface) const +{ + if (interface.first_ls() == interface.second_ls()) + return first_sorted_vector_of_domains_contains_all_domains_in_second_vector(my_sorted_node_domains, {interface.first_ls()}); + return first_sorted_vector_of_domains_contains_all_domains_in_second_vector(my_sorted_node_domains, {interface.first_ls(),interface.second_ls()}); +} + +void SubElementNode::insert_node_domains(const std::vector & domainsToAdd) const +{ + my_sorted_node_domains.insert(my_sorted_node_domains.end(), domainsToAdd.begin(), domainsToAdd.end()); + stk::util::sort_and_unique(my_sorted_node_domains); +} + + +const SubElementNode * +SubElementNode::find_node_with_common_ancestry(const CDMesh & search_mesh) const +{ + // This only works with a lineage of edge nodes (not internal nodes). + if (is_mesh_node()) + { + return search_mesh.get_mesh_node(entityId()); + } + + const NodeVec parents = get_parents(); + const unsigned num_parents = parents.size(); + + if (num_parents == 2) + { + const SubElementNode * search_parent1 = search_mesh.find_node_with_common_ancestry(parents[0]); + const SubElementNode * search_parent2 = search_mesh.find_node_with_common_ancestry(parents[1]); + if (nullptr != search_parent1 && nullptr != search_parent2) + { + return SubElementNode::common_child({search_parent1, search_parent2}); + } + } + return nullptr; +} + +void +SubElementNode::get_ancestors(NodeSet & ancestors) const +{ + if (is_mesh_node()) + { + ancestors.insert(this); + return; + } + const NodeVec parents = get_parents(); + for(auto&& parent : parents) + { + parent->get_ancestors(ancestors); + } +} + +SubElementNodeAncestry +SubElementNode::get_ancestry() const +{ + return SubElementNodeAncestry(this); +} + +void +SubElementNode::build_stencil(std::map & stencil, const double self_weight) const +{ + if (is_mesh_node()) + { + stencil[this] += self_weight; + return; + } + const NodeVec parents = get_parents(); + const std::vector parent_weights = get_parent_weights(); + for(unsigned i=0; ibuild_stencil(stencil, self_weight * parent_weights[i]); + } +} + +void +SubElementNode::build_constraint_stencil(const FieldRef field, std::vector & entities, std::vector & weights) const +{ + ThrowRequire(!is_mesh_node()); + static const double wt_min = 1.e-9; + typedef std::tuple EntityAndWeight; + std::vector entitiesAndWeights; + + const MasterElement* master_elem = my_cached_owner->get_evaluation_master_element(field); + + const unsigned npe = master_elem->get_topology().num_nodes(); + std::vector shapefcn (npe,0.); + master_elem->shape_fcn(1, my_cached_owner_coords.data(), shapefcn.data()); + + const auto & nodes = my_cached_owner->get_nodes(); + for (unsigned n=0; n wt_min) + { + entitiesAndWeights.push_back(std::make_tuple(nodes[n]->entityId(), nodes[n]->entity(), shapefcn[n])); + } + } + + std::sort(entitiesAndWeights.begin(), entitiesAndWeights.end(), [](const EntityAndWeight & a, const EntityAndWeight & b) { return std::get<0>(a) > std::get<0>(b); }); + + entities.clear(); + weights.clear(); + + entities.push_back(entity()); + weights.push_back(-1.0); + + for (auto && entityAndWeight : entitiesAndWeights) + { + entities.push_back(std::get<1>(entityAndWeight)); + weights.push_back(std::get<2>(entityAndWeight)); + } +} + +// Notes on coordinate systems. +// (1) real coordinates +// This can be obtained using Element::coordinates( Vector3d of owner coordinates ). +// (2) owner coordinates +// This is the parametric coordinates of the finite element. +// This can be obtained using SubElement::owner_coordinates( Vector3d of subelement coordinates ). +// This is the type of coordinates stored in SubElement::my_coords, no matter what level of subelement. +// In places where both owner coordinates and subelement coordinates are used, this is kept in the var owner_coords. +// (3) subelement coordinates +// This is the parametric coordinates within the subelement. +// In places where both owner coordinates and subelement coordinates are used, this is kept in the var local_coords. + +SubElement::SubElement( const stk::topology topo, + const NodeVec & nodes, + const std::vector & side_ids, + const Mesh_Element * owner) + : ElementObj( topo, nodes), + my_parent_side_ids( side_ids ), + my_owner( owner ) +{ /* %TRACE% */ /* %TRACE% */ + ThrowAssert( nodes.size() == topology().num_nodes() ); + ThrowAssert( my_parent_side_ids.size() == topology().num_sides() ); + + set_permutation(); +} + +void +SubElement::set_permutation() +{ + // For true subelements (not just coincident with the owning mesh element), permute the element + // nodes and sides in a consistent way. This will produce consistent node and side ordering + // from decomposition to decomposition. In this way, repeated decompositions will produce identical results. + + bool coincident_with_owner = true; + for (size_t n=0; nget_nodes()[n]) + { + coincident_with_owner = false; + break; + } + } + if (coincident_with_owner) return; + + std::vector node_ancestries; + node_ancestries.reserve(my_nodes.size()); + for (auto&& node : my_nodes) + { + node_ancestries.emplace_back(node); + } + const unsigned permutation_index = topology().lexicographical_smallest_permutation(node_ancestries.data(), true); // only consider positive permutations (true means this) + + if (permutation_index == stk::mesh::DEFAULT_PERMUTATION) return; + + // permute nodes + NodeVec permuted_nodes(my_nodes.size()); + topology().permutation_nodes(my_nodes.data(), permutation_index, permuted_nodes.data()); + my_nodes = permuted_nodes; + + // permute sides + std::vector side_permutation = get_side_permutation(topology(), static_cast(permutation_index)); + + std::vector permuted_parent_side_ids(my_parent_side_ids.size()); + for (unsigned iside=0; isideentity()) return false; + } + return true; +} + +void +SubElement::get_owner_coord_transform(double * dOwnerdSub) const +{ +// const NodeVec & owner_nodes = my_owner->get_nodes(); +// for ( int n = 0; n < my_num_nodes; n++ ) +// { +// krinolog << "node " << n << ", owner node phys_coords = " << owner_nodes[n]->coordinates()[0] << "," << owner_nodes[n]->coordinates()[1] << stk::diag::dendl; +// } + + std::vector owner_coords; + fill_node_owner_coords(my_owner, owner_coords); + + // Hard coded for linear simplex elements with constant transformations + const unsigned nnodes = num_nodes(); + ThrowAssert(my_master_elem.num_intg_pts() == nnodes); + const double * d_shape = my_master_elem.shape_fcn_deriv(); + + const int dim = spatial_dim(); + for ( int i = 0; i < dim; i++ ) + { + for ( int j = 0; j < dim; j++ ) + { + double & dOwnerdSub_ij = dOwnerdSub[i*dim + j]; + + dOwnerdSub_ij = 0.0; + for ( unsigned n = 0; n < nnodes; n++ ) + { + dOwnerdSub_ij += owner_coords[n][i] * d_shape[n*dim + j]; + } + + //krinolog << "dOwnerdSub[" << i << "][" << j << "] = " << dOwnerdSub_ij << stk::diag::dendl; + } + } +} + +void +SubElement::determine_decomposed_elem_phase(const CDMesh & mesh) +{ + if(have_subelements()) + { + for(auto && subelem : my_subelements) + { + subelem->determine_decomposed_elem_phase(mesh); + } + // Phase for SubElement with subelements is left empty + return; + } + + if(!my_owner->have_interface()) + { + set_phase(my_owner->get_phase()); + } + else + { + const PhaseTag startPhase = my_phase.empty() ? my_owner->get_phase() : my_phase; + my_phase = update_phase(mesh, startPhase, my_owner->get_sorted_cutting_interfaces(), myInterfaceSigns); + } + + if(krinolog.shouldPrint(LOG_DEBUG)) + { + krinolog << "SubElement with nodes "; + for (auto && node : my_nodes) + krinolog << node->get_ancestry() << " "; + krinolog << "has phase " << my_phase << "\n"; + } +} + +std::vector SubElement::subelement_interface_signs(const InterfaceID interface, const int sign) const +{ + if (have_interface(interface) && sign != 0) + { + std::vector interfaceSigns = myInterfaceSigns; + interfaceSigns[my_owner->get_interface_index(interface)] = sign; + return interfaceSigns; + } + return myInterfaceSigns; +} + +void SubElement::initialize_interface_signs() +{ + myInterfaceSigns.assign(my_owner->get_num_interfaces(), 0); +} + +void SubElement::set_interface_signs(const std::vector & interfaceSigns) +{ + myInterfaceSigns = interfaceSigns; +} + +void SubElement::update_interface_signs(const InterfaceID interface, const int sign) +{ + set_interface_signs(subelement_interface_signs(interface, sign)); +} + +double +SubElement::relative_volume() const +{ /* %TRACE% */ /* %TRACE% */ + // This is a relative volume compared to the owner volume. + // Actually this is a relative volume if the "parametric" volume of the element is unity. + // Otherwise, it is off by a factor. + const int nelem = 1; + const int dim = spatial_dim(); + const int nint = my_master_elem.num_intg_pts(); + std::vector coords(my_nodes.size() * dim, 0.); + std::vector det_J(nint, 0.); + double error = 0.; + + // integration weights + const double * intg_weights = my_master_elem.intg_weights(); + + // load coords + int count = 0; + for ( auto && node : my_nodes ) + { + const Vector3d & owner_coords = node->owner_coords(my_owner); + for ( int j = 0; j < dim; j++ ) + { + coords[count++] = owner_coords[j]; + } + } + + // determinant at integration points + my_master_elem.determinant( dim, nelem, coords.data(), det_J.data(), &error ); + + double elem_volume = 0.; + for ( int ip = 0; ip < nint; ip++ ) + { + elem_volume += det_J[ip] * intg_weights[ip]; + } + + return elem_volume; +} + +double +SubElement::maximum_relative_angle() const +{ /* %TRACE% */ /* %TRACE% */ + // Find the maximum angle formed at the vertices in parametric coordinates. + // These are obviously differentt than that maximum angle in physical coordinates due to + // the shape of the owning element. + double max_angle = 0; + + const stk::topology topol = topology(); + const unsigned num_edges = topol.num_edges(); + for ( unsigned edge0 = 0; edge0 < num_edges; edge0++ ) + { + const unsigned * lnn0 = get_edge_node_ordinals(topol, edge0); + ThrowAssert( + 2 == topol.edge_topology(edge0).num_nodes() || 3 == topol.edge_topology(edge0).num_nodes()); + + for ( unsigned edge1 = edge0+1; edge1 < num_edges; edge1++ ) + { + const unsigned * lnn1 = get_edge_node_ordinals(topol, edge1); + ThrowAssert(2 == topol.edge_topology(edge1).num_nodes() || + 3 == topol.edge_topology(edge1).num_nodes()); + + int node0 = -1; + int node1 = -1; + if (lnn0[0] == lnn1[0]) + { + node0 = 0; node1 = 0; + } + else if (lnn0[0] == lnn1[1]) + { + node0 = 0; node1 = 1; + } + else if (lnn0[1] == lnn1[0]) + { + node0 = 1; node1 = 0; + } + else if (lnn0[1] == lnn1[1]) + { + node0 = 1; node1 = 1; + } + else + { + continue; + } + const Vector3d vec0 = my_nodes[lnn0[1-node0]]->owner_coords(my_owner) - my_nodes[lnn0[node0]]->owner_coords(my_owner); + const Vector3d vec1 = my_nodes[lnn1[1-node1]]->owner_coords(my_owner) - my_nodes[lnn1[node1]]->owner_coords(my_owner); + const double angle = std::acos( Dot(vec0.unit_vector(),vec1.unit_vector()) ); + + //if (angle > 2.4) + //{ + // krinolog << "DEBUG: bad angle = " << angle << " between edges=" << edge0 << "," << edge1 << " at node=" << lnn0[node0] << stk::diag::dendl; + //} + + if (angle > max_angle) + { + max_angle = angle; + } + } + } + + return max_angle; +} + +void +SubElement::decompose_edges(CDMesh & mesh, const InterfaceID interface_key) +{ /* %TRACE% */ /* %TRACE% */ + const std::string & owner_type = my_owner->topology().name(); + const std::string & sub_type = topology().name(); + ThrowRuntimeError("Subelement decomposition for subelement of type '" << sub_type + << "' which was generated from owning element of type '" << owner_type + << "' is missing the capability to generate conformal facets."); +} + +void +SubElement::find_refined_edges(std::vector & refined_edges) const +{ /* %TRACE% */ /* %TRACE% */ + + const stk::topology topol = topology(); + const unsigned num_edges = topol.num_edges(); + refined_edges.reserve(num_edges); + for ( unsigned edge = 0; edge < num_edges; edge++ ) + { + const unsigned * edge_node_ordinals = get_edge_node_ordinals(topol, edge); + + const int num_edge_nodes = topol.edge_topology(edge).num_nodes(); + ThrowRequire(2 == num_edge_nodes || 3 == num_edge_nodes); + + if ((2 == num_edge_nodes && + NULL != SubElementNode::common_child({my_nodes[edge_node_ordinals[0]], my_nodes[edge_node_ordinals[1]]})) || + (3 == num_edge_nodes && + (NULL != SubElementNode::common_child({my_nodes[edge_node_ordinals[0]], my_nodes[edge_node_ordinals[2]]}) || + NULL != SubElementNode::common_child({my_nodes[edge_node_ordinals[1]], my_nodes[edge_node_ordinals[2]]})))) + { + refined_edges.push_back(edge); + } + } +} + +int +SubElement::find_longest_bad_edge(std::vector & bad_edges) const +{ /* %TRACE% */ /* %TRACE% */ + + const stk::topology topol = topology(); + + const unsigned num_bad_edges = bad_edges.size(); + PointVec edge_midpt(num_bad_edges,Vector3d::ZERO); + + if (0 == num_bad_edges) return -1; + + double max_length = 0; + unsigned longest_bad_edge_index = 0; + for ( unsigned index = 0; index < num_bad_edges; index++ ) + { + const unsigned edge = bad_edges[index]; + + const unsigned * const lnn = get_edge_node_ordinals(topol, edge); + const int num_edge_nodes = topol.edge_topology(edge).num_nodes(); + + const SubElementNode * const node0 = my_nodes[lnn[0]]; + const SubElementNode * const node1 = my_nodes[lnn[1]]; + + const Vector3d & coord0 = node0->coordinates(); + const Vector3d & coord1 = node1->coordinates(); + + const double edge_straight_length = (coord0 - coord1).length(); + ThrowRequire(edge_straight_length > 0.0); + + if (2 == num_edge_nodes) + { + edge_midpt[index] = 0.5*(coord0 + coord1); + } + else + { + ThrowAssert (3 == num_edge_nodes); + edge_midpt[index] = my_nodes[lnn[0]]->coordinates(); + } + + // we need an absolute mechanism for selecting the edge to bisect so that all elements that share + // common edges will make the same decisions + if (utility::is_more(edge_straight_length,max_length)) + { + longest_bad_edge_index = index; + max_length = edge_straight_length; + } + else if (!utility::is_less(edge_straight_length,max_length)) // tie breaker + { + const Vector3d & edge_midside_coords = edge_midpt[index]; + // note that it is safe to assume that longest_bad_edge is already assigned if edge_length == max_length + const Vector3d longest_edge_midside_coords = edge_midpt[longest_bad_edge_index]; + + ThrowAssert((utility::is_not_equal(edge_midside_coords[0],longest_edge_midside_coords[0]) || + utility::is_not_equal(edge_midside_coords[1],longest_edge_midside_coords[1]))); + + if (utility::is_more(edge_midside_coords[0],longest_edge_midside_coords[0]) || + (!utility::is_less(edge_midside_coords[0],longest_edge_midside_coords[0]) && + (utility::is_more(edge_midside_coords[1],longest_edge_midside_coords[1])))) + { + longest_bad_edge_index = index; + max_length = edge_straight_length; + } + } + } + return bad_edges[longest_bad_edge_index]; +} + +int +SubElement::parent_side_id(const int iside) const +{ + return my_parent_side_ids[iside]; +} + +void +SubElement::debug_subelements(const NodeVec & lnodes, const InterfaceID & interface, const int case_id) const +{ /* %TRACE% */ /* %TRACE% */ + krinolog << "owner_id=" << my_owner->entityId() << ", after cutting with interface " << interface << ", case_id=" << case_id << stk::diag::dendl; + + for (unsigned n=0; nget_ancestry() << "]\n"; + } + } + krinolog << " interface signs = "; + const std::vector interfaces = my_owner->get_sorted_cutting_interfaces(); + ThrowRequire(interfaces.size() == myInterfaceSigns.size()); + for (unsigned i=0; idebug(); + } + krinolog << stk::diag::dendl; +} + +void +SubElement::debug() const +{ /* %TRACE% */ /* %TRACE% */ + const double sub_vol = relative_volume(); + krinolog << " owner_id=" << my_owner->entityId() << ", relative_volume=" << sub_vol << ", interface signs = "; + const std::vector interfaces = my_owner->get_sorted_cutting_interfaces(); + ThrowRequire(interfaces.size() == myInterfaceSigns.size()); + for (unsigned i=0; iget_ancestry() << "] with domains { "; + for (int domain : node->get_sorted_node_domains()) krinolog << domain << " "; + krinolog << "}\n"; + } + } + for (unsigned n=0; nhave_interface(interface) && (myInterfaceSigns[my_owner->get_interface_index(interface)] == 0); +} + +SubElement_Tri_6::SubElement_Tri_6( + const NodeVec & nodes, + const std::vector & parent_side_ids, + const Mesh_Element * owner) + : SubElement( stk::topology::TRIANGLE_6_2D, + nodes, + parent_side_ids, + owner) +{ /* %TRACE% */ /* %TRACE% */ +} + +SubElement_Tet_10::SubElement_Tet_10( + const NodeVec & nodes, + const std::vector & parent_side_ids, + const Mesh_Element * owner) + : SubElement( stk::topology::TETRAHEDRON_10, + nodes, + parent_side_ids, + owner) +{ /* %TRACE% */ /* %TRACE% */ +} + +SubElement_Tri_3::SubElement_Tri_3( + const NodeVec & nodes, + const std::vector & parent_side_ids, + const Mesh_Element * owner) + : SubElement( stk::topology::TRIANGLE_3_2D, + nodes, + parent_side_ids, + owner) +{ /* %TRACE% */ /* %TRACE% */ + +} + +void +SubElement_Tri_3::build_quadratic_subelements(CDMesh & mesh) +{ /* %TRACE% */ /* %TRACE% */ + + if ( my_subelements.size() > 0 ) + { + for ( auto && subelem : my_subelements ) + { + subelem->build_quadratic_subelements(mesh); + } + return; + } + + // create 1, 6-noded tri + NodeVec sub_nodes = my_nodes; + sub_nodes.resize(6,(SubElementNode *)NULL); + + sub_nodes[3] = mesh.create_midside_node(my_owner, my_nodes[0], my_nodes[1]); + sub_nodes[4] = mesh.create_midside_node(my_owner, my_nodes[1], my_nodes[2]); + sub_nodes[5] = mesh.create_midside_node(my_owner, my_nodes[2], my_nodes[0]); + + std::unique_ptr sub = std::make_unique( sub_nodes, my_parent_side_ids, my_owner ); + sub->set_interface_signs(get_interface_signs()); + add_subelement( std::move(sub) ); +} + +void SubElement_Tri_3::cut_interior_intersection_point(CDMesh & mesh, const Vector3d & pCoords, const std::vector & sortedDomains) +{ + const std::vector weights{1.-pCoords[0]-pCoords[1], pCoords[0], pCoords[1]}; + + bool badCut = false; + for (auto && weight : weights) + { + if(weight < mesh.get_snapper().get_edge_tolerance()) + { + if (krinolog.shouldPrint(LOG_DEBUG)) + krinolog << "Skipping cut of interior intersection point because of quality." << stk::diag::dendl; + badCut = true; + break; + } + } + + if (!badCut) + { + const SubElementNode * cutNode = mesh.create_child_internal_or_face_node( my_owner, my_nodes, weights ); + cutNode->set_node_domains(sortedDomains); + + NodeVec lnodes = my_nodes; + lnodes.push_back(cutNode); + + handle_tri(lnodes, get_interface_signs(), 0,1,3, 0,-1,-1, false,false,false); + handle_tri(lnodes, get_interface_signs(), 1,2,3, 1,-1,-1, false,false,false); + handle_tri(lnodes, get_interface_signs(), 2,0,3, 2,-1,-1, false,false,false); + } +} + +void +SubElement_Tri_3::determine_node_signs(const CDMesh & mesh, const InterfaceID interface_key) +{ + determine_node_signs_on_edge( mesh, interface_key, 0,1 ); + determine_node_signs_on_edge( mesh, interface_key, 1,2 ); + determine_node_signs_on_edge( mesh, interface_key, 2,0 ); +} + +void +SubElement_Tri_3::determine_node_scores(const CDMesh & mesh, const InterfaceID interface_key) +{ + // No-op, node scores are not used on tris +} + +void +SubElement_Tri_3::decompose_edges(CDMesh & mesh, const InterfaceID interface_key) +{ + process_edge( mesh, interface_key, 0,1 ); + process_edge( mesh, interface_key, 1,2 ); + process_edge( mesh, interface_key, 2,0 ); +} + +void +SubElement_Tri_3::fix_hanging_children(CDMesh & mesh, const InterfaceID & interface, const std::vector & edges_with_children) +{ + int edge_case_id = 0; + for (auto edge_with_children : edges_with_children) + { + edge_case_id += 1< node_signs = {{0, 0, 0}}; + + if (edge_case_id == 0) // uncut subelement + { + if (my_nodes[0]->node_sign_is_set()) node_signs[0] = my_nodes[0]->get_node_sign(); + if (my_nodes[1]->node_sign_is_set()) node_signs[1] = my_nodes[1]->get_node_sign(); + if (my_nodes[2]->node_sign_is_set()) node_signs[2] = my_nodes[2]->get_node_sign(); + + if (!(node_signs[0] >= 0 && node_signs[1] >= 0 && node_signs[2] >= 0) && + !(node_signs[0] <= 0 && node_signs[1] <= 0 && node_signs[2] <= 0)) + { + return; + } + } + else + { + for (auto && edge_with_child : edges_with_children) + { + const unsigned * edge_node_ordinals = get_edge_node_ordinals(topology(), edge_with_child); + const int i0 = edge_node_ordinals[0]; + const int i1 = edge_node_ordinals[1]; + node_signs[i0] = my_nodes[i0]->get_node_sign(); + node_signs[i1] = my_nodes[i1]->get_node_sign(); + } + } + + perform_decomposition(mesh, interface, node_signs); +} + +void +SubElement_Tri_3::perform_decomposition(CDMesh & mesh, const InterfaceID interface_key, const std::array & node_signs) +{ /* %TRACE% */ /* %TRACE% */ + + const int case_id = + (node_signs[0]+1) + + (node_signs[1]+1)*3 + + (node_signs[2]+1)*9; + + NodeVec lnodes = my_nodes; + lnodes.resize(6,(SubElementNode *)NULL); + + static const unsigned case_permutations[] = + { 0, 0, 0, 2, 0, 0, 2, 1, 1, 1, // 0-9 + 1, 2, 2, 0, 2, 2, 1, 1, 1, 1, // 10-19 + 2, 0, 0, 2, 0, 0, 0 }; // 20-26 + static const unsigned permute_case_ids[] = + { 0, 1, 2, 1, 4, 5, 2,21,24, 1, // 0-9 + 4,21, 4,13,22, 5,22,25, 2, 5, // 10-19 + 24,21,22,25,24,25,26 }; // 20-26 + + stk::topology topo = stk::topology::TRIANGLE_6_2D; + std::vector permute(6); + topo.permutation_node_ordinals(case_permutations[case_id], permute.begin()); + + const unsigned i0 = permute[0]; + const unsigned i1 = permute[1]; + const unsigned i2 = permute[2]; + const unsigned i3 = permute[3]; + const unsigned i5 = permute[5]; + + // nodes and sides permute the same way + const unsigned s0 = permute[0]; + const unsigned s1 = permute[1]; + const unsigned s2 = permute[2]; + + const int permute_case_id = permute_case_ids[case_id]; + + // FIXME: Remove this diagnostic + std::vector node_val(3); + node_val[2] = case_id/9; + node_val[1] = (case_id-9*node_val[2])/3; + node_val[0] = case_id-9*node_val[2]-3*node_val[1]; + ThrowRequire(permute_case_id == (node_val[i0] + node_val[i1]*3 + node_val[i2]*9)); + + const Simplex_Generation_Method simplexMethod = mesh.get_cdfem_support().get_simplex_generation_method(); + +// krinolog << "case_id, permute_case_id = " << case_id << ", " << permute_case_id << stk::diag::dendl; + + switch (permute_case_id) + { + case 0: // ls[0]<0 && ls[1]<0 && ls[2]<0 + case 1: // ls[0]=0 && ls[1]<0 && ls[2]<0 + { + update_interface_signs(interface_key, -1); + } + break; + + case 13: // ls[0]=0 && ls[1]=0 && ls[2]=0 + case 25: // ls[0]=0 && ls[1]>0 && ls[2]>0 + case 26: // ls[0]>0 && ls[1]>0 && ls[2]>0 + { + update_interface_signs(interface_key, +1); + } + break; + + case 2: // ls[0]>0 && ls[1]<0 && ls[2]<0 + case 24: // ls[0]<0 && ls[1]>0 && ls[2]>0 + { + lnodes[i3] = SubElementNode::common_child({lnodes[i0], lnodes[i1]}); + lnodes[i5] = SubElementNode::common_child({lnodes[i2], lnodes[i0]}); + ThrowRequire(nullptr != lnodes[i3] && nullptr != lnodes[i5]); + + const bool diag = determine_diagonal_for_cut_triangle(simplexMethod, lnodes, i0, i1, i2, i3, i5); + + const int sign = (permute_case_id==2) ? 1 : -1; + handle_tri(lnodes, subelement_interface_signs(interface_key, sign), i0,i3,i5, s0,-1,s2, false,true,false); + handle_quad(mesh, lnodes, subelement_interface_signs(interface_key, -sign), i3,i1,i2,i5, s0,s1,s2,-1, false,false,false,true, diag); + } + break; + + case 4: // ls[0]=0 && ls[1]=0 && ls[2]<0 + case 22: // ls[0]=0 && ls[1]=0 && ls[2]>0 + { + const int sign = (permute_case_id==4) ? -1 : 1; + update_interface_signs(interface_key, sign); + } + break; + + case 5: // ls[0]>0 && ls[1]=0 && ls[2]<0 + case 21: // ls[0]<0 && ls[1]=0 && ls[2]>0 + { + lnodes[i5] = SubElementNode::common_child({lnodes[i2], lnodes[i0]}); + ThrowRequire(nullptr != lnodes[i5]); + + const int sign = (permute_case_id==5) ? 1 : -1; + handle_tri(lnodes, subelement_interface_signs(interface_key, sign), i1,i5,i0, -1,s2,s0, true,false,false); + handle_tri(lnodes, subelement_interface_signs(interface_key, -sign), i5,i1,i2, -1,s1,s2, true,false,false); + } + break; + + default: ThrowRuntimeError("Subelement decomposition error. case_id,permute_case_id=" << case_id << "," << permute_case_id); + } + + if (krinolog.shouldPrint(LOG_DEBUG)) + { + debug_subelements(lnodes, interface_key, case_id); + } +} + +bool +SubElement_Tri_3::determine_diagonal_for_cut_triangle(const Simplex_Generation_Method & simplexMethod, const NodeVec & lnodes, const int i0, const int i1, const int i2, const int i3, const int i5) +{ + /* + * 2 o + * / \ + * 5 o \ + * / \ \ + * o---o---o + * 0 3 1 + */ + + // true: connect nodes 3 and 2 + // false: connect nodes 5 and 1 + + if (simplexMethod == CUT_QUADS_BY_GLOBAL_IDENTIFIER) + { + SubElementNodeAncestry ancestry1 = lnodes[i1]->get_ancestry(); + SubElementNodeAncestry ancestry2 = lnodes[i2]->get_ancestry(); + + return ancestry2 < ancestry1; + } + else + { + ThrowRequire(simplexMethod == CUT_QUADS_BY_LARGEST_ANGLE); + + // Angle-based scheme + // Select diagonal that cuts largest angle in quad. Since there isn't an issue with + // conforming, this is always possible (unlike tet). + const int config = ElementObj::evaluate_quad(lnodes[i3],lnodes[i1],lnodes[i2],lnodes[i5]); + return (config == -1); + } +} + +void +SubElement_Tri_3::handle_tri( NodeVec & lnodes, + const std::vector & subInterfaceSigns, + const int i0, const int i1, const int i2, + const int s0, const int s1, const int s2, + const bool is_interface0, const bool is_interface1, const bool is_interface2) +{ + ThrowRequire(!is_degenerate(lnodes, i0,i1,i2)); + + NodeVec sub_nodes(3,(SubElementNode *)NULL); + std::vector sub_parent_ids(3); + + sub_nodes[0] = lnodes[i0]; + sub_nodes[1] = lnodes[i1]; + sub_nodes[2] = lnodes[i2]; + sub_parent_ids[0] = (s0>= 0) ? my_parent_side_ids[s0] : -1; + sub_parent_ids[1] = (s1>= 0) ? my_parent_side_ids[s1] : -1; + sub_parent_ids[2] = (s2>= 0) ? my_parent_side_ids[s2] : -1; + + std::unique_ptr sub = std::make_unique( sub_nodes, sub_parent_ids, my_owner); + sub->set_interface_signs(subInterfaceSigns); + add_subelement( std::move(sub) ); +} + +void +SubElement_Tri_3::handle_quad( CDMesh & mesh, + NodeVec & lnodes, + const std::vector & subInterfaceSigns, + const int i0, const int i1, const int i2, const int i3, + const int s0, const int s1, const int s2, const int s3, + const bool is_interface0, const bool is_interface1, const bool is_interface2, const bool is_interface3, + const bool face ) +{ +#if 1 + if (face) + { + handle_tri( lnodes, subInterfaceSigns, i0,i1,i2, s0,s1,-1, is_interface0,is_interface1,false ); + handle_tri( lnodes, subInterfaceSigns, i2,i3,i0, s2,s3,-1, is_interface2,is_interface3,false ); + } + else + { + handle_tri( lnodes, subInterfaceSigns, i0,i1,i3, s0,-1,s3, is_interface0,false,is_interface3 ); + handle_tri( lnodes, subInterfaceSigns, i2,i3,i1, s2,-1,s1, is_interface2,false,is_interface1 ); + } +#else + NodeVec quad_nodes; + std::vector weights; + const double x0 = 0; + const double y0 = 0; + + quad_nodes.push_back(lnodes[i0]); + weights.push_back(0.25*(1-x0)*(1-y0)); + quad_nodes.push_back(lnodes[i1]); + weights.push_back(0.25*(1+x0)*(1-y0)); + quad_nodes.push_back(lnodes[i2]); + weights.push_back(0.25*(1+x0)*(1+y0)); + quad_nodes.push_back(lnodes[i3]); + weights.push_back(0.25*(1-x0)*(1+y0)); + + lnodes[6] = mesh.create_internal_node( my_owner, quad_nodes, weights ); + + handle_tri(lnodes, ls_index, sign, i0,i1,6, s0,-1,-1); + handle_tri(lnodes, ls_index, sign, i1,i2,6, s1,-1,-1); + handle_tri(lnodes, ls_index, sign, i2,i3,6, s2,-1,-1); + handle_tri(lnodes, ls_index, sign, i3,i0,6, s3,-1,-1); +#endif +} + +bool +SubElement_Tri_3::is_degenerate( NodeVec & lnodes, + const int i0, const int i1, const int i2 ) +{ /* %TRACE% */ /* %TRACE% */ + + if ( lnodes[i0] == lnodes[i1] || + lnodes[i0] == lnodes[i2] || + lnodes[i1] == lnodes[i2] ) + { + // this tri is degenerate with two coincident nodes + return true; + } + + return false; +} + +SubElement_Tet_4::SubElement_Tet_4( + const NodeVec & nodes, + const std::vector & parent_side_ids, + const Mesh_Element * owner) + : SubElement( stk::topology::TETRAHEDRON_4, + nodes, + parent_side_ids, + owner) +{ /* %TRACE% */ /* %TRACE% */ +} + +void SubElement_Tet_4::cut_face_intersection_point_with_permutation(CDMesh & mesh, const std::array & permuteNodes, const std::array & permuteSides, const std::vector & faceNodeWeights, const std::vector & sortedDomains) +{ + const SubElementNode * cutNode = mesh.create_child_internal_or_face_node(my_owner, + {my_nodes[permuteNodes[0]], my_nodes[permuteNodes[1]], my_nodes[permuteNodes[2]]}, + {faceNodeWeights[0], faceNodeWeights[1], faceNodeWeights[2]}); + const auto & previousDomains = cutNode->get_sorted_node_domains(); + ThrowRequire(previousDomains.empty() || sortedDomains == previousDomains); + cutNode->set_node_domains(sortedDomains); + + NodeVec lnodes; + lnodes.reserve(5); + for (int i=0; i<4; ++i) + lnodes.push_back(my_nodes[permuteNodes[i]]); + lnodes.push_back(cutNode); + + handle_tet(lnodes, get_interface_signs(), 0,1,4,3, permuteSides[0],-1,-1,permuteSides[3]); + handle_tet(lnodes, get_interface_signs(), 1,2,4,3, permuteSides[1],-1,-1,permuteSides[3]); + handle_tet(lnodes, get_interface_signs(), 2,0,4,3, permuteSides[2],-1,-1,permuteSides[3]); +} + +void SubElement_Tet_4::cut_interior_intersection_point(CDMesh & mesh, const Vector3d & pCoords, const std::vector & sortedDomains) +{ + const std::vector weights{1.-pCoords[0]-pCoords[1]-pCoords[2], pCoords[0], pCoords[1], pCoords[2]}; + + bool badCut = false; + for (auto && weight : weights) + { + if(weight < mesh.get_snapper().get_edge_tolerance()) + { + if (krinolog.shouldPrint(LOG_DEBUG)) + krinolog << "Skipping cut of interior intersection point because of quality." << stk::diag::dendl; + badCut = true; + break; + } + } + + if (!badCut) + { + const SubElementNode * cutNode = mesh.create_child_internal_or_face_node( my_owner, my_nodes, weights ); + cutNode->set_node_domains(sortedDomains); + + NodeVec lnodes = my_nodes; + lnodes.push_back(cutNode); + + handle_tet(lnodes, get_interface_signs(), 0,3,1,4, -1,-1,-1,0); + handle_tet(lnodes, get_interface_signs(), 1,3,2,4, -1,-1,-1,1); + handle_tet(lnodes, get_interface_signs(), 0,2,3,4, -1,-1,-1,2); + handle_tet(lnodes, get_interface_signs(), 0,1,2,4, -1,-1,-1,3); + } +} + +void +SubElement_Tet_4::build_quadratic_subelements(CDMesh & mesh) +{ /* %TRACE% */ /* %TRACE% */ + + if ( my_subelements.size() > 0 ) + { + for ( auto && subelem : my_subelements ) + { + subelem->build_quadratic_subelements(mesh); + } + return; + } + + // create 1, 10-noded tet + NodeVec sub_nodes = my_nodes; + sub_nodes.resize(10,(SubElementNode *)NULL); + + const stk::topology tet10_topology = stk::topology::TETRAHEDRON_10; + + for (unsigned edge_i=0; edge_i sub = std::make_unique( sub_nodes, my_parent_side_ids, my_owner ); + sub->set_interface_signs(get_interface_signs()); + add_subelement( std::move(sub) ); +} + +struct FaceIntersection +{ + FaceIntersection(const int iFace, const Vector3d & coords, const std::vector & domains) + : face(iFace), + parametricCoords(coords), + sortedDomains(domains) {} + + int face; + Vector3d parametricCoords; + std::vector sortedDomains; +}; + +void +SubElement_Tet_4::cut_face_interior_intersection_points(CDMesh & mesh, const InterfaceID & interface1, const InterfaceID & interface2, int level) +{ /* %TRACE% */ /* %TRACE% */ + + if ( my_subelements.size() > 0 ) + { + for ( auto && subelem : my_subelements ) + { + subelem->cut_face_interior_intersection_points(mesh, interface1, interface2, level); + } + return; + } + + static constexpr std::array,4> permuteNodes{{ {{0,3,1,2}}, {{1,3,2,0}}, {{0,2,3,1}}, {{0,1,2,3}} }}; + static constexpr std::array,4> permuteSides{{ {{2,1,3,0}}, {{0,2,3,1}}, {{3,1,0,2}}, {{0,1,2,3}} }}; + + std::vector faceNodes(3); + // Note: this recursively cuts the first identified face interior intersection point + for (unsigned iFace=0; iFace faceIntersections; + my_owner->fill_face_interior_intersections(faceNodes, interface1, interface2, faceIntersections); + + for (auto && faceIntersection : faceIntersections) + { + const Vector3d & faceCoords = faceIntersection.parametricCoords; + const std::vector faceNodeWeights{1.-faceCoords[0]-faceCoords[1], faceCoords[0], faceCoords[1]}; + bool badCut = false; + for (auto && weight : faceNodeWeights) + { + if (weight < mesh.get_snapper().get_edge_tolerance()) + { + if (krinolog.shouldPrint(LOG_DEBUG)) + krinolog << "Skipping cut of interior face intersection point because of quality." << stk::diag::dendl; + badCut = true; + break; + } + } + if (!badCut) + { + ThrowRequireMsg(level < 8, "Face cut recursion level exceeded."); + cut_face_intersection_point_with_permutation(mesh, permuteNodes[iFace], permuteSides[iFace], faceNodeWeights, faceIntersection.sortedDomains); + cut_face_interior_intersection_points(mesh, interface1, interface2, ++level); + return; + } + } + } +} + +const unsigned ** +SubElement_Tet_4::get_permutation_side_ordinals() +{ + static const unsigned permutation_side_ordinals0[] = { 0, 1, 2, 3 }; + static const unsigned permutation_side_ordinals1[] = { 1, 2, 0, 3 }; + static const unsigned permutation_side_ordinals2[] = { 2, 0, 1, 3 }; + static const unsigned permutation_side_ordinals3[] = { 2, 1, 3, 0 }; + static const unsigned permutation_side_ordinals4[] = { 1, 3, 2, 0 }; + static const unsigned permutation_side_ordinals5[] = { 3, 2, 1, 0 }; + static const unsigned permutation_side_ordinals6[] = { 3, 1, 0, 2 }; + static const unsigned permutation_side_ordinals7[] = { 1, 0, 3, 2 }; + static const unsigned permutation_side_ordinals8[] = { 0, 3, 1, 2 }; + static const unsigned permutation_side_ordinals9[] = { 0, 2, 3, 1 }; + static const unsigned permutation_side_ordinals10[] = { 2, 3, 0, 1 }; + static const unsigned permutation_side_ordinals11[] = { 3, 0, 2, 1 }; + static const unsigned * permutation_side_ordinals[] = + { permutation_side_ordinals0 , permutation_side_ordinals1 , permutation_side_ordinals2, permutation_side_ordinals3 , permutation_side_ordinals4 , permutation_side_ordinals5 , + permutation_side_ordinals6 , permutation_side_ordinals7 , permutation_side_ordinals8, permutation_side_ordinals9, permutation_side_ordinals10, permutation_side_ordinals11 }; + return permutation_side_ordinals; +} + +double SubElement_Tet_4::tet_volume(const std::array & nodes) +{ + return Dot(nodes[3]-nodes[0],Cross(nodes[1]-nodes[0], nodes[2]-nodes[0]))/6.0; +} + +void +SubElement_Tet_4::fix_hanging_children(CDMesh & mesh, const InterfaceID & interface_key, const std::vector & edges_with_children) +{ + int edge_case_id = 0; + for (unsigned i=0; i node_signs = {{0, 0, 0, 0}}; + + if (edge_case_id == 0) // uncut subelement + { + if (my_nodes[0]->node_sign_is_set()) node_signs[0] = my_nodes[0]->get_node_sign(); + if (my_nodes[1]->node_sign_is_set()) node_signs[1] = my_nodes[1]->get_node_sign(); + if (my_nodes[2]->node_sign_is_set()) node_signs[2] = my_nodes[2]->get_node_sign(); + if (my_nodes[3]->node_sign_is_set()) node_signs[3] = my_nodes[3]->get_node_sign(); + + if (!(node_signs[0] >= 0 && node_signs[1] >= 0 && node_signs[2] >= 0 && node_signs[3] >= 0) && + !(node_signs[0] <= 0 && node_signs[1] <= 0 && node_signs[2] <= 0 && node_signs[3] <= 0)) + { + return; + } + } + else + { + for (auto && edge_with_child : edges_with_children) + { + const unsigned * edge_node_ordinals = get_edge_node_ordinals(topology(), edge_with_child); + const int i0 = edge_node_ordinals[0]; + const int i1 = edge_node_ordinals[1]; + node_signs[i0] = my_nodes[i0]->get_node_sign(); + node_signs[i1] = my_nodes[i1]->get_node_sign(); + } + } + + perform_decomposition(mesh, interface_key, node_signs); + } + else + { + ThrowAssert(case_id == 2 || case_id == 3 || case_id == 4); + + static const int edge_case_permutations [] = + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-9 + 2, 6, -1, -1, 5, -1, -1, -1, -1, -1, // 10-19 + 0, 8, 5, -1, -1, -1, 0, -1, 0, -1, // 20-29 + -1, -1, -1, 1, -1, 4, -1, 3, -1, -1, // 30-39 + -1, 2, 2, -1, -1, -1, -1, -1, -1, 1, // 40-49 + -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, // 50-59 + -1, -1, -1, -1 // 60-63 + }; + + const int permutation = edge_case_permutations[edge_case_id]; + ThrowRequire(permutation >= 0); + + stk::topology topo = stk::topology::TETRAHEDRON_10; + std::vector permute_nodes(10); + topo.permutation_node_ordinals(permutation, permute_nodes.begin()); + + const unsigned i0 = permute_nodes[0]; + const unsigned i1 = permute_nodes[1]; + const unsigned i2 = permute_nodes[2]; + const unsigned i3 = permute_nodes[3]; + const unsigned i5 = permute_nodes[5]; + const unsigned i6 = permute_nodes[6]; + const unsigned i7 = permute_nodes[7]; + const unsigned i8 = permute_nodes[8]; + + const unsigned ** permutation_side_ordinals = get_permutation_side_ordinals(); + const unsigned s0 = permutation_side_ordinals[permutation][0]; + const unsigned s1 = permutation_side_ordinals[permutation][1]; + const unsigned s2 = permutation_side_ordinals[permutation][2]; + const unsigned s3 = permutation_side_ordinals[permutation][3]; + + NodeVec lnodes = my_nodes; + lnodes.resize(10,(SubElementNode *)NULL); + + const int arbitrary_sign = 0; + const auto subInterfaceSigns = subelement_interface_signs(interface_key, arbitrary_sign); + const Simplex_Generation_Method simplexMethod = mesh.get_cdfem_support().get_simplex_generation_method(); + const bool globalIDsAreParallelConsistent = mesh.get_cdfem_support().get_global_ids_are_parallel_consistent(); + + if (case_id == 2) + { + lnodes[i6] = SubElementNode::common_child({lnodes[i0], lnodes[i2]}); + lnodes[i8] = SubElementNode::common_child({lnodes[i1], lnodes[i3]}); + ThrowRequire(nullptr != lnodes[i6] && nullptr != lnodes[i8]); + + + handle_tet( lnodes, subInterfaceSigns, i6,i2,i3,i8, -1,s1,-1,s2 ); + handle_tet( lnodes, subInterfaceSigns, i3,i0,i6,i8, s0,-1,-1,s2 ); + handle_tet( lnodes, subInterfaceSigns, i6,i0,i1,i8, -1,s0,-1,s3 ); + handle_tet( lnodes, subInterfaceSigns, i1,i2,i6,i8, s1,-1,-1,s3 ); + } + else if (case_id == 3) + { + lnodes[i6] = SubElementNode::common_child({lnodes[i0], lnodes[i2]}); + lnodes[i7] = SubElementNode::common_child({lnodes[i0], lnodes[i3]}); + lnodes[i8] = SubElementNode::common_child({lnodes[i1], lnodes[i3]}); + ThrowRequire(nullptr != lnodes[i6] && nullptr != lnodes[i7] && nullptr != lnodes[i8]); + + const bool face0 = determine_diagonal_for_cut_triangular_face(simplexMethod, globalIDsAreParallelConsistent, lnodes, i3, i0, i1, i7, i8); + const bool face2 = determine_diagonal_for_cut_triangular_face(simplexMethod, globalIDsAreParallelConsistent, lnodes, i0, i3, i2, i7, i6); + + // Connect 6-8 + handle_tet( lnodes, subInterfaceSigns, i1,i2,i6,i8, s1,-1,-1,s3 ); + handle_pyramid( lnodes, subInterfaceSigns, i1,i0,i7,i8,i6, s3,s2,-1,-1,s0, face0 ); + handle_pyramid( lnodes, subInterfaceSigns, i2,i3,i7,i6,i8, s1,s0,-1,-1,s2, face2 ); + } + else if (case_id == 4) + { + lnodes[i5] = SubElementNode::common_child({lnodes[i1], lnodes[i2]}); + lnodes[i7] = SubElementNode::common_child({lnodes[i0], lnodes[i3]}); + lnodes[i8] = SubElementNode::common_child({lnodes[i1], lnodes[i3]}); + ThrowRequire(nullptr != lnodes[i5] && nullptr != lnodes[i7] && nullptr != lnodes[i8]); + + const bool face0 = determine_diagonal_for_cut_triangular_face(simplexMethod, globalIDsAreParallelConsistent, lnodes, i3, i0, i1, i7, i8); + const bool face1 = determine_diagonal_for_cut_triangular_face(simplexMethod, globalIDsAreParallelConsistent, lnodes, i1, i2, i3, i5, i8); + + // Connect 5-7 + handle_tet( lnodes, subInterfaceSigns, i0,i5,i2,i7, -1,-1,s2,s3 ); + handle_pyramid( lnodes, subInterfaceSigns, i1,i0,i7,i8,i5, s3,-1,-1,s1,s0, face0 ); + handle_pyramid( lnodes, subInterfaceSigns, i5,i8,i3,i2,i7, -1,s0,s2,-1,s1, face1 ); + } + + if (krinolog.shouldPrint(LOG_DEBUG)) + { + debug_subelements(lnodes, interface_key, case_id); + } + } +} + +void +SubElement_Tet_4::determine_node_signs(const CDMesh & mesh, const InterfaceID interface_key) +{ + ThrowAssert(!have_subelements()); + determine_node_signs_on_edge( mesh, interface_key, 0,1 ); + determine_node_signs_on_edge( mesh, interface_key, 1,2 ); + determine_node_signs_on_edge( mesh, interface_key, 0,2 ); + determine_node_signs_on_edge( mesh, interface_key, 0,3 ); + determine_node_signs_on_edge( mesh, interface_key, 1,3 ); + determine_node_signs_on_edge( mesh, interface_key, 2,3 ); +} + +void +SubElement_Tet_4::determine_node_scores(const CDMesh & mesh, const InterfaceID interface_key) +{ + ThrowAssert(!have_subelements()); + + const Simplex_Generation_Method simplexMethod = mesh.get_cdfem_support().get_simplex_generation_method(); + if (simplexMethod == CUT_QUADS_BY_NEAREST_EDGE_CUT) + { + // nodal edge cut length based criterion + // Use globally consistent comparison at nodes based on the shortest relative edge length for the cut edges that use the node. + + // The general idea is to prefer edges that emanate away from nodes that have nearby cuts. This, for perfectly shaped elements, + // will cut the largest angles. + + determine_node_scores_on_edge( mesh, interface_key, 0,1 ); + determine_node_scores_on_edge( mesh, interface_key, 1,2 ); + determine_node_scores_on_edge( mesh, interface_key, 0,2 ); + determine_node_scores_on_edge( mesh, interface_key, 0,3 ); + determine_node_scores_on_edge( mesh, interface_key, 1,3 ); + determine_node_scores_on_edge( mesh, interface_key, 2,3 ); + } + else if (simplexMethod == CUT_QUADS_BY_LARGEST_ANGLE) + { + // nodal face angle criterion + // Use globally consistent comparison at nodes based on the largest angle for the cut faces that use the node. + // This does not rely on perfectly shaped elements to cut the larges angles. + + determine_node_scores_on_face( mesh, interface_key, 0,1,3 ); + determine_node_scores_on_face( mesh, interface_key, 1,2,3 ); + determine_node_scores_on_face( mesh, interface_key, 2,0,3 ); + determine_node_scores_on_face( mesh, interface_key, 2,0,3 ); + } +} + + +static std::pair get_quad_angle_measures(const Vector3d & x0, const Vector3d & x1, const Vector3d & x2, const Vector3d & x3) +{ + std::array sides{x1-x0, x2-x1, x3-x2, x0-x3}; + for (auto && side : sides) side.unitize(); + + // return measure02,measure13 where measureAB=std::max(-cos(A),-cos(B)) + return {std::max(Dot(sides[3],sides[0]), Dot(sides[1],sides[2])), std::max(Dot(sides[0],sides[1]), Dot(sides[2],sides[3]))}; +} + +static void determine_node_scores_on_triangle_face( const std::array & faceNodes ) +{ + /* + * 2 o + * / \ + * / o 4 + * / / \ + * o---o---o + * 0 3 1 + */ + + const std::pair measure23Andmeasure04 = get_quad_angle_measures(faceNodes[2]->coordinates(), faceNodes[0]->coordinates(), faceNodes[3]->coordinates(), faceNodes[4]->coordinates()); + faceNodes[2]->set_node_score(measure23Andmeasure04.first); + faceNodes[0]->set_node_score(measure23Andmeasure04.second); +} + +void +SubElement_Tet_4::determine_node_scores_on_face( const CDMesh & mesh, const InterfaceID interface, const int i0, const int i1, const int i2 ) +{ + + const SubElementNode * parent0 = my_nodes[i0]; + const SubElementNode * parent1 = my_nodes[i1]; + const SubElementNode * parent2 = my_nodes[i2]; + + if (parent0->get_node_on_interface() || parent1->get_node_on_interface() || parent2->get_node_on_interface()) return; + + const SubElementNode * child0 = SubElementNode::common_child({parent0, parent1}); + const SubElementNode * child1 = SubElementNode::common_child({parent1, parent2}); + const SubElementNode * child2 = SubElementNode::common_child({parent2, parent0}); + + const int caseId = + ((child0 == nullptr) ? 0 : 1) + + ((child1 == nullptr) ? 0 : 2) + + ((child2 == nullptr) ? 0 : 4); + + if (caseId == 3) + determine_node_scores_on_triangle_face({parent0, parent1, parent2, child0, child1}); + else if (caseId == 5) + determine_node_scores_on_triangle_face({parent2, parent0, parent1, child2, child0}); + else if (caseId == 6) + determine_node_scores_on_triangle_face({parent1, parent2, parent0, child1, child2}); +} + +void +SubElement_Tet_4::decompose_edges(CDMesh & mesh, const InterfaceID interface_key) +{ /* %TRACE% */ /* %TRACE% */ + + process_edge( mesh, interface_key, 0,1 ); + process_edge( mesh, interface_key, 1,2 ); + process_edge( mesh, interface_key, 0,2 ); + process_edge( mesh, interface_key, 0,3 ); + process_edge( mesh, interface_key, 1,3 ); + process_edge( mesh, interface_key, 2,3 ); +} + +void +SubElement_Tet_4::perform_decomposition(CDMesh & mesh, const InterfaceID interface_key, const std::array & node_signs) +{ /* %TRACE% */ /* %TRACE% */ + + // create between 4 to 6 conforming tetrahedral subelements + + const int case_id = + (node_signs[0]+1) + + (node_signs[1]+1)*3 + + (node_signs[2]+1)*9 + + (node_signs[3]+1)*27; + + NodeVec lnodes = my_nodes; + lnodes.resize(10,(SubElementNode *)NULL); + + static const unsigned case_permutations[] = + { 0, 0, 0, 1, 0, 0, 1, 5, 0, 2, // 0-9 + 2, 6, 1, 0, 0, 1, 1,10, 2, 2, // 10-19 + 2,11, 2, 4, 1, 8, 4, 4, 3, 3, // 20-29 + 4, 3, 3, 9, 5, 7, 7, 6, 6, 9, // 30-39 + 0, 9, 9, 6, 7, 7, 7, 9,11, 3, // 40-49 + 4, 3, 3, 4, 4, 8, 3, 4, 4,11, // 50-59 + 4, 2, 2,10, 8, 1,10, 0, 1, 6, // 60-69 + 2, 2, 7, 5, 1, 0, 0, 1, 0, 0, // 70-79 + 0 }; // 80 + + stk::topology topo = stk::topology::TETRAHEDRON_10; + std::vector permute_nodes(10); + topo.permutation_node_ordinals(case_permutations[case_id], permute_nodes.begin()); + + const unsigned i0 = permute_nodes[0]; + const unsigned i1 = permute_nodes[1]; + const unsigned i2 = permute_nodes[2]; + const unsigned i3 = permute_nodes[3]; + const unsigned i4 = permute_nodes[4]; + const unsigned i5 = permute_nodes[5]; + const unsigned i6 = permute_nodes[6]; + const unsigned i7 = permute_nodes[7]; + const unsigned i8 = permute_nodes[8]; + + const unsigned ** permutation_side_ordinals = get_permutation_side_ordinals(); + const unsigned s0 = permutation_side_ordinals[case_permutations[case_id]][0]; + const unsigned s1 = permutation_side_ordinals[case_permutations[case_id]][1]; + const unsigned s2 = permutation_side_ordinals[case_permutations[case_id]][2]; + const unsigned s3 = permutation_side_ordinals[case_permutations[case_id]][3]; + + static const unsigned permute_case_ids[] = + { 0, 1, 2, 1, 4, 5, 2, 5, 8, 1, // 0-9 + 4, 5, 4,13,14, 5,14,75, 2, 5, // 10-19 + 8, 5,14,75, 8,75,78, 1, 4, 5, // 20-29 + 4,13,14, 5,14,75, 4,13,14,13, // 30-39 + 40,67,14,67,76, 5,14,75,14,67, // 40-49 + 76,75,76,79, 2, 5, 8, 5,14,75, // 50-59 + 8,75,78, 5,14,75,14,67,76,75, // 60-69 + 76,79, 8,75,78,75,76,79,78,79, // 70-79 + 80 }; // 80 + + const int permute_case_id = permute_case_ids[case_id]; + + // FIXME: Remove this diagnostic + std::vector node_val(4); + node_val[3] = case_id/27; + node_val[2] = (case_id-27*node_val[3])/9; + node_val[1] = (case_id-27*node_val[3]-9*node_val[2])/3; + node_val[0] = case_id-27*node_val[3]-9*node_val[2]-3*node_val[1]; + ThrowRequire(permute_case_id == (node_val[i0] + node_val[i1]*3 + node_val[i2]*9 + node_val[i3]*27)); + + const Simplex_Generation_Method simplexMethod = mesh.get_cdfem_support().get_simplex_generation_method(); + const bool globalIDsAreParallelConsistent = mesh.get_cdfem_support().get_global_ids_are_parallel_consistent(); + + //krinolog << "permute_case_id " << permute_case_id << stk::diag::dendl; + + switch (permute_case_id) + { + case 0: // ls[0]<0 && ls[1]<0 && ls[2]<0 && ls[3]<0 + case 1: // ls[0]=0 && ls[1]<0 && ls[2]<0 && ls[3]<0 + case 4: // ls[0]=0 && ls[1]=0 && ls[2]<0 && ls[3]<0 + { + update_interface_signs(interface_key, -1); + } + break; + + case 40: // ls[0]=0 && ls[1]=0 && ls[2]=0 && ls[3]=0 + case 76: // ls[0]=0 && ls[1]=0 && ls[2]>0 && ls[3]>0 + case 79: // ls[0]=0 && ls[1]>0 && ls[2]>0 && ls[3]>0 + case 80: // ls[0]>0 && ls[1]>0 && ls[2]>0 && ls[3]>0 + { + update_interface_signs(interface_key, +1); + } + break; + + case 2: // ls[0]>0 && ls[1]<0 && ls[2]<0 && ls[3]<0 + case 78: // ls[0]<0 && ls[1]>0 && ls[2]>0 && ls[3]>0 + { + const int sign = (permute_case_id==2) ? -1 : 1; + + lnodes[i4] = SubElementNode::common_child({lnodes[i0], lnodes[i1]}); + lnodes[i6] = SubElementNode::common_child({lnodes[i0], lnodes[i2]}); + lnodes[i7] = SubElementNode::common_child({lnodes[i0], lnodes[i3]}); + ThrowRequire(nullptr != lnodes[i4] && nullptr != lnodes[i6] && nullptr != lnodes[i7]); + + // face0: true: connect 4 and 3, false: connect 7 and 1 + // face2: true: connect 7 and 2, false: connect 6 and 3 + // face3: true: connect 6 and 1, false: connect 4 and 2 + const bool face0 = determine_diagonal_for_cut_triangular_face(simplexMethod, globalIDsAreParallelConsistent, lnodes, i0, i1, i3, i4, i7); + const bool face2 = determine_diagonal_for_cut_triangular_face(simplexMethod, globalIDsAreParallelConsistent, lnodes, i0, i3, i2, i7, i6); + const bool face3 = determine_diagonal_for_cut_triangular_face(simplexMethod, globalIDsAreParallelConsistent, lnodes, i0, i2, i1, i6, i4); + + handle_tet( lnodes, subelement_interface_signs(interface_key, -sign), i6,i4,i7,i0, s3,s0,s2,-1 ); + handle_wedge( mesh, lnodes, subelement_interface_signs(interface_key, sign), i4,i7,i6,i1,i3,i2, s0,s2,s3,-1,s1, face0,face2,!face3 ); + } + break; + + case 5: // ls[0]>0 && ls[1]=0 && ls[2]<0 && ls[3]<0 + case 75: // ls[0]<0 && ls[1]=0 && ls[2]>0 && ls[3]>0 + { + const int sign = (permute_case_id==5) ? -1 : 1; + + lnodes[i6] = SubElementNode::common_child({lnodes[i0], lnodes[i2]}); + lnodes[i7] = SubElementNode::common_child({lnodes[i0], lnodes[i3]}); + ThrowRequire(nullptr != lnodes[i6] && nullptr != lnodes[i7]); + + // face2: true: connect 7 and 2, false: connect 6 and 3 + const bool face2 = determine_diagonal_for_cut_triangular_face(simplexMethod, globalIDsAreParallelConsistent, lnodes, i0, i3, i2, i7, i6); + + handle_tet( lnodes, subelement_interface_signs(interface_key, -sign), i6,i1,i7,i0, s3,s0,s2,-1 ); + handle_pyramid( lnodes, subelement_interface_signs(interface_key, sign), i7,i6,i2,i3,i1, -1,s3,s1,s0,s2, face2 ); + } + break; + + case 8: // ls[0]>0 && ls[1]>0 && ls[2]<0 && ls[3]<0 + { + lnodes[i5] = SubElementNode::common_child({lnodes[i1], lnodes[i2]}); + lnodes[i6] = SubElementNode::common_child({lnodes[i0], lnodes[i2]}); + lnodes[i7] = SubElementNode::common_child({lnodes[i0], lnodes[i3]}); + lnodes[i8] = SubElementNode::common_child({lnodes[i1], lnodes[i3]}); + ThrowRequire(nullptr != lnodes[i5] && nullptr != lnodes[i6] && nullptr != lnodes[i7] && nullptr != lnodes[i8]); + + // face0: true: connect 7 and 1, false: connect 8 and 0 + // face1: true: connect 5 and 3, false: connect 8 and 2 + // face2: true: connect 7 and 2, false: connect 6 and 3 + // face3: true: connect 5 and 0, false: connect 6 and 1 + const bool face0 = determine_diagonal_for_cut_triangular_face(simplexMethod, globalIDsAreParallelConsistent, lnodes, i3, i0, i1, i7, i8); + const bool face1 = determine_diagonal_for_cut_triangular_face(simplexMethod, globalIDsAreParallelConsistent, lnodes, i1, i2, i3, i5, i8); + const bool face2 = determine_diagonal_for_cut_triangular_face(simplexMethod, globalIDsAreParallelConsistent, lnodes, i0, i3, i2, i7, i6); + const bool face3 = determine_diagonal_for_cut_triangular_face(simplexMethod, globalIDsAreParallelConsistent, lnodes, i2, i1, i0, i5, i6); + + // face 4: true: connect 6 and 8, false: connect 7 and 5 + const bool face4 = ElementObj::determine_diagonal_for_internal_quad_of_cut_tet_from_edge_nodes(simplexMethod, lnodes[i8], lnodes[i5], lnodes[i6], lnodes[i7], + face0, face1, face2, face3); + + ThrowAssert( mesh.num_ls_fields() > 1 || + simplexMethod != CUT_QUADS_BY_GLOBAL_IDENTIFIER || + face4 == ElementObj::determine_diagonal_for_internal_quad_of_cut_tet_from_owner_nodes(lnodes[i0], lnodes[i1], lnodes[i2], lnodes[i3]) ); + + handle_wedge( mesh, lnodes, subelement_interface_signs(interface_key, -1), i8,i3,i7,i5,i2,i6, s1,s2,-1,s0,s3, !face1,!face2,face4 ); + handle_wedge( mesh, lnodes, subelement_interface_signs(interface_key, 1), i8,i1,i5,i7,i0,i6, s0,s3,-1,s1,s2, !face0,!face3,face4 ); + } + break; + + case 13: // ls[0]=0 && ls[1]=0 && ls[2]=0 && ls[3]<0 + case 67: // ls[0]=0 && ls[1]=0 && ls[2]=0 && ls[3]>0 + { + const int sign = (permute_case_id==13) ? -1 : 1; + update_interface_signs(interface_key, sign); + } + break; + + case 14: // ls[0]>0 && ls[1]=0 && ls[2]=0 && ls[3]<0 + { + lnodes[i7] = SubElementNode::common_child({lnodes[i0], lnodes[i3]}); + ThrowRequire(nullptr != lnodes[i7]); + + handle_tet( lnodes, subelement_interface_signs(interface_key, 1), i1,i7,i2,i0, s0,s2,s3,-1 ); + handle_tet( lnodes, subelement_interface_signs(interface_key, -1), i2,i7,i1,i3, s2,s0,s1,-1 ); + } + break; + + default: ThrowRuntimeError("Subelement decomposition error."); + } + + if (krinolog.shouldPrint(LOG_DEBUG)) + { + debug_subelements(lnodes, interface_key, case_id); + } +} + +bool SubElement_Tet_4::determine_diagonal_for_cut_triangular_face(const Simplex_Generation_Method & simplexMethod, const bool globalIDsAreParallelConsistent, const NodeVec & lnodes, const int i0, const int i1, const int i2, const int i3, const int i5) +{ + /* + * 2 o + * / \ + * 5 o \ + * / \ \ + * o---o---o + * 0 3 1 + */ + + // true: connect nodes 3 and 2 + // false: connect nodes 5 and 1 + + if (simplexMethod == CUT_QUADS_BY_GLOBAL_IDENTIFIER) + { + return lnodes[i2]->get_ancestry() < lnodes[i1]->get_ancestry(); + } + else + { + ThrowRequire(simplexMethod == CUT_QUADS_BY_LARGEST_ANGLE || simplexMethod == CUT_QUADS_BY_NEAREST_EDGE_CUT); + return SubElementNode::higher_priority_by_score_then_ancestry(*lnodes[i2],*lnodes[i1], globalIDsAreParallelConsistent); + } +} + +void +SubElement::get_edge_position( + const SubElementNode * n0, + const SubElementNode * n1, + const SubElementNode * n2, + double & position ) +{ + const SubElementEdgeNode * edge_child = dynamic_cast( n2 ); + ThrowRequire(NULL != edge_child); + position = edge_child->get_position(n0,n1); +} + +void +SubElement_Tet_4::handle_pyramid( NodeVec & lnodes, + const std::vector & subInterfaceSigns, + const int i0, const int i1, const int i2, const int i3, const int i4, + const int s0, const int s1, const int s2, const int s3, const int s4, + const bool face4 ) +{ + if (face4) + { + handle_tet( lnodes, subInterfaceSigns, i0,i1,i2,i4, s0,s1,-1,s4 ); + handle_tet( lnodes, subInterfaceSigns, i2,i3,i0,i4, s2,s3,-1,s4 ); + } + else + { + handle_tet( lnodes, subInterfaceSigns, i0,i1,i3,i4, s0,-1,s3,s4 ); + handle_tet( lnodes, subInterfaceSigns, i2,i3,i1,i4, s2,-1,s1,s4 ); + } +} + +void +SubElement_Tet_4::handle_tet( NodeVec & lnodes, + const std::vector & subInterfaceSigns, + const int i0, const int i1, const int i2, const int i3, + const int s0, const int s1, const int s2, const int s3) +{ + ThrowRequire(!is_degenerate(lnodes, i0,i1,i2,i3)); + + NodeVec sub_nodes(4,(SubElementNode *)NULL); + std::vector sub_parent_ids(4); + + sub_nodes[0] = lnodes[i0]; + sub_nodes[1] = lnodes[i1]; + sub_nodes[2] = lnodes[i2]; + sub_nodes[3] = lnodes[i3]; + sub_parent_ids[0] = (s0>= 0) ? my_parent_side_ids[s0] : -1; + sub_parent_ids[1] = (s1>= 0) ? my_parent_side_ids[s1] : -1; + sub_parent_ids[2] = (s2>= 0) ? my_parent_side_ids[s2] : -1; + sub_parent_ids[3] = (s3>= 0) ? my_parent_side_ids[s3] : -1; + + std::unique_ptr sub = std::make_unique( sub_nodes, sub_parent_ids, my_owner); + sub->set_interface_signs(subInterfaceSigns); + add_subelement( std::move(sub) ); +} + +void +SubElement_Tet_4::handle_wedge( CDMesh & mesh, + NodeVec & lnodes, + const std::vector & subInterfaceSigns, + const int i0, const int i1, const int i2, const int i3, const int i4, const int i5, + const int s0, const int s1, const int s2, const int s3, const int s4, + const bool face0, const bool face1, const bool face2 ) +{ +/* + * PARENT Linear 6-Node Wedge Nodes + * 5 (SPACE_DIM=3!) + * . o + * . / \ + * . / \ Face_Quad_4_3D() 0-1-4-3 + * . / \ Face_Quad_4_3D() 1-2-5-4 + * . / \ Face_Quad_4_3D() 0-3-5-2 + * 2 . o---------o 4 Face_Tri_3_3D() 0-2-1 + * o . 3 . Face_Tri_3_3D() 3-4-5 + * /.\ . + * /. \ . + * /. \ . + * /. \ . + * o---------o + * 0 1 + * + */ + // face0: true: connect 0 and 4, false: connect 1 and 3 + // face1: true: connect 1 and 5, false: connect 2 and 4 + // face2: true: connect 0 and 5, false: connect 2 and 3 + + std::vector wedge_nodes(6); + wedge_nodes[0] = i0; + wedge_nodes[1] = i1; + wedge_nodes[2] = i2; + wedge_nodes[3] = i3; + wedge_nodes[4] = i4; + wedge_nodes[5] = i5; + + std::vector wedge_sides(5); + wedge_sides[0] = s0; + wedge_sides[1] = s1; + wedge_sides[2] = s2; + wedge_sides[3] = s3; + wedge_sides[4] = s4; + + // Each of the 3 quad faces can be subdivided in 2 ways, giving a total of 8 possible decompositions. + // 2 of these are illegal, however, since they don't result in tetrahedra. + + static const unsigned tet_nodes0[] = { 0,5,4,3, 0,2,1,5, 0,1,4,5 }; + static const unsigned tet_nodes1[] = { 1,3,5,4, 1,0,2,5, 1,0,5,3 }; + static const unsigned tet_nodes2[] = { 0,5,4,3, 0,2,1,4, 0,2,4,5 }; + static const unsigned tet_nodes3[] = { 0,0,0,0, 0,0,0,0, 0,0,0,0 }; // illegal + static const unsigned tet_nodes4[] = { 0,0,0,0, 0,0,0,0, 0,0,0,0 }; // illegal + static const unsigned tet_nodes5[] = { 1,3,5,4, 1,0,2,3, 1,2,5,3 }; + static const unsigned tet_nodes6[] = { 2,4,3,5, 2,1,0,4, 2,0,3,4 }; + static const unsigned tet_nodes7[] = { 2,4,3,5, 2,1,0,3, 2,1,3,4 }; + static const unsigned * tet_node_map[] = { tet_nodes0 , tet_nodes1 , tet_nodes2, tet_nodes3 , tet_nodes4 , tet_nodes5, tet_nodes6, tet_nodes7 }; + + static const int tet_sides0[] = { 2, 4, 0,-1, 2, 1,-1, 3, -1, 1,-1, 0 }; + static const int tet_sides1[] = { 0, 4, 1,-1, -1, 2, 1, 3, 0, 2,-1,-1 }; + static const int tet_sides2[] = { 2, 4, 0,-1, -1, 1, 0, 3, 2, 1,-1,-1 }; + static const int tet_sides3[] = {-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 }; // illegal + static const int tet_sides4[] = {-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 }; // illegal + static const int tet_sides5[] = { 0, 4, 1,-1, 0, 2,-1, 3, -1, 2,-1, 1 }; + static const int tet_sides6[] = { 1, 4, 2,-1, 1, 0,-1, 3, -1, 0,-1, 2 }; + static const int tet_sides7[] = { 1, 4, 2,-1, -1, 0, 2, 3, 1, 0,-1,-1 }; + static const int * tet_side_map[] = { tet_sides0 , tet_sides1 , tet_sides2 , tet_sides3 , tet_sides4 , tet_sides5, tet_sides6, tet_sides7 }; + + const unsigned case_id = + (face0 ? 0 : 1) + + (face1 ? 0 : 2) + + (face2 ? 0 : 4); + + //krinolog << "Wedge case_id = " << case_id << stk::diag::dendl; + + if (case_id < 3 || case_id > 4) + { + const unsigned * tet_nodes = tet_node_map[case_id]; + const int * tet_sides = tet_side_map[case_id]; + + unsigned lnn[12]; + int lsn[12]; + + for (int n=0; n<12; ++n) + { + lnn[n] = wedge_nodes[tet_nodes[n]]; + lsn[n] = (tet_sides[n]<0) ? -1 : wedge_sides[tet_sides[n]]; + } + + handle_tet(lnodes, subInterfaceSigns, lnn[0], lnn[1], lnn[2], lnn[3], lsn[0], lsn[1], lsn[2], lsn[3]); + handle_tet(lnodes, subInterfaceSigns, lnn[4], lnn[5], lnn[6], lnn[7], lsn[4], lsn[5], lsn[6], lsn[7]); + handle_tet(lnodes, subInterfaceSigns, lnn[8], lnn[9], lnn[10], lnn[11], lsn[8], lsn[9], lsn[10], lsn[11]); + } + else + { + ThrowRequire(case_id == 3 || case_id == 4); + if(krinolog.shouldPrint(LOG_DEBUG)) krinolog << "Schonhardt's polyhedron formed, Adding Steiner point." << "\n"; + + // Schonhardt's polyhedra should never be forced now that diagonals are cut using a globally consistent nodal criterion + ThrowRequireMsg(false, "Schonhardt polyhedron found. This should not happen."); + + NodeVec wedge_nodevec(6); + wedge_nodevec[0] = lnodes[i0]; + wedge_nodevec[1] = lnodes[i1]; + wedge_nodevec[2] = lnodes[i2]; + wedge_nodevec[3] = lnodes[i3]; + wedge_nodevec[4] = lnodes[i4]; + wedge_nodevec[5] = lnodes[i5]; + + const Vector3d & v0 = lnodes[i0]->owner_coords(my_owner); + const Vector3d & v1 = lnodes[i1]->owner_coords(my_owner); + const Vector3d & v2 = lnodes[i2]->owner_coords(my_owner); + const Vector3d & v3 = lnodes[i3]->owner_coords(my_owner); + const Vector3d & v4 = lnodes[i4]->owner_coords(my_owner); + const Vector3d & v5 = lnodes[i5]->owner_coords(my_owner); + + std::vector weights(6); + + // Use the centroid of 1 of the elements that will be within the volume of the wedge depending + // on which way that face2 is cut (which is on the internal quad, which may be non-planar). + // This is not guaranteed, however, to always produce tets that stay within the deformed wedge. + if (face2) + { + weights[0] = 1./4.; + weights[1] = 1./4.; + weights[4] = 1./4.; + weights[5] = 1./4.; + } + else + { + weights[1] = 1./4.; + weights[2] = 1./4.; + weights[3] = 1./4.; + weights[4] = 1./4.; + } + + const SubElementNode * centroid = mesh.create_steiner_node( my_owner, wedge_nodevec, weights ); + + const int i6 = lnodes.size(); + lnodes.push_back(centroid); + const Vector3d & v6 = lnodes[i6]->owner_coords(my_owner); + + // create 8 tets + handle_tet(lnodes, subInterfaceSigns, i0, i2, i1, i6, -1, -1, -1, s3); + handle_tet(lnodes, subInterfaceSigns, i3, i4, i5, i6, -1, -1, -1, s4); + + if (tet_volume({{v0, v2, v1, v6}}) < 0. || tet_volume({{v3, v4, v5, v6}}) < 0.) + { + krinolog << "Error: Schonhard polyhedron decomposition includes inverted tets." << stk::diag::dendl; + } + + if (face0) + { + handle_tet(lnodes, subInterfaceSigns, i0, i1, i4, i6, -1, -1, -1, s0); + handle_tet(lnodes, subInterfaceSigns, i0, i4, i3, i6, -1, -1, -1, s0); + + if (tet_volume({{v0, v1, v4, v6}}) < 0. || tet_volume({{v0, v4, v3, v6}}) < 0.) + { + krinolog << "Error: Schonhard polyhedron decomposition includes inverted tets." << stk::diag::dendl; + } + } + else + { + handle_tet(lnodes, subInterfaceSigns, i0, i1, i3, i6, -1, -1, -1, s0); + handle_tet(lnodes, subInterfaceSigns, i1, i4, i3, i6, -1, -1, -1, s0); + + if (tet_volume({{v0, v1, v3, v6}}) < 0. || tet_volume({{v1, v4, v3, v6}}) < 0.) + { + krinolog << "Error: Schonhard polyhedron decomposition includes inverted tets." << stk::diag::dendl; + } + } + + if (face1) + { + handle_tet(lnodes, subInterfaceSigns, i1, i2, i5, i6, -1, -1, -1, s1); + handle_tet(lnodes, subInterfaceSigns, i1, i5, i4, i6, -1, -1, -1, s1); + + if (tet_volume({{v1, v2, v5, v6}}) < 0. || tet_volume({{v1, v5, v4, v6}}) < 0.) + { + krinolog << "Error: Schonhard polyhedron decomposition includes inverted tets." << stk::diag::dendl; + } + } + else + { + handle_tet(lnodes, subInterfaceSigns, i1, i2, i4, i6, -1, -1, -1, s1); + handle_tet(lnodes, subInterfaceSigns, i2, i5, i4, i6, -1, -1, -1, s1); + + if (tet_volume({{v1, v2, v4, v6}}) < 0. || tet_volume({{v2, v5, v4, v6}}) < 0.) + { + krinolog << "Error: Schonhard polyhedron decomposition includes inverted tets." << stk::diag::dendl; + } + } + + if (face2) + { + handle_tet(lnodes, subInterfaceSigns, i0, i3, i5, i6, -1, -1, -1, s2); + handle_tet(lnodes, subInterfaceSigns, i0, i5, i2, i6, -1, -1, -1, s2); + + if (tet_volume({{v0, v3, v5, v6}}) < 0. || tet_volume({{v0, v5, v2, v6}}) < 0.) + { + krinolog << "Error: Schonhard polyhedron decomposition includes inverted tets." << stk::diag::dendl; + } + } + else + { + handle_tet(lnodes, subInterfaceSigns, i0, i3, i2, i6, -1, -1, -1, s2); + handle_tet(lnodes, subInterfaceSigns, i3, i5, i2, i6, -1, -1, -1, s2); + + if (tet_volume({{v0, v3, v2, v6}}) < 0. || tet_volume({{v3, v5, v2, v6}}) < 0.) + { + krinolog << "Error: Schonhard polyhedron decomposition includes inverted tets." << stk::diag::dendl; + } + } + } +} + +double +SubElement::parametric_distance( const SubElementNode * node0, const SubElementNode * node1 ) +{ + if (node0->is_mesh_node() && node1->is_mesh_node()) + return 1.0; + + std::map node0_stencil; + std::map node1_stencil; + node0->build_stencil(node0_stencil); + node1->build_stencil(node1_stencil); + + // parametric distance = sqrt(0.5*sum(dx_i^2)) + + double sum_sqr_dist = 0.; + for (auto && entry : node0_stencil) + { + const SubElementNode * parent = entry.first; + const double wt0 = entry.second; + + auto it = node1_stencil.find(parent); + const double wt1 = (it == node1_stencil.end()) ? 0.0 : (it->second); + + sum_sqr_dist += (wt0 - wt1)*(wt0 - wt1); + } + for (auto && entry : node1_stencil) + { + const SubElementNode * parent = entry.first; + const double wt1 = entry.second; + + auto it = node0_stencil.find(parent); + if (it == node0_stencil.end()) + { + sum_sqr_dist += wt1*wt1; + } + } + + return std::sqrt(0.5*sum_sqr_dist); +} + +void set_node_signs_for_edge(const InterfaceID interface, const SubElementNode * node1, const SubElementNode * node2, const int node1Sign, const int node2Sign, const double crossingLocation, const CDFEM_Snapper & snapper) +{ + if (node1Sign != node2Sign) + { + // determine tolerance for this edge that may have already been cut + const bool always_snap = snapper.always_snap(); + const double edge_tol = always_snap ? 0.0 : snapper.get_edge_tolerance()/SubElement::parametric_distance(node1, node2); + + if (always_snap || edge_tol > 0.5) + { + if (crossingLocation < 0.5) + { + node1->set_node_sign(0); + } + else + { + node2->set_node_sign(0); + } + } + else + { + if (crossingLocation < edge_tol) + { + node1->set_node_sign(0); + } + else if (crossingLocation > 1.-edge_tol) + { + node2->set_node_sign(0); + } + } + } + + node1->set_node_sign(node1Sign); + node2->set_node_sign(node2Sign); +} + +void +SubElement::determine_node_signs_on_edge( const CDMesh & mesh, const InterfaceID interface, const int i0, const int i1 ) +{ /* %TRACE% */ /* %TRACE% */ + + if (my_owner->have_interface(interface)) + { + const SubElementNode * parent1 = my_nodes[i0]; + const SubElementNode * parent2 = my_nodes[i1]; + + if (parent1->captures_interface(interface)) + parent1->set_node_sign(0); + if (parent2->captures_interface(interface)) + parent2->set_node_sign(0); + + const int sign = myInterfaceSigns[my_owner->get_interface_index(interface)]; + if (sign == 0) + { + const int sign1 = my_owner->interface_node_sign(interface, parent1); + const int sign2 = my_owner->interface_node_sign(interface, parent2); + + const double position = (sign1 == sign2) ? (-1.0) : my_owner->interface_crossing_position(interface, parent1, parent2); + set_node_signs_for_edge(interface, parent1, parent2, sign1, sign2, position, mesh.get_snapper()); + } + } +} + +void +SubElement::determine_node_scores_on_edge( const CDMesh & mesh, const InterfaceID interface, const int i0, const int i1 ) +{ /* %TRACE% */ /* %TRACE% */ + + const SubElementNode * parent1 = my_nodes[i0]; + const SubElementNode * parent2 = my_nodes[i1]; + + if (parent1->get_node_on_interface() || parent2->get_node_on_interface()) return; + + const SubElementNode * child = SubElementNode::common_child({parent1, parent2}); + + if (child) + { + const NodeVec & parents = child->get_parents(); + const std::vector parent_weights = child->get_parent_weights(); + for (size_t i=0; iset_node_score(1.-parent_weights[i]); + } +} + +void +SubElement::process_edge( CDMesh & mesh, const InterfaceID interface, const int i0, const int i1 ) +{ /* %TRACE% */ /* %TRACE% */ + const SubElementNode * parent1 = my_nodes[i0]; + const SubElementNode * parent2 = my_nodes[i1]; + + if (parent1->get_node_on_interface() || parent2->get_node_on_interface()) return; + + const SubElementNode * subnode = SubElementNode::common_child({parent1, parent2}); + + if (subnode == nullptr && + parent1->node_sign_is_set() && + parent2->node_sign_is_set() && + parent1->get_node_sign() == -parent2->get_node_sign() && + have_interface(interface)) + { + const double position = my_owner->interface_crossing_position(interface, parent1, parent2); + ThrowRequireMsg(position > 0. && position < 1., "Error process_edge " << position << " " << parent1->get_node_sign() << " " << parent2->get_node_sign()); + + std::unique_ptr newNode = std::make_unique(my_owner, position, parent1, parent2); + subnode = mesh.add_managed_node(std::move(newNode)); + + // add subnode as child to parents + parent1->add_child(subnode); + parent2->add_child(subnode); + } +} + +void +SubElement::determine_node_signs(const CDMesh & mesh, const InterfaceID interface_key) +{ + if(have_subelements()) + { + for ( auto && subelem : my_subelements ) + { + subelem->SubElement::determine_node_signs(mesh, interface_key); + } + return; + } + + determine_node_signs(mesh, interface_key); +} + +void +SubElement::determine_node_scores(const CDMesh & mesh, const InterfaceID interface_key) +{ + if(have_subelements()) + { + for ( auto && subelem : my_subelements ) + { + subelem->SubElement::determine_node_scores(mesh, interface_key); + } + return; + } + + determine_node_scores(mesh, interface_key); +} + +void +SubElement::decompose(CDMesh & mesh, const InterfaceID interface_key) +{ + if(have_subelements()) + { + for ( auto && subelem : my_subelements ) + { + subelem->decompose(mesh, interface_key); + } + return; + } + + decompose_edges(mesh, interface_key); +} + +std::vector +SubElement::get_edges_with_children(const InterfaceID & interface) const +{ + // Iterate edges looking for any common children of the edge nodes + const stk::topology Top = topology(); + const int num_edges = Top.num_edges(); + + std::vector edgesWithChildren; + for ( int edge = 0; edge < num_edges; ++edge ) + { + const unsigned * edge_node_ordinals = get_edge_node_ordinals(Top, edge); + + const SubElementNode * node0 = my_nodes[edge_node_ordinals[0]]; + const SubElementNode * node1 = my_nodes[edge_node_ordinals[1]]; + const SubElementNode * child = SubElementNode::common_child({node0, node1}); + if( child ) + { + if(krinolog.shouldPrint(LOG_DEBUG)) + { + krinolog << "Found hanging node on edge " << edge << " of element id=" << entityId() << "\n"; + krinolog << " Ancestry: " << child->get_ancestry() << "\n"; + } + edgesWithChildren.push_back(edge); + } + } + return edgesWithChildren; +} + +void +SubElement::handle_hanging_children(CDMesh & mesh, const InterfaceID & interface) +{ + if(have_subelements()) + { + for ( auto && subelem : my_subelements ) + { + subelem->handle_hanging_children(mesh, interface); + } + return; + } + + const std::vector edgesWithChildren = get_edges_with_children(interface); + + fix_hanging_children(mesh, interface, edgesWithChildren); + + for ( auto && subelem : my_subelements ) + { + ThrowRequire(!subelem->have_edges_with_children()); + } +} + +void +SubElement::build_quadratic_subelements(CDMesh & mesh) +{ + ThrowRequireMsg(have_subelements(), "This subelement type does not support quadratic subelements."); + for ( auto && subelem : my_subelements ) + { + subelem->build_quadratic_subelements(mesh); + } +} + +void +SubElement::cut_face_interior_intersection_points(CDMesh & mesh, const InterfaceID & interface1, const InterfaceID & interface2, int level) +{ /* %TRACE% */ /* %TRACE% */ + + if (topology().num_faces() == 0) + return; + + ThrowRequireMsg(false, "This subelement type does not support cut_face_interior_intersection_points: " << topology()); +} + +bool +SubElement_Tet_4::is_degenerate( NodeVec & lnodes, + const int i0, const int i1, const int i2, const int i3 ) +{ /* %TRACE% */ /* %TRACE% */ + + if ( lnodes[i0] == lnodes[i1] || + lnodes[i0] == lnodes[i2] || + lnodes[i0] == lnodes[i3] || + lnodes[i1] == lnodes[i2] || + lnodes[i1] == lnodes[i3] || + lnodes[i2] == lnodes[i3] ) + { + // this tet is degenerate with two coincident nodes + return true; + } + + return false; +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_SubElement.hpp b/packages/krino/krino/krino_lib/Akri_SubElement.hpp new file mode 100644 index 000000000000..cb6e086b947c --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_SubElement.hpp @@ -0,0 +1,431 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_SubElement_h +#define Akri_SubElement_h + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace krino { + +class ProlongationNodeData; +class ProlongationPointData; +class SubElementNodeAncestry; + +bool node_on_negative_side_of_interface(const SubElementNode * node, const InterfaceID key); + +class SubElement : public ElementObj { +public: + SubElement( const stk::topology topo, + const NodeVec & nodes, + const std::vector & side_ids, + const Mesh_Element * owner); + virtual ~SubElement() {} + + static double coordinate_relative_tol() { return 1.e-6; } + static double parametric_distance( const SubElementNode * node0, const SubElementNode * node1 ); + + void set_permutation(); + double relative_volume() const; + double maximum_relative_angle() const; + + void decompose(CDMesh & mesh, const InterfaceID interface_key); + virtual void decompose_edges(CDMesh & mesh, const InterfaceID interface_key); + virtual void determine_node_signs(const CDMesh & mesh, const InterfaceID interface_key); + virtual void determine_node_scores(const CDMesh & mesh, const InterfaceID interface_key); + + void handle_hanging_children(CDMesh & mesh, const InterfaceID & interface); + virtual void fix_hanging_children(CDMesh & mesh, const InterfaceID & interface, const std::vector & edges_with_children) {} + + virtual void build_quadratic_subelements(CDMesh & mesh); + virtual void cut_face_interior_intersection_points(CDMesh & mesh, const InterfaceID & interface1, const InterfaceID & interface2, int level = 0); + + static void get_edge_position(const SubElementNode * n0, const SubElementNode * n1, const SubElementNode * n2, double & position ); + + bool check_entity_nodes(const stk::mesh::BulkData & stkMesh) const; + void get_owner_coord_transform(double * dOwnerdSub) const; + const Mesh_Element & get_owner() const { return *my_owner; } + + int parent_side_id(const int iside) const; + virtual void determine_decomposed_elem_phase(const CDMesh & mesh) override final; + std::vector subelement_interface_signs(const InterfaceID interface, const int sign) const; + const std::vector & get_interface_signs() const { return myInterfaceSigns; } + void initialize_interface_signs(); + void set_interface_signs(const std::vector & interfaceSigns); + void update_interface_signs(const InterfaceID interface_key, const int sign); + unsigned num_sides() const { return my_parent_side_ids.size(); } + void debug_subelements(const NodeVec & lnodes, const InterfaceID & interface, const int case_id) const; + void debug() const; + bool have_interface(const InterfaceID& interface) const; + void determine_if_cut_by_interface(const InterfaceID & interface, bool & haveAnyCrossing, bool & haveRealCrossing) const; + +protected: + std::vector get_edges_with_children(const InterfaceID & interface) const; + void determine_node_signs_on_edge( const CDMesh & mesh, const InterfaceID interface_key, const int i0, const int i1 ); + void determine_node_scores_on_edge( const CDMesh & mesh, const InterfaceID interface_key, const int i0, const int i1 ); + void process_edge( CDMesh & mesh, const InterfaceID interface_key, const int i0, const int i1 ); + void find_refined_edges(std::vector & refined_edges) const; + int find_longest_bad_edge(std::vector & bad_edges) const; + +protected: + std::vector my_parent_side_ids; + const Mesh_Element * my_owner; + std::vector myInterfaceSigns; + +private: + //: Default constructor not allowed + SubElement(); +}; + +class SubElement_Tri_3 : public SubElement { +public: + SubElement_Tri_3( const NodeVec & nodes, + const std::vector & parent_side_ids, + const Mesh_Element * owner); + virtual ~SubElement_Tri_3() {} + + virtual void decompose_edges(CDMesh & mesh, const InterfaceID interface_key) override; + virtual void fix_hanging_children(CDMesh & mesh, const InterfaceID & interface, const std::vector & edges_with_children) override; + virtual void build_quadratic_subelements(CDMesh & mesh) override; + + virtual void determine_node_signs(const CDMesh & mesh, const InterfaceID interface_key) override; + virtual void determine_node_scores(const CDMesh & mesh, const InterfaceID interface_key) override; + virtual void cut_interior_intersection_point(CDMesh & mesh, const Vector3d & pCoords, const std::vector & sortedDomains) override; + +protected: + void perform_decomposition(CDMesh & mesh, const InterfaceID interface_key, const std::array & node_signs); + bool determine_diagonal_for_cut_triangle(const Simplex_Generation_Method & simplexMethod, const NodeVec & lnodes, const int i0, const int i1, const int i2, const int i3, const int i5); + bool is_degenerate( NodeVec & lnodes, + const int i0, const int i1, const int i2 ); + void handle_tri( NodeVec & lnodes, + const std::vector & subInterfaceSigns, + const int i0, const int i1, const int i2, + const int s0, const int s1, const int s2, + const bool is_interface0, const bool is_interface1, const bool is_interface2); + void handle_quad( CDMesh & mesh, + NodeVec & lnodes, + const std::vector & subInterfaceSigns, + const int i0, const int i1, const int i2, const int i3, + const int s0, const int s1, const int s2, const int s3, + const bool is_interface0, const bool is_interface1, const bool is_interface2, const bool is_interface3, + const bool face ); +private: + //: Default constructor not allowed + SubElement_Tri_3(); + +}; + +class SubElement_Tri_6 : public SubElement { +public: + SubElement_Tri_6( const NodeVec & nodes, + const std::vector & side_ids, + const Mesh_Element * owner); + virtual ~SubElement_Tri_6() {} + +private: + //: Default constructor not allowed + SubElement_Tri_6(); + +}; + +class SubElement_Tet_4 : public SubElement { +public: + SubElement_Tet_4( const NodeVec & nodes, + const std::vector & side_ids, + const Mesh_Element * owner); + virtual ~SubElement_Tet_4() {} + + virtual void decompose_edges(CDMesh & mesh, const InterfaceID interface_key) override; + virtual void fix_hanging_children(CDMesh & mesh, const InterfaceID & interface, const std::vector & edges_with_children) override; + virtual void build_quadratic_subelements(CDMesh & mesh) override; + virtual void cut_face_interior_intersection_points(CDMesh & mesh, const InterfaceID & interface1, const InterfaceID & interface2, int level = 0) override; + + virtual void determine_node_signs(const CDMesh & mesh, const InterfaceID interface_key) override; + virtual void determine_node_scores(const CDMesh & mesh, const InterfaceID interface_key) override; + virtual void cut_interior_intersection_point(CDMesh & mesh, const Vector3d & pCoords, const std::vector & sortedDomains) override; + +protected: + void perform_decomposition(CDMesh & mesh, const InterfaceID interface_key, const std::array & node_signs); + void determine_node_scores_on_face( const CDMesh & mesh, const InterfaceID interface, const int i0, const int i1, const int i2 ); + static const unsigned ** get_permutation_side_ordinals(); + static double tet_volume(const std::array & nodes); + + bool is_degenerate( NodeVec & lnodes, + const int i0, const int i1, const int i2, const int i3 ); + void handle_tet( NodeVec & lnodes, + const std::vector & subInterfaceSigns, + const int i0, const int i1, const int i2, const int i3, + const int s0, const int s1, const int s2, const int s3); + void handle_pyramid( NodeVec & lnodes, + const std::vector & subInterfaceSigns, + const int i0, const int i1, const int i2, const int i3, const int i4, + const int s0, const int s1, const int s2, const int s3, const int s4, + const bool face4 ); + void handle_wedge( CDMesh & mesh, + NodeVec & lnodes, + const std::vector & subInterfaceSigns, + const int i0, const int i1, const int i2, const int i3, const int i4, const int i5, + const int s0, const int s1, const int s2, const int s3, const int s4, + const bool face0, const bool face1, const bool face2 ); +private: + //: Default constructor not allowed + SubElement_Tet_4(); + + bool determine_diagonal_for_cut_triangular_face(const Simplex_Generation_Method & simplexMethod, const bool globalIDsAreParallelConsistent, const NodeVec & lnodes, const int i0, const int i1, const int i2, const int i3, const int i5); + void cut_face_intersection_point_with_permutation(CDMesh & mesh, const std::array & permuteNodes, const std::array & permuteSides, const std::vector & faceNodeWeights, const std::vector & sortedDomains); +}; + +class SubElement_Tet_10 : public SubElement { +public: + SubElement_Tet_10( const NodeVec & nodes, + const std::vector & side_ids, + const Mesh_Element * owner); + virtual ~SubElement_Tet_10() {} + +private: + //: Default constructor not allowed + SubElement_Tet_10(); + +}; + +class SubElementNode { +public: + SubElementNode(const Mesh_Element * in_owner) + : my_entity(), + my_entityId(0), + my_is_prolonged_flag(false), + my_cached_owner(in_owner) + {} + + virtual ~SubElementNode() {} + virtual bool is_mesh_node() const { return false; } + virtual NodeVec get_parents() const { throw std::runtime_error("Incorrect usage of SubElementNode. The type of node does not have parents."); } + virtual size_t get_num_parents() const { throw std::runtime_error("Incorrect usage of SubElementNode. The type of node does not have parents."); } + virtual std::vector get_parent_weights() const { throw std::runtime_error("Incorrect usage of SubElementNode. The type of node does not have parents."); } + virtual void prolongate_fields(const CDMesh & mesh) const = 0; + + void get_parent_entities(std::vector & parent_entities) const; + + void set_entity(const stk::mesh::BulkData & mesh, stk::mesh::Entity in_node_obj) const { my_entity = in_node_obj; my_entityId = mesh.identifier(my_entity); } + void set_entity(stk::mesh::Entity nodeEntity, stk::mesh::EntityId nodeEntityId) const { my_entity = nodeEntity; my_entityId = nodeEntityId; } + const Mesh_Element * get_active_element() const { return my_cached_owner; } + bool is_prolonged() const { return my_is_prolonged_flag; } + void set_prolonged_flag(const bool val) const { my_is_prolonged_flag = val; } // this is dangerous, know what you are doing + bool on_common_edge(const SubElementNode * other) const; + + bool entity_is_valid(const stk::mesh::BulkData & mesh) const { return mesh.is_valid(my_entity); } + bool check_entity(const stk::mesh::BulkData & mesh) const { return (my_entityId == 0) ? (my_entity == stk::mesh::Entity()) : (my_entityId == mesh.identifier(my_entity)); } + stk::mesh::Entity & entity() const { return my_entity; } + stk::mesh::EntityId entityId() const { return my_entityId; } + void set_entityId_from_entity(const stk::mesh::BulkData & mesh) const { ThrowAssert(mesh.is_valid(my_entity)); my_entityId = mesh.identifier(my_entity); } + + const Vector3d & owner_coords( const Mesh_Element * in_owner ) const { + if ( in_owner != my_cached_owner ) { + // call derived types function for recomputing owner_coords + my_cached_owner = in_owner; + my_cached_owner_coords = compute_owner_coords( in_owner ); + } + return my_cached_owner_coords; + } + bool get_node_on_interface() const { return my_node_sign == 0; } + + void set_node_domains(const std::vector & nodeDomains) const { my_sorted_node_domains = nodeDomains; } + const std::vector & get_sorted_node_domains() const { return my_sorted_node_domains; } + void insert_node_domains(const std::vector & domainsToAdd) const; + bool captures_intersection_point_domains(const std::vector & intersectionPointDomains) const; + bool captures_interface(const InterfaceID & interface) const; + bool captures_intersection_point_domains(const InterfaceID & interface) const; + static bool higher_priority_by_score_then_ancestry(const SubElementNode & a, const SubElementNode & b, const bool globalIDsAreParallelConsistent); + static bool less_by_entity_id(const SubElementNode & a, const SubElementNode & b); + static bool less_by_coordinates_then_by_entity_id(const SubElementNode & a, const SubElementNode & b); + bool node_sign_is_set() const { return my_node_sign != -2; } + void set_node_sign(const int sign) const { my_node_sign = (my_node_sign == 0 || my_node_sign == -sign) ? 0 : sign; } + int get_node_sign() const { ThrowRequire(node_sign_is_set()); return my_node_sign; } + void clear_node_sign() const { my_node_sign = -2; } + void clear_node_score() const { my_node_score = 1.; } + bool node_score_is_set() const { return my_node_score != 1.; } + void set_node_score(const double score) const { my_node_score = std::min(my_node_score, score); } + double get_node_score() const { ThrowRequire(node_sign_is_set()); return my_node_score; } + + // pure virtual function for recomputing local coordinates + virtual Vector3d compute_owner_coords( const Mesh_Element * in_owner ) const = 0; + + const Vector3d & coordinates() const { return my_global_coords; } + + void add_child(const SubElementNode* child) const { my_children.push_back(child); } + static const SubElementNode * common_child( const NodeVec & parents ); + bool have_child() const; + bool have_child(const SubElementNode* child) const; + + // This is called from Mesh::find_prolongation_node to determine the set of candidate prolongation nodes. + std::vector prolongation_node_fields(const CDMesh & mesh) const; + + const SubElementNode * find_node_with_common_ancestry(const CDMesh & search_mesh) const; + + void get_ancestors(NodeSet & ancestors) const; + SubElementNodeAncestry get_ancestry() const; + void build_stencil(std::map & stencil, const double self_weight = 1.0) const; + void build_constraint_stencil(const FieldRef field, std::vector & entities, std::vector & weights) const; + +protected: + void prolong_cdfem_displacements(const CDMesh & mesh, + const ProlongationPointData * prolong_data, + const bool zero_if_no_prolong_data = true) const; + void prolong_zeroed_fields(const CDMesh & mesh, const ProlongationNodeData * nodeToExamineForExistingField) const; + + mutable stk::mesh::Entity my_entity; + mutable stk::mesh::EntityId my_entityId; + mutable bool my_is_prolonged_flag; + Vector3d my_global_coords; + mutable signed char my_node_sign; + mutable double my_node_score; + + mutable const Mesh_Element * my_cached_owner; + mutable Vector3d my_cached_owner_coords; + + mutable std::vector my_children; + mutable std::vector my_sorted_node_domains; + +private: + //: copy constructor not allowed + SubElementNode(SubElementNode const & copy); +}; + +class SubElementChildNode : public SubElementNode { +public: + SubElementChildNode( const Mesh_Element * owner, + const NodeVec & parents, + const std::vector & weights ); + + virtual ~SubElementChildNode() {} + virtual void prolongate_fields(const CDMesh & mesh) const override; + virtual Vector3d compute_owner_coords( const Mesh_Element * in_owner ) const override; + + virtual NodeVec get_parents() const override { return my_parents; } + virtual size_t get_num_parents() const override { return my_parents.size(); } + virtual std::vector get_parent_weights() const override { return my_weights; } + bool needs_to_be_ale_prolonged(const CDMesh & mesh) const; + +private: + void prolong_ale_fields(const CDMesh & mesh, const ProlongationPointData * prolong_data) const; + void prolong_interpolation_fields(const CDMesh & mesh) const; + + NodeVec my_parents; + std::vector my_weights; +}; + +class SubElementSteinerNode : public SubElementChildNode { +public: + + SubElementSteinerNode( const Mesh_Element * in_owner, + const NodeVec & parents, + const std::vector & weights ) + : SubElementChildNode(in_owner, parents, weights) {} + + virtual ~SubElementSteinerNode() {} + virtual void prolongate_fields(const CDMesh & mesh) const override; + virtual Vector3d compute_owner_coords( const Mesh_Element * owner ) const override { + throw std::runtime_error("Incorrect usage of SubElementSteinerNode. The type of node only has one owner."); + } +}; + +class SubElementEdgeNode : public SubElementChildNode { +public: + SubElementEdgeNode( const Mesh_Element * owner, + const double & position, + const SubElementNode *parent1, + const SubElementNode *parent2) + : SubElementChildNode( owner, {parent1, parent2}, {1.-position, position}) {} + + virtual ~SubElementEdgeNode() {} + + double get_position() const { ThrowAssert(get_num_parents() == 2); return get_parent_weights()[1]; } + double get_position(const SubElementNode *parent1, const SubElementNode *parent2) const { ThrowAssert(check_parents(parent1,parent2)); return ((parent1==get_parents()[0]) ? get_parent_weights()[1] : get_parent_weights()[0]); } + +private: + bool check_parents(const SubElementNode *parent1, const SubElementNode *parent2) const { + return (get_num_parents() == 2 && ((parent1==get_parents()[0] && parent2==get_parents()[1]) || (parent2==get_parents()[0] && parent1==get_parents()[1]))); + } +}; + + class SubElementMidSideNode : public SubElementNode { +public: + SubElementMidSideNode() = delete; + SubElementMidSideNode( const Mesh_Element * owner, + const SubElementNode *parent1, + const SubElementNode *parent2); + SubElementMidSideNode( const Mesh_Element * owner, + const SubElementNode *parent1, + const SubElementNode *parent2, + stk::mesh::Entity meshNode, + stk::mesh::EntityId meshNodeId); + + virtual ~SubElementMidSideNode() {} + virtual void prolongate_fields(const CDMesh & mesh) const override; + virtual Vector3d compute_owner_coords( const Mesh_Element * in_owner ) const override { + return 0.5 * my_parent1->owner_coords(in_owner) + 0.5 * my_parent2->owner_coords(in_owner); + } + virtual bool is_mesh_node() const override { return my_is_mesh_node; } + + virtual NodeVec get_parents() const override { ThrowRequire(!my_is_mesh_node); return NodeVec{my_parent1,my_parent2}; } + virtual size_t get_num_parents() const override { return 2; } + virtual std::vector get_parent_weights() const override { ThrowRequire(!my_is_mesh_node); return std::vector{0.5,0.5}; } + +protected: + bool my_is_mesh_node; + const SubElementNode *my_parent1; + const SubElementNode *my_parent2; + +private: + void prolong_ale_fields(const CDMesh & mesh) const; + void prolong_interpolation_fields(const CDMesh & mesh) const; + bool is_mesh_node_that_needs_to_be_prolonged(const CDMesh & mesh) const; +}; + + +class SubElementMeshNode : public SubElementNode { +public: + + SubElementMeshNode( const Mesh_Element * owner, + stk::mesh::Entity nodeEntity, + stk::mesh::EntityId nodeEntityId, + const Vector3d & owner_coords, + const Vector3d & global_coords ); + + virtual ~SubElementMeshNode() {} + + virtual void prolongate_fields(const CDMesh & mesh) const override; + virtual Vector3d compute_owner_coords( const Mesh_Element * owner ) const override { return owner->get_node_parametric_coords(this); } + virtual bool is_mesh_node() const override { return true; } + bool needs_to_be_ale_prolonged(const CDMesh & mesh) const; +protected: + +private: + void prolong_ale_fields(const CDMesh & mesh, + const ProlongationPointData * prolong_data, + const ProlongationNodeData * old_node) const; + void prolong_interpolation_fields(const CDMesh & mesh, const ProlongationNodeData * old_node) const; + //: Default constructor not allowed + SubElementMeshNode(); +}; + +} // namespace krino + +#endif // Akri_SubElement_h diff --git a/packages/krino/krino/krino_lib/Akri_SubElementChildNodeAncestry.cpp b/packages/krino/krino/krino_lib/Akri_SubElementChildNodeAncestry.cpp new file mode 100644 index 000000000000..b592e297f5a9 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_SubElementChildNodeAncestry.cpp @@ -0,0 +1,246 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include + +namespace krino { + +SubElementChildNodeAncestry::SubElementChildNodeAncestry( stk::CommBuffer & b ) +{ + size_t ancestrySize = 0; + b.unpack(ancestrySize); + ThrowAssert(ancestrySize > 0); + + myAncestry.reserve(ancestrySize); + + size_t numParents = 0; + std::vector parentIDs; + std::vector parentWeights; + + for(unsigned index=0; indexis_mesh_node()) + { + myAncestry.emplace_back(std::vector{node->entityId()}, std::vector{0.}); + return; + } + + const NodeVec & parents = node->get_parents(); + const auto & parentWeights = node->get_parent_weights(); + std::vector parentIDs; + parentIDs.reserve(parents.size()); + + for (auto && parent : node->get_parents()) + { + if (parent->is_mesh_node()) + parentIDs.push_back(parent->entityId()); + else + parentIDs.push_back(0); + } + + myAncestry.emplace_back(parentIDs, parentWeights); + + for (auto && parent : node->get_parents()) + if (!parent->is_mesh_node()) + build_ancestry(parent); +} + +void +SubElementChildNodeAncestry::get_parent_node_keys(std::vector & parentNodeKeys) const +{ + parentNodeKeys.clear(); + for (auto&& cut : myAncestry) + for (auto && parentID : cut.myParentIDs) + if (parentID != 0) + parentNodeKeys.emplace_back(stk::topology::NODE_RANK, parentID); +} + +bool +SubElementChildNodeAncestry::is_shared(const stk::mesh::BulkData & mesh, const SubElementNode * node) +{ + if (node->is_mesh_node()) + { + return mesh.bucket(node->entity()).shared(); + } + for (auto && parent : node->get_parents()) + { + if (!is_shared(mesh, parent)) + { + return false; + } + } + return true; +} + +void +SubElementChildNodeAncestry::pack_into_buffer(stk::CommBuffer & b) const +{ + ThrowAssert(!myAncestry.empty()); + const size_t ancestrySize = myAncestry.size(); + b.pack(ancestrySize); + for (auto&& cut : myAncestry) + { + const size_t numParents = cut.myParentIDs.size(); + b.pack(numParents); + for (size_t i=0; i parents(numParents, nullptr); + + for (size_t i=0; i +SubElementChildNodeAncestry::get_constrained_node_ancestries(const std::unordered_map > & constrainedNodeMap) const +{ + std::set parentNodeIDs; + for (auto&& cut : myAncestry) + for (auto && parentID : cut.myParentIDs) + if (parentID != 0) + parentNodeIDs.insert(parentID); + + std::vector constrainedNodeAncestries; + constrainedNodeAncestries.push_back(*this); + for (auto&& parentNodeID : parentNodeIDs) + { + std::vector toBeConverted; + toBeConverted.swap(constrainedNodeAncestries); + auto it = constrainedNodeMap.find(parentNodeID); + if (it == constrainedNodeMap.end()) break; + constrainedNodeAncestries.reserve(it->second.size() * toBeConverted.size()); + for (auto&& constrainedParentNodeID : it->second) + { + for (auto&& unconverted : toBeConverted) + { + SubElementChildNodeAncestry converted = unconverted; + for (auto&& cut : converted.myAncestry) + { + for (auto && cutParentNodeID : cut.myParentIDs) + if (cutParentNodeID == parentNodeID) + cutParentNodeID = constrainedParentNodeID; + } + constrainedNodeAncestries.push_back(converted); + } + } + } + return constrainedNodeAncestries; +} + +const SubElementNode * +SubElementChildNodeAncestry::build_missing_child_nodes(CDMesh & mesh, unsigned & ancestryIndex) const +{ + ThrowAssert(ancestryIndex < myAncestry.size()); + const Cut & cut = myAncestry[ancestryIndex]; + const size_t numParents = cut.myParentIDs.size(); + std::vector parents(numParents, nullptr); + + for (size_t i=0; iget_ancestors(ancestors); + + std::vector ancestorNodes; + ancestorNodes.reserve(ancestors.size()); + for (auto&& ancestor : ancestors) + { + ThrowAssert(ancestor->entity_is_valid(mesh.stk_bulk())); + ancestorNodes.push_back(ancestor->entity()); + } + + std::vector elems; + stk::mesh::get_entities_through_relations(mesh.stk_bulk(), ancestorNodes, stk::topology::ELEMENT_RANK, elems); + + const Mesh_Element * owner = nullptr; + for(auto && elem : elems) + { + owner = mesh.find_mesh_element(mesh.stk_bulk().identifier(elem)); + if(owner != nullptr) break; + } + if(!owner) + { + // It is possible in parallel for both nodes of a parent edge to be shared with a processor, + // but that processor does not own any of the elements connected to that edge. In that case + // we do not need to build the missing edge node. + return nullptr; + } + + if (parents.size() == 2) + return mesh.create_edge_node(owner, parents[0], parents[1], cut.myParentWeights[1]); + + return mesh.create_child_internal_or_face_node(owner, parents, cut.myParentWeights); +} + +} diff --git a/packages/krino/krino/krino_lib/Akri_SubElementChildNodeAncestry.hpp b/packages/krino/krino/krino_lib/Akri_SubElementChildNodeAncestry.hpp new file mode 100644 index 000000000000..55c73d64a243 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_SubElementChildNodeAncestry.hpp @@ -0,0 +1,49 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_SubElementChildNodeAncestry_h +#define Akri_SubElementChildNodeAncestry_h + +#include +#include +#include +#include + +namespace krino +{ +class CDMesh; +class SubElementNode; + +class SubElementChildNodeAncestry { +public: + SubElementChildNodeAncestry(const SubElementNode * node) { build_ancestry(node); } + SubElementChildNodeAncestry( stk::CommBuffer & b ); + + static bool is_shared(const stk::mesh::BulkData & mesh, const SubElementNode * node); + void pack_into_buffer(stk::CommBuffer & b) const; + const SubElementNode * find_subelement_node(CDMesh & mesh) const; + const SubElementNode * find_subelement_node(CDMesh & mesh, unsigned & ancestry_index) const; + void get_parent_node_keys(std::vector & parent_node_keys) const; + void build_missing_child_nodes(CDMesh & mesh) const; + std::vector get_constrained_node_ancestries(const std::unordered_map > & constrained_node_map) const; + +private: + struct Cut { + Cut(const std::vector & parentIDs, const std::vector & parentWeights) : myParentIDs(parentIDs), myParentWeights(parentWeights) {} + std::vector myParentIDs; + std::vector myParentWeights; + }; + void build_ancestry(const SubElementNode * in_node); + const SubElementNode * build_missing_child_nodes(CDMesh & mesh, unsigned & ancestry_index) const; + + std::vector myAncestry; +}; + +} + +#endif // Akri_SubElementChildNodeAncestry_h diff --git a/packages/krino/krino/krino_lib/Akri_SubElementNodeAncestry.cpp b/packages/krino/krino/krino_lib/Akri_SubElementNodeAncestry.cpp new file mode 100644 index 000000000000..9dad99d5cf55 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_SubElementNodeAncestry.cpp @@ -0,0 +1,32 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include + +namespace krino { + +void SubElementNodeAncestry::print(std::ostream & os) const +{ + if (my_node->is_mesh_node()) + { + os << my_node->entityId(); + } + else + { + os << "{ "; + for (auto&& parent : get_parents()) + { + parent.print(os); + os << " "; + } + os << "}"; + } +} + +} diff --git a/packages/krino/krino/krino_lib/Akri_SubElementNodeAncestry.hpp b/packages/krino/krino/krino_lib/Akri_SubElementNodeAncestry.hpp new file mode 100644 index 000000000000..b36fc35423e0 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_SubElementNodeAncestry.hpp @@ -0,0 +1,65 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_SubElementNodeAncestry_h +#define Akri_SubElementNodeAncestry_h + +#include + +namespace krino { + +class SubElementNodeAncestry { +public: + SubElementNodeAncestry() = default; + SubElementNodeAncestry(const SubElementNodeAncestry & rhs) = default; + SubElementNodeAncestry(const SubElementNode * node) : my_node(node) {} + + template + static bool compare(const SubElementNodeAncestry & x, const SubElementNodeAncestry & y, const LESS & compare) + { + const std::vector xParents = x.get_parents(); + const std::vector yParents = y.get_parents(); + if (xParents.empty()) + { + if (yParents.empty()) return compare(*(x.my_node), *(y.my_node)); + else return true; + } + else if (yParents.empty()) return false; + else return xParents < yParents; + } + + void print(std::ostream & os) const; + +private: + std::vector get_parents() const + { + std::vector parents; + if (!my_node->is_mesh_node()) + { + const NodeVec nodeParents = my_node->get_parents(); + parents.reserve(nodeParents.size()); + for (auto && nodeParent : nodeParents) + parents.emplace_back(nodeParent); + std::sort(parents.begin(), parents.end()); + } + return parents; + } + + const SubElementNode * my_node = nullptr; +}; + +inline bool operator<(const SubElementNodeAncestry & x, const SubElementNodeAncestry & y) +{ + return SubElementNodeAncestry::compare(x,y,SubElementNode::less_by_entity_id); +} + +inline std::ostream & operator << (std::ostream & os, const SubElementNodeAncestry & ancestry) { ancestry.print(os); return os; } + +} + +#endif // Akri_SubElementNodeAncestry_h diff --git a/packages/krino/krino/krino_lib/Akri_Surface.cpp b/packages/krino/krino/krino_lib/Akri_Surface.cpp new file mode 100644 index 000000000000..6fc43d1f8dd7 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Surface.cpp @@ -0,0 +1,28 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include + +namespace krino{ + +void +Surface::prepare_to_compute(const double time, const BoundingBox & point_bbox, const double truncation_length) +{ + ThrowErrorMsgIf(NULL != my_transformation, + "This surface with type (" << type() + << ") has motion specified, but the prepare_to_compute() method has not been implemented yet to support motion."); +} + +BoundingBox +Surface::get_bounding_box() +{ + ThrowRuntimeError("This surface with type (" << type() << ") has not implemented get_bounding_box()."); +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_Surface.hpp b/packages/krino/krino/krino_lib/Akri_Surface.hpp new file mode 100644 index 000000000000..78019bafe845 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Surface.hpp @@ -0,0 +1,98 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Surface_h +#define Akri_Surface_h + +#include +#include +#include +#include + +namespace krino { + +class Transformation; + +enum Surface_Type +{ + POINT=0, + SPHERE, + ELLIPSOID, + CYLINDER, + COMPOSITE_SURFACE, + PLANE, + RANDOM, + FACETED_SURFACE, + // Never, ever, ever add an entry after MAX_SURFACE_TYPE. Never. + MAX_SURFACE_TYPE +}; + +class Surface; +typedef std::vector SurfaceVec; +typedef std::vector< std::unique_ptr > SurfaceAutoVec; + +// abstract class used to define a surface. + +class Surface { + +public: + Surface() { Surface::set_transformation(nullptr); } + virtual ~Surface() {} + + virtual BoundingBox get_bounding_box(); + + // pre-calculations needed to compute distance + virtual void prepare_to_compute(const double time, const BoundingBox & point_bbox, const double truncation_length); + + // compute signed distance from specific point to surface + virtual double point_signed_distance(const Vector3d &x) const = 0; + + // compute signed distance from specific point to surface + // For distances larger than truncation_length (if truncation_length > 0.), the surface is allowed to return + // far_field_value instead of the actual signed distance. + virtual double truncated_point_signed_distance(const Vector3d &x, const double truncation_length, const double far_field_value) const = 0; + // If surface does return far_field_value instead of actual signed distance, the sign may be wrong. + virtual bool truncated_distance_may_have_wrong_sign() const = 0; + + // for debugging memory usage + virtual size_t storage_size() const = 0; + + // methods related to moving surfaces (transformations) + virtual void set_transformation(Transformation * trans) { my_transformation = trans; } + Transformation * get_transformation() { return my_transformation; } + const Transformation * get_transformation() const { return my_transformation; } + + // queries + virtual Surface_Type type() const = 0; + +protected: + Transformation * my_transformation; +}; + +class SurfaceThatTakesAdvantageOfNarrowBandAndThereforeMightHaveWrongSign : public Surface { +public: + virtual bool truncated_distance_may_have_wrong_sign() const override { return true; } + virtual double point_signed_distance(const Vector3d &x) const override + { + return truncated_point_signed_distance(x, 0., 0.); + } + virtual double truncated_point_signed_distance(const Vector3d &x, const double truncation_length, const double far_field_value) const override = 0; +}; + +class SurfaceThatDoesntTakeAdvantageOfNarrowBandAndThereforeHasCorrectSign : public Surface { +public: + virtual bool truncated_distance_may_have_wrong_sign() const override { return false; } + virtual double point_signed_distance(const Vector3d &x) const override = 0; + virtual double truncated_point_signed_distance(const Vector3d &x, const double truncation_length, const double far_field_value) const override + { + return point_signed_distance(x); + } +}; +} // namespace krino + +#endif // Akri_Surface_h diff --git a/packages/krino/krino/krino_lib/Akri_Transformation.cpp b/packages/krino/krino/krino_lib/Akri_Transformation.cpp new file mode 100644 index 000000000000..d7c563f2a588 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Transformation.cpp @@ -0,0 +1,112 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include + +namespace krino{ + +void +Quaternion::set_from_rotation_vector(const Vector3d & v) +{ + // the angle theta is the length of the rotation vector omega + const double theta = v.length(); + const double real_min = 10.0*std::numeric_limits::min(); + + double coef; + if ( theta > real_min ) { + coef = std::sin(0.5*theta)/theta; + } else { + coef = 0.5; + } + + q[0] = std::cos( 0.5*theta ); + q[1] = coef*v[0]; + q[2] = coef*v[1]; + q[3] = coef*v[2]; + + const double inv_length = 1.0/std::sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]); + for (auto && val : q) val *= inv_length; +} + +Vector3d use_quaternion_to_rotate_3d_vector(const std::array & q, const Vector3d & v) +{ + return Vector3d( + ((2. * ( q[0] * q[0] + q[1] * q[1] ) - 1.) * v[0] + + (2. * ( q[1] * q[2] - q[0] * q[3] ) ) * v[1] + + (2. * ( q[1] * q[3] + q[0] * q[2] ) ) * v[2]), + ((2. * ( q[1] * q[2] + q[0] * q[3] ) ) * v[0] + + (2. * ( q[0] * q[0] + q[2] * q[2] ) - 1.) * v[1] + + (2. * ( q[2] * q[3] - q[0] * q[1] ) ) * v[2]), + ((2. * ( q[1] * q[3] - q[0] * q[2] ) ) * v[0] + + (2. * ( q[2] * q[3] + q[0] * q[1] ) ) * v[1] + + (2. * ( q[0] * q[0] + q[3] * q[3] ) - 1.) * v[2])); +} + + +Vector3d +Quaternion::rotate_3d_vector(const Vector3d & v) const +{ + return use_quaternion_to_rotate_3d_vector(q, v); +} + +Vector3d +Quaternion::reverse_rotate_3d_vector(const Vector3d & v) const +{ + return use_quaternion_to_rotate_3d_vector(std::array{q[0],-q[1],-q[2],-q[3]}, v); +} + +void +Transformation::initialize( const Vector3d & initial_displacement, const Vector3d & initial_rotation, const Vector3d & reference_point ) +{ + my_reference_point = reference_point; + my_update_orientation.set_from_rotation_vector(initial_rotation); + my_update_offset = initial_displacement - my_update_orientation.rotate_3d_vector(my_reference_point); + my_reference_point += initial_displacement; +} + +void +Transformation::initialize() +{ + // this form of initialization assumes that set_initial_displacement() and set_initial_rotation() have been called or are zero + const Vector3d initial_displacement = my_update_offset; + my_update_offset = initial_displacement - my_update_orientation.rotate_3d_vector(my_reference_point); + my_reference_point += initial_displacement; +} + +void +Transformation::update( const double time ) const +{ + if (time == my_last_update) + { + return; + } + if (my_last_update < 0.0) + { + my_last_update = time; + return; + } + + const double dt = time - my_last_update; + const Vector3d update_rotation_angle = dt*my_rotational_velocity; + my_update_orientation.set_from_rotation_vector(update_rotation_angle); + my_update_offset = my_reference_point - my_update_orientation.rotate_3d_vector(my_reference_point) + my_translational_velocity * dt; + my_reference_point += my_translational_velocity * dt; + + my_last_update = time; +} + +void +Transformation::apply( Vector3d & x0 ) const +{ + x0 = my_update_orientation.rotate_3d_vector(x0) + my_update_offset; +} + +} // namespace krino diff --git a/packages/krino/krino/krino_lib/Akri_Transformation.hpp b/packages/krino/krino/krino_lib/Akri_Transformation.hpp new file mode 100644 index 000000000000..602d3510af9a --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Transformation.hpp @@ -0,0 +1,65 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Transformation_h +#define Akri_Transformation_h + +#include +#include + +namespace krino { + +class Quaternion { + public: + Quaternion() : q{{ 1.0, 0.0, 0.0, 0.0 }} {} + + void set_from_rotation_vector(const Vector3d & v); + Vector3d rotate_3d_vector(const Vector3d & v) const; + Vector3d reverse_rotate_3d_vector(const Vector3d & v) const; + + private: + std::array q; ///< q[0] stores angle, (q[1],q[2],a[3]) stores axis. +}; + +class Transformation { +public: + Transformation() + : my_translational_velocity(Vector3d::ZERO), my_rotational_velocity(Vector3d::ZERO), + my_reference_point(Vector3d::ZERO), my_last_update(-1.0), my_update_orientation(), my_update_offset(Vector3d::ZERO) {} + virtual ~Transformation() {} + + void set_translational_velocity(const Vector3d & v) { my_translational_velocity = v; } + void set_rotational_velocity(const Vector3d & v) { my_rotational_velocity = v; } + void set_reference_point(const Vector3d & v) { my_reference_point = v; } + + // temporary storage until initialize() + void set_initial_displacement(const Vector3d & v) { my_update_offset = v; } + void set_initial_rotation(const Vector3d & v) { my_update_orientation.set_from_rotation_vector(v); } + + const Vector3d & get_translational_velocity() const { return my_translational_velocity; } + const Vector3d & get_rotational_velocity() const { return my_rotational_velocity; } + const Vector3d & get_reference_point() const { return my_reference_point; } + + void initialize( const Vector3d & initial_displacement, const Vector3d & initial_rotation, const Vector3d & reference_point ); + void initialize(); // this form assumes that set_initial_displacement() and set_initial_rotation() have been called or are zero + void update( const double time ) const; + void apply( Vector3d & x0 ) const; + +protected: + Vector3d my_translational_velocity; + Vector3d my_rotational_velocity; + mutable Vector3d my_reference_point; + mutable double my_last_update; + mutable Quaternion my_update_orientation; + mutable Vector3d my_update_offset; +}; + + +} // namespace krino + +#endif // Akri_Transformation_h diff --git a/packages/krino/krino/krino_lib/Akri_Triangle.hpp b/packages/krino/krino/krino_lib/Akri_Triangle.hpp new file mode 100644 index 000000000000..868fbe2b0a1e --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Triangle.hpp @@ -0,0 +1,169 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Triangle_h +#define Akri_Triangle_h + +#include +#include +#include +#include + +#include + +namespace krino { + +enum class ProjectionType{NODE, EDGE, FACE, NUM_PROJ_TYPE}; + +template +class Triangle3 { + public: + typedef REAL Real; + + + Triangle3() {} /// Default, all coords zero + Triangle3(const Vec &n0, const Vec &n1, const Vec &n2) : nodes{{n0, n1, n2}} {} /// Explicit set three coordinates + explicit Triangle3(const MemberInit) {/* MemberInit::NONE */} /// Special non-initialize constructor (for performance) + + Vec normal() const { return normal_dir().unit_vector(); } /// Unit vector + Vec normal_dir() const {return Cross(GetNode(1)-GetNode(0),GetNode(2)-GetNode(0)); } /// Non-unit normal (faster) + Real area() const { return 0.5*normal_dir().length(); } + + Vec ParametricToRealCoords(const Vec & Param) const { + // Avoids temporary constructions + return Vec( + GetNode(0)[0]*(1.0-Param[0]-Param[1]) + GetNode(1)[0]*Param[0] + GetNode(2)[0]*Param[1], + GetNode(0)[1]*(1.0-Param[0]-Param[1]) + GetNode(1)[1]*Param[0] + GetNode(2)[1]*Param[1], + GetNode(0)[2]*(1.0-Param[0]-Param[1]) + GetNode(1)[2]*Param[0] + GetNode(2)[2]*Param[1] + ); + } + + /// Compute the closest point on the triangle to an input point. Return the type of projection, i.e., + /// is the projected point on a node, edge, surface of the triangle. Optionally calculate parametric coordinates. + template ProjectionType ClosestPoint(const Vec& p, Vec& ClosestPt, T ParamPt = nullptr) const; + + Real DistanceSquared(const Vec& P, Vec& ParamPt) const; + Real DistanceSquared(const Vec& P) const; + + const Vec& GetNode(const int index) const { assert(index >= 0 && index < 3); return nodes[index]; } + Vec& GetNode(const int index) { assert(index >= 0 && index < 3); return nodes[index]; } + + void SetNodes(const Vec& n0, const Vec& n1, const Vec& n2) { nodes = {{n0, n1, n2}}; } // Change triangle coordinates +private: + std::array,3> nodes; +}; + +namespace detail { +template +struct assign_parametric_coords { + static void apply(const REAL & x, const REAL & y, T parametric_coords) + { + static_assert(std::is_pointer::value, "Expecting pointer"); + parametric_coords[0] = x; + parametric_coords[1] = y; + } +}; +template +struct assign_parametric_coords { + static void apply(const REAL, const REAL, std::nullptr_t) {} +}; +} + +template +inline REAL Triangle3::DistanceSquared(const Vec& P, Vec& ParamPt) const +{ + Vec ClosestPt(MemberInit::NONE); + ClosestPoint(P, ClosestPt, ParamPt.data()); + return (P-ClosestPt).length_squared(); +} + +template +inline REAL Triangle3::DistanceSquared(const Vec& P) const +{ + Vec ClosestPt(MemberInit::NONE); + ClosestPoint(P, ClosestPt); + return (P-ClosestPt).length_squared(); +} + +// Adapted from closest face projection from "Real time Collision Detection" text, highly optimized +template +template +ProjectionType Triangle3::ClosestPoint(const Vec& p, Vec& ClosestPt, T ParamPt) const +{ + const Vec& a = GetNode(0); + const Vec& b = GetNode(1); + const Vec& c = GetNode(2); + + Vec ab(b-a); + Vec ac(c-a); + Vec ap(p-a); + + Real d1(Dot(ab,ap)); + Real d2(Dot(ac,ap)); + + if(d1 <= 0.0 && d2 <= 0.0) { + ClosestPt = a; + detail::assign_parametric_coords::apply(0.0, 0.0, ParamPt); + return ProjectionType::NODE; + } + + Vec bp(p-b); + Real d3(Dot(ab,bp)); + Real d4(Dot(ac,bp)); + + if(d3 >= 0.0 && d4 <= d3) { + ClosestPt = b; + detail::assign_parametric_coords::apply(1.0, 0.0, ParamPt); + return ProjectionType::NODE; + } + + Real vc(d1*d4-d3*d2); + if(vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) { + Real v = d1/(d1-d3); + ClosestPt = a + v * ab; + detail::assign_parametric_coords::apply(v, 0.0, ParamPt); + return ProjectionType::EDGE; + } + + Vec cp(p-c); + Real d5(Dot(ab,cp)); + Real d6(Dot(ac,cp)); + if(d6 >= 0.0 && d5 <= d6) { + ClosestPt = c; + detail::assign_parametric_coords::apply(0.0, 1.0, ParamPt); + return ProjectionType::NODE; + } + + Real vb(d5*d2 - d1*d6); + if(vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0) { + Real w(d2/(d2-d6)); + ClosestPt = a + w * ac; + detail::assign_parametric_coords::apply(0.0, w, ParamPt); + return ProjectionType::EDGE; + } + + Real va(d3*d6 - d5*d4); + if(va <= 0.0 && (d4-d3) >= 0.0 && (d5-d6) >= 0.0) { + Real w((d4-d3)/((d4-d3) + (d5-d6))); + ClosestPt = b + w * (c-b); + detail::assign_parametric_coords::apply(1.0-w, w, ParamPt); + return ProjectionType::EDGE; + } + + Real denom(1.0/(va+vb+vc)); + Real v(vb*denom); + Real w(vc*denom); + ClosestPt = a + ab*v + ac*w; + detail::assign_parametric_coords::apply(v, w, ParamPt); + return ProjectionType::FACE; +} + +typedef Triangle3 Triangle3d; + +} +#endif // Akri_Triangle_h diff --git a/packages/krino/krino/krino_lib/Akri_TypeDefs.hpp b/packages/krino/krino/krino_lib/Akri_TypeDefs.hpp new file mode 100644 index 000000000000..3a949159289b --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_TypeDefs.hpp @@ -0,0 +1,27 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef SIERRA_Akri_TypeDefs_h +#define SIERRA_Akri_TypeDefs_h + +#include +#include +#include + +namespace krino { + + class NINT{}; + class NPE_VAR{}; + class NPE_COORD{}; + class DIM{}; + + typedef std::vector< Vector3d > PointVec; + +} + +#endif // SIERRA_Akri_TypeDefs_h diff --git a/packages/krino/krino/krino_lib/Akri_Utility.hpp b/packages/krino/krino/krino_lib/Akri_Utility.hpp new file mode 100644 index 000000000000..a8a7e7b69aa2 --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Utility.hpp @@ -0,0 +1,66 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef SIERRA_Akri_Utility_h +#define SIERRA_Akri_Utility_h + +#include +#include +#include +#include + +namespace krino { +namespace utility { + + inline bool sign_change( double f1, double f2 ) { + return ( (f1 < 0.) ? (f2 >= 0.) : (f2 < 0.) ); // GOMA sign convention + //return ( (f1 > 0.) ? (f2 <= 0.) : (f2 > 0.) ); // Marching cubes sign convention + } + + inline int sign( double f ) { + return ( (f < 0.) ? -1 : 1 ); // GOMA sign convention + //return ( (f > 0.) ? 1 : -1 ); // Marching cubes sign convention + } + + inline bool is_less(double f1, double f2, double tol) { return (f2-f1 > tol*(std::fabs(f1)+std::fabs(f2))); } + inline bool is_more(double f1, double f2, double tol) { return (is_less(f2,f1,tol)); } + inline bool is_not_equal(double f1, double f2, double tol) { return (is_less(f1,f2,tol) || is_less(f2,f1,tol)); } + inline bool is_equal(double f1, double f2, double tol) { return (!is_less(f1,f2,tol) && !is_less(f2,f1,tol)); } + + inline bool is_less(double f1, double f2) { return is_less(f1,f2,100.0*std::numeric_limits::epsilon()); } + inline bool is_more(double f1, double f2) { return is_more(f1,f2,100.0*std::numeric_limits::epsilon()); } + inline bool is_not_equal(double f1, double f2) { return is_not_equal(f1,f2,100.0*std::numeric_limits::epsilon()); } + inline bool is_equal(double f1, double f2) { return is_equal(f1,f2,100.0*std::numeric_limits::epsilon()); } + + inline bool is_less(double f1, int i1, double f2, int i2, double tol) { return is_less(f1,f2,tol) ? true : (is_more(f1,f2,tol) ? false : (i1::epsilon()); } + inline bool is_more(double f1, int i1, double f2, int i2) { return is_more(f1,i1,f2,i2,100.0*std::numeric_limits::epsilon()); } + + template + inline void + free_all(std::vector & vec) {std::vector empty_vec; vec.swap(empty_vec);} + + template + inline size_t + storage_size(const std::vector & vec) {return (sizeof(std::vector) + vec.size()*sizeof(T*)); } + + // linear cost, requires T::storage_size + template + inline size_t + storage_size(const std::vector> & autovec) + { + size_t store_size = sizeof(std::vector>) + autovec.size()*sizeof(std::unique_ptr); + for (auto && entry : autovec) store_size += entry->storage_size(); + return store_size; + } + +} // namespace utility +} // namespace krino + +#endif // SIERRA_Akri_Utility_h diff --git a/packages/krino/krino/krino_lib/Akri_Vec.cpp b/packages/krino/krino/krino_lib/Akri_Vec.cpp new file mode 100644 index 000000000000..fa4faa7d9e1c --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Vec.cpp @@ -0,0 +1,38 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +namespace krino { + +static bool float_less(double a, double b) +{ + return static_cast(a) < static_cast(b); +} + +bool is_less_than_in_x_then_y_then_z(const Vector3d& A, const Vector3d &B) +{ + if (float_less(A[0], B[0])) + return true; + else if (float_less(B[0], A[0])) + return false; + + if (float_less(A[1], B[1])) + return true; + else if (float_less(B[1], A[1])) + return false; + + if (float_less(A[2], B[2])) + return true; + else if (float_less(B[2], A[2])) + return false; + + return false; +} + +} diff --git a/packages/krino/krino/krino_lib/Akri_Vec.hpp b/packages/krino/krino/krino_lib/Akri_Vec.hpp new file mode 100644 index 000000000000..e1213d9bb77b --- /dev/null +++ b/packages/krino/krino/krino_lib/Akri_Vec.hpp @@ -0,0 +1,26 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Vec_h +#define Akri_Vec_h + +#include + +namespace krino { + +using stk::math::MemberInit; +using stk::math::Vec; +typedef stk::math::Vec Vector3d; +typedef stk::math::Vec Vector2d; +typedef stk::math::Vec Float3d; + +bool is_less_than_in_x_then_y_then_z(const Vector3d& A, const Vector3d &B); + +} // namespace krino + +#endif // Akri_Vec_h diff --git a/packages/krino/krino/krino_lib/CMakeLists.txt b/packages/krino/krino/krino_lib/CMakeLists.txt new file mode 100644 index 000000000000..ac224f4eba62 --- /dev/null +++ b/packages/krino/krino/krino_lib/CMakeLists.txt @@ -0,0 +1,19 @@ +SET(HEADERS "") +SET(SOURCES "") + +INCLUDE_DIRECTORIES(${${PACKAGE_NAME}_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +FILE(GLOB HEADERS *.hpp) +FILE(GLOB SOURCES *.cpp) + +TRIBITS_ADD_LIBRARY( + krino_lib + HEADERS ${HEADERS} + SOURCES ${SOURCES} + DEPLIBS krino_master_element_lib) + +INSTALL(FILES ${HEADERS} DESTINATION + ${CMAKE_INSTALL_PREFIX}/${${PROJECT_NAME}_INSTALL_INCLUDE_DIR}/krino_lib) + diff --git a/packages/krino/krino/master_element/Akri_MasterElement.hpp b/packages/krino/krino/master_element/Akri_MasterElement.hpp new file mode 100644 index 000000000000..5749c4a6ca5d --- /dev/null +++ b/packages/krino/krino/master_element/Akri_MasterElement.hpp @@ -0,0 +1,21 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_MasterElement_h +#define Akri_MasterElement_h + +#include + +namespace krino { + +// Set actual MasterElement type we will use +typedef MasterElementHybrid MasterElement; + +} // end namespace krino + +#endif // Akri_MasterElement_h diff --git a/packages/krino/krino/master_element/Akri_MasterElementBasis.hpp b/packages/krino/krino/master_element/Akri_MasterElementBasis.hpp new file mode 100644 index 000000000000..39cb2743a920 --- /dev/null +++ b/packages/krino/krino/master_element/Akri_MasterElementBasis.hpp @@ -0,0 +1,701 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_MasterElementBasis_h +#define Akri_MasterElementBasis_h + +#define ATTR_RESTRICT __restrict__ + +namespace krino { + +class Basis +{ +public: + Basis(unsigned deg) : my_degree(deg) {} + + virtual ~Basis() {} + virtual void nodal_parametric_coordinates(double* ATTR_RESTRICT p_coords) const = 0; + virtual void shape_fcn(const int nint, const double* p_coords, double* result) const = 0; + virtual void shape_fcn_deriv(const int nint, const double* p_coords, double* result ) const = 0; + unsigned degree() const { return my_degree; } + +private: + unsigned my_degree; +}; + +class Basis_LINE_2 : public Basis +{ +public: + Basis_LINE_2() : Basis(1) {} + + void nodal_parametric_coordinates(double* ATTR_RESTRICT p_coords) const + { + p_coords[0] = -1.0; + p_coords[1] = 1.0; + } + void shape_fcn(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for (int ip(0); ip < nint; ++ip) + { + const double x = p_coords[ip]; + result[ip*2 + 0] = ( 1.0 - x ) * 0.5; + result[ip*2 + 1] = ( 1.0 + x ) * 0.5; + } + } + void shape_fcn_deriv(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for ( int ip(0); ip < nint; ++ip ) + { + result[ip*2 + 0] = -0.5; + result[ip*2 + 1] = 0.5; + } + } +}; + +class Basis_LINE_3 : public Basis +{ +public: + Basis_LINE_3() : Basis(2) {} + + void nodal_parametric_coordinates(double* ATTR_RESTRICT p_coords) const + { + p_coords[0] = -1.0; + p_coords[1] = 1.0; + p_coords[2] = 0.0; + } + void shape_fcn(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for (int ip(0); ip < nint; ++ip) + { + const double x = p_coords[ip]; + result[ip*3 + 0] = -x * ( 1.0 - x ) * 0.5; + result[ip*3 + 1] = x * ( 1.0 + x ) * 0.5; + result[ip*3 + 2] = ( 1.0 - x ) * ( 1.0 + x ); + } + } + void shape_fcn_deriv(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for ( int ip(0); ip < nint; ++ip ) + { + const double x = p_coords[ip]; + result[ip*3 + 0] = x; + result[ip*3 + 1] = x; + result[ip*3 + 2] = -2.0*x; + } + } +}; + +class Basis_TRI_3 : public Basis +{ +public: + Basis_TRI_3() : Basis(1) {} + + void nodal_parametric_coordinates(double* ATTR_RESTRICT p_coords) const + { + p_coords[0] = 0.0; p_coords[1] = 0.0; + p_coords[2] = 1.0; p_coords[3] = 0.0; + p_coords[4] = 0.0; p_coords[5] = 1.0; + } + void shape_fcn(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for (int ip(0); ip < nint; ++ip) + { + const double x = p_coords[2*ip + 0]; + const double y = p_coords[2*ip + 1]; + result[ip*3 + 0] = 1.0 - x - y; + result[ip*3 + 1] = x; + result[ip*3 + 2] = y; + } + } + void shape_fcn_deriv(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for ( int ip(0); ip < nint; ++ip ) + { + result[ip*6 + 0] = -1.0; + result[ip*6 + 1] = -1.0; + result[ip*6 + 2] = 1.0; + result[ip*6 + 3] = 0.0; + result[ip*6 + 4] = 0.0; + result[ip*6 + 5] = 1.0; + } + } +}; + +class Basis_TRI_6 : public Basis +{ +public: + Basis_TRI_6() : Basis(2) {} + + void nodal_parametric_coordinates(double* ATTR_RESTRICT p_coords) const + { + p_coords[ 0] = 0.0; p_coords[ 1] = 0.0; + p_coords[ 2] = 1.0; p_coords[ 3] = 0.0; + p_coords[ 4] = 0.0; p_coords[ 5] = 1.0; + p_coords[ 6] = 0.5; p_coords[ 7] = 0.0; + p_coords[ 8] = 0.5; p_coords[ 9] = 0.5; + p_coords[10] = 0.0; p_coords[11] = 0.5; + } + void shape_fcn(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for (int ip(0); ip < nint; ++ip) + { + const double x = p_coords[2*ip + 0]; + const double y = p_coords[2*ip + 1]; + result[ip*6 + 0] = (x + y - 1.0)*(2.0*x + 2.0*y - 1.0); + result[ip*6 + 1] = x*(2.0*x - 1.0); + result[ip*6 + 2] = y*(2.0*y - 1.0); + result[ip*6 + 3] = -4.0*x*(x + y - 1.0); + result[ip*6 + 4] = 4.0*x*y; + result[ip*6 + 5] = -4.0*y*(x + y - 1.0); + } + } + void shape_fcn_deriv(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for ( int ip(0); ip < nint; ++ip ) + { + const double x = p_coords[2*ip + 0]; + const double y = p_coords[2*ip + 1]; + result[ip*12 + 0] = 4.0*x + 4.0*y - 3.0; + result[ip*12 + 1] = 4.0*x + 4.0*y - 3.0; + result[ip*12 + 2] = 4.0*x - 1.0; + result[ip*12 + 3] = 0.0; + result[ip*12 + 4] = 0.0; + result[ip*12 + 5] = 4.0*y - 1.0; + result[ip*12 + 6] = -4.0*(2.0*x + y - 1.0); + result[ip*12 + 7] = -4.0*x; + result[ip*12 + 8] = 4.0*y; + result[ip*12 + 9] = 4.0*x; + result[ip*12 + 10] = -4.0*y; + result[ip*12 + 11] = -4.0*(x + 2.0*y - 1.0); + } + } +}; + +class Basis_QUAD_4 : public Basis +{ +public: + Basis_QUAD_4() : Basis(1) {} + + void nodal_parametric_coordinates(double* ATTR_RESTRICT p_coords) const + { + p_coords[ 0] = -1.0; p_coords[ 1] = -1.0; + p_coords[ 2] = 1.0; p_coords[ 3] = -1.0; + p_coords[ 4] = 1.0; p_coords[ 5] = 1.0; + p_coords[ 6] = -1.0; p_coords[ 7] = 1.0; + } + void shape_fcn(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for (int ip(0); ip < nint; ++ip) + { + const double x = p_coords[2*ip + 0]; + const double y = p_coords[2*ip + 1]; + result[ip*4 + 0] = 0.25*(1.0 - x)*(1.0 - y); + result[ip*4 + 1] = 0.25*(1.0 + x)*(1.0 - y); + result[ip*4 + 2] = 0.25*(1.0 + x)*(1.0 + y); + result[ip*4 + 3] = 0.25*(1.0 - x)*(1.0 + y); + } + } + void shape_fcn_deriv(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for ( int ip(0); ip < nint; ++ip ) + { + const double x = p_coords[2*ip + 0]; + const double y = p_coords[2*ip + 1]; + result[ip*8 + 0] = -0.25*(1.0 - y); + result[ip*8 + 1] = -0.25*(1.0 - x); + result[ip*8 + 2] = 0.25*(1.0 - y); + result[ip*8 + 3] = -0.25*(1.0 + x); + result[ip*8 + 4] = 0.25*(1.0 + y); + result[ip*8 + 5] = 0.25*(1.0 + x); + result[ip*8 + 6] = -0.25*(1.0 + y); + result[ip*8 + 7] = 0.25*(1.0 - x); + } + } +}; + +class Basis_QUAD_9 : public Basis +{ +public: + Basis_QUAD_9() : Basis(2) {} + + void nodal_parametric_coordinates(double* ATTR_RESTRICT p_coords) const + { + p_coords[ 0] = -1.0; p_coords[ 1] = -1.0; + p_coords[ 2] = 1.0; p_coords[ 3] = -1.0; + p_coords[ 4] = 1.0; p_coords[ 5] = 1.0; + p_coords[ 6] = -1.0; p_coords[ 7] = 1.0; + p_coords[ 8] = 0.0; p_coords[ 9] = -1.0; + p_coords[10] = 1.0; p_coords[11] = 0.0; + p_coords[12] = 0.0; p_coords[13] = 1.0; + p_coords[14] = -1.0; p_coords[15] = 0.0; + p_coords[16] = 0.0; p_coords[17] = 0.0; + } + void shape_fcn(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for (int ip(0); ip < nint; ++ip) + { + const double x = p_coords[2*ip + 0]; + const double y = p_coords[2*ip + 1]; + result[ip*9 + 0] = 0.25*x*(x - 1.0)*y*(y - 1.0); + result[ip*9 + 1] = 0.25*x*(x + 1.0)*y*(y - 1.0); + result[ip*9 + 2] = 0.25*x*(x + 1.0)*y*(y + 1.0); + result[ip*9 + 3] = 0.25*x*(x - 1.0)*y*(y + 1.0); + result[ip*9 + 4] = 0.5*(1.0 - x)*(1.0 + x)*y*(y - 1.0); + result[ip*9 + 5] = 0.5*x*(x + 1.0)*(1.0 - y)*(1.0 + y); + result[ip*9 + 6] = 0.5*(1.0 - x)*(1.0 + x)*y*(y + 1.0); + result[ip*9 + 7] = 0.5*x*(x - 1.0)*(1.0 - y)*(1.0 + y); + result[ip*9 + 8] = (1.0 - x)*(1.0 + x)*(1.0 - y)*(1.0 + y); + } + } + void shape_fcn_deriv(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for ( int ip(0); ip < nint; ++ip ) + { + const double x = p_coords[2*ip + 0]; + const double y = p_coords[2*ip + 1]; + result[ip*18 + 0] = (-0.25 + 0.5*x)*(-1. + y)*y; + result[ip*18 + 1] = (-1.0 + x)*x*(-0.25 + 0.5*y); + result[ip*18 + 2] = (0.25 + 0.5*x)*(-1. + y)*y; + result[ip*18 + 3] = x*(1. + x)*(-0.25 + 0.5*y); + result[ip*18 + 4] = (0.25 + 0.5*x)*y*(1. + y); + result[ip*18 + 5] = x*(1. + x)*(0.25 + 0.5*y); + result[ip*18 + 6] = (-0.25 + 0.5*x)*y*(1. + y); + result[ip*18 + 7] = (-1. + x)*x*(0.25 + 0.5*y); + result[ip*18 + 8] = x*(1.0 - y)*y; + result[ip*18 + 9] = 0.5*(1.0 - x)*(1.0 + x)*(-1.0 + 2.0*y); + result[ip*18 + 10] = 0.5*(1.0 - y)*(1.0 + y)*(1.0 + 2.0*x); + result[ip*18 + 11] = -x*(1.0 + x)*y; + result[ip*18 + 12] = -y*(1.0 + y)*x; + result[ip*18 + 13] = 0.5*(1.0 - x)*(1.0 + x)*(1.0 + 2.0*y); + result[ip*18 + 14] = 0.5*(1.0 - y)*(1.0+ y)*(-1.0 + 2.0*x); + result[ip*18 + 15] = (1.0 - x)*x*y; + result[ip*18 + 16] = -2.0*(1.0 - y)*(1.0 + y)*x; + result[ip*18 + 17] = -2.0*(1.0 - x)*(1.0 + x)*y; + } + } +}; + +class Basis_TET_4 : public Basis +{ +public: + Basis_TET_4() : Basis(1) {} + + void nodal_parametric_coordinates(double* ATTR_RESTRICT p_coords) const + { + p_coords[ 0] = 0.0; p_coords[ 1] = 0.0; p_coords[ 2] = 0.0; + p_coords[ 3] = 1.0; p_coords[ 4] = 0.0; p_coords[ 5] = 0.0; + p_coords[ 6] = 0.0; p_coords[ 7] = 1.0; p_coords[ 8] = 0.0; + p_coords[ 9] = 0.0; p_coords[10] = 0.0; p_coords[11] = 1.0; + } + void shape_fcn(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for (int ip(0); ip < nint; ++ip) + { + const double x = p_coords[3*ip + 0]; + const double y = p_coords[3*ip + 1]; + const double z = p_coords[3*ip + 2]; + result[ip*4 + 0] = 1.0 - x - y - z; + result[ip*4 + 1] = x; + result[ip*4 + 2] = y; + result[ip*4 + 3] = z; + } + } + void shape_fcn_deriv(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for ( int ip(0); ip < nint; ++ip ) + { + result[ip*12 + 0] = -1.0; + result[ip*12 + 1] = -1.0; + result[ip*12 + 2] = -1.0; + result[ip*12 + 3] = 1.0; + result[ip*12 + 4] = 0.0; + result[ip*12 + 5] = 0.0; + result[ip*12 + 6] = 0.0; + result[ip*12 + 7] = 1.0; + result[ip*12 + 8] = 0.0; + result[ip*12 + 9] = 0.0; + result[ip*12 + 10] = 0.0; + result[ip*12 + 11] = 1.0; + } + } +}; + +class Basis_TET_10 : public Basis +{ +public: + Basis_TET_10() : Basis(2) {} + + void nodal_parametric_coordinates(double* ATTR_RESTRICT p_coords) const + { + p_coords[ 0] = 0.0; p_coords[ 1] = 0.0; p_coords[ 2] = 0.0; + p_coords[ 3] = 1.0; p_coords[ 4] = 0.0; p_coords[ 5] = 0.0; + p_coords[ 6] = 0.0; p_coords[ 7] = 1.0; p_coords[ 8] = 0.0; + p_coords[ 9] = 0.0; p_coords[10] = 0.0; p_coords[11] = 1.0; + p_coords[12] = 0.5; p_coords[13] = 0.0; p_coords[14] = 0.0; + p_coords[15] = 0.5; p_coords[16] = 0.5; p_coords[17] = 0.0; + p_coords[18] = 0.0; p_coords[19] = 0.5; p_coords[20] = 0.0; + p_coords[21] = 0.0; p_coords[22] = 0.0; p_coords[23] = 0.5; + p_coords[24] = 0.5; p_coords[25] = 0.0; p_coords[26] = 0.5; + p_coords[27] = 0.0; p_coords[28] = 0.5; p_coords[29] = 0.5; + } + void shape_fcn(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for (int ip(0); ip < nint; ++ip) + { + const double x = p_coords[3*ip + 0]; + const double y = p_coords[3*ip + 1]; + const double z = p_coords[3*ip + 2]; + result[ip*10 + 0] = (-1. + x + y + z)*(-1. + 2.*x + 2.*y + 2.*z); + result[ip*10 + 1] = x*(-1. + 2.*x); + result[ip*10 + 2] = y*(-1. + 2.*y); + result[ip*10 + 3] = z*(-1. + 2.*z); + result[ip*10 + 4] = -4.*x*(-1. + x + y + z); + result[ip*10 + 5] = 4.*x*y; + result[ip*10 + 6] = -4.*y*(-1. + x + y + z); + result[ip*10 + 7] = -4.*z*(-1. + x + y + z); + result[ip*10 + 8] = 4.*x*z; + result[ip*10 + 9] = 4.*y*z; + } + } + void shape_fcn_deriv(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for ( int ip(0); ip < nint; ++ip ) + { + const double x = p_coords[3*ip + 0]; + const double y = p_coords[3*ip + 1]; + const double z = p_coords[3*ip + 2]; + result[ip*30 + 0] = -3.+ 4.*x + 4.*y + 4.*z; + result[ip*30 + 1] = -3.+ 4.*x + 4.*y + 4.*z; + result[ip*30 + 2] = -3.+ 4.*x + 4.*y + 4.*z; + result[ip*30 + 3] = -1.+ 4.*x; + result[ip*30 + 4] = 0.; + result[ip*30 + 5] = 0.; + result[ip*30 + 6] = 0.; + result[ip*30 + 7] = -1.+ 4.*y; + result[ip*30 + 8] = 0.; + result[ip*30 + 9] = 0.; + result[ip*30 + 10] = 0.; + result[ip*30 + 11] = -1.+ 4.*z; + result[ip*30 + 12] = -4.*(-1.+ 2*x + y + z); + result[ip*30 + 13] = -4.*x; + result[ip*30 + 14] = -4.*x; + result[ip*30 + 15] = 4.*y; + result[ip*30 + 16] = 4.*x; + result[ip*30 + 17] = 0.; + result[ip*30 + 18] = -4.*y; + result[ip*30 + 19] = -4.*(-1.+ x + 2*y + z); + result[ip*30 + 20] = -4.*y; + result[ip*30 + 21] = -4.*z; + result[ip*30 + 22] = -4.*z; + result[ip*30 + 23] = -4.*(-1.+ x + y + 2*z); + result[ip*30 + 24] = 4.*z; + result[ip*30 + 25] = 0.; + result[ip*30 + 26] = 4.*x; + result[ip*30 + 27] = 0.; + result[ip*30 + 28] = 4.*z; + result[ip*30 + 29] = 4.*y; + } + } +}; + +class Basis_HEX_8 : public Basis +{ +public: + Basis_HEX_8() : Basis(1) {} + + void nodal_parametric_coordinates(double* ATTR_RESTRICT p_coords) const + { + p_coords[ 0] = -1.0; p_coords[ 1] = -1.0; p_coords[ 2] = -1.0; + p_coords[ 3] = 1.0; p_coords[ 4] = -1.0; p_coords[ 5] = -1.0; + p_coords[ 6] = 1.0; p_coords[ 7] = 1.0; p_coords[ 8] = -1.0; + p_coords[ 9] = -1.0; p_coords[10] = 1.0; p_coords[11] = -1.0; + p_coords[12] = -1.0; p_coords[13] = -1.0; p_coords[14] = 1.0; + p_coords[15] = 1.0; p_coords[16] = -1.0; p_coords[17] = 1.0; + p_coords[18] = 1.0; p_coords[19] = 1.0; p_coords[20] = 1.0; + p_coords[21] = -1.0; p_coords[22] = 1.0; p_coords[23] = 1.0; + } + void shape_fcn(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for (int ip(0); ip < nint; ++ip) + { + const double x = p_coords[3*ip + 0]; + const double y = p_coords[3*ip + 1]; + const double z = p_coords[3*ip + 2]; + result[ip*8 + 0] = 0.125*(1.0 - x)*(1.0 - y)*(1.0 - z); + result[ip*8 + 1] = 0.125*(1.0 + x)*(1.0 - y)*(1.0 - z); + result[ip*8 + 2] = 0.125*(1.0 + x)*(1.0 + y)*(1.0 - z); + result[ip*8 + 3] = 0.125*(1.0 - x)*(1.0 + y)*(1.0 - z); + result[ip*8 + 4] = 0.125*(1.0 - x)*(1.0 - y)*(1.0 + z); + result[ip*8 + 5] = 0.125*(1.0 + x)*(1.0 - y)*(1.0 + z); + result[ip*8 + 6] = 0.125*(1.0 + x)*(1.0 + y)*(1.0 + z); + result[ip*8 + 7] = 0.125*(1.0 - x)*(1.0 + y)*(1.0 + z); + } + } + void shape_fcn_deriv(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for ( int ip(0); ip < nint; ++ip ) + { + const double x = p_coords[3*ip + 0]; + const double y = p_coords[3*ip + 1]; + const double z = p_coords[3*ip + 2]; + result[ip*24 + 0] = -(1.0 - y)*(1.0 - z)*0.125; + result[ip*24 + 1] = -(1.0 - x)*(1.0 - z)*0.125; + result[ip*24 + 2] = -(1.0 - x)*(1.0 - y)*0.125; + result[ip*24 + 3] = (1.0 - y)*(1.0 - z)*0.125; + result[ip*24 + 4] = -(1.0 + x)*(1.0 - z)*0.125; + result[ip*24 + 5] = -(1.0 + x)*(1.0 - y)*0.125; + result[ip*24 + 6] = (1.0 + y)*(1.0 - z)*0.125; + result[ip*24 + 7] = (1.0 + x)*(1.0 - z)*0.125; + result[ip*24 + 8] = -(1.0 + x)*(1.0 + y)*0.125; + result[ip*24 + 9] = -(1.0 + y)*(1.0 - z)*0.125; + result[ip*24 + 10] = (1.0 - x)*(1.0 - z)*0.125; + result[ip*24 + 11] = -(1.0 - x)*(1.0 + y)*0.125; + result[ip*24 + 12] = -(1.0 - y)*(1.0 + z)*0.125; + result[ip*24 + 13] = -(1.0 - x)*(1.0 + z)*0.125; + result[ip*24 + 14] = (1.0 - x)*(1.0 - y)*0.125; + result[ip*24 + 15] = (1.0 - y)*(1.0 + z)*0.125; + result[ip*24 + 16] = -(1.0 + x)*(1.0 + z)*0.125; + result[ip*24 + 17] = (1.0 + x)*(1.0 - y)*0.125; + result[ip*24 + 18] = (1.0 + y)*(1.0 + z)*0.125; + result[ip*24 + 19] = (1.0 + x)*(1.0 + z)*0.125; + result[ip*24 + 20] = (1.0 + x)*(1.0 + y)*0.125; + result[ip*24 + 21] = -(1.0 + y)*(1.0 + z)*0.125; + result[ip*24 + 22] = (1.0 - x)*(1.0 + z)*0.125; + result[ip*24 + 23] = (1.0 - x)*(1.0 + y)*0.125; + } + } +}; + +class Basis_HEX_27 : public Basis +{ +public: + Basis_HEX_27() : Basis(1) {} + + void nodal_parametric_coordinates(double* ATTR_RESTRICT p_coords) const + { + p_coords[ 0] = -1.0; p_coords[ 1] = -1.0; p_coords[ 2] = -1.0; + p_coords[ 3] = 1.0; p_coords[ 4] = -1.0; p_coords[ 5] = -1.0; + p_coords[ 6] = 1.0; p_coords[ 7] = 1.0; p_coords[ 8] = -1.0; + p_coords[ 9] = -1.0; p_coords[10] = 1.0; p_coords[11] = -1.0; + p_coords[12] = -1.0; p_coords[13] = -1.0; p_coords[14] = 1.0; + p_coords[15] = 1.0; p_coords[16] = -1.0; p_coords[17] = 1.0; + p_coords[18] = 1.0; p_coords[19] = 1.0; p_coords[20] = 1.0; + p_coords[21] = -1.0; p_coords[22] = 1.0; p_coords[23] = 1.0; + p_coords[24] = 0.0; p_coords[25] = -1.0; p_coords[26] = -1.0; + p_coords[27] = 1.0; p_coords[28] = 0.0; p_coords[29] = -1.0; + p_coords[30] = 0.0; p_coords[31] = 1.0; p_coords[32] = -1.0; + p_coords[33] = -1.0; p_coords[34] = 0.0; p_coords[35] = -1.0; + p_coords[36] = -1.0; p_coords[37] = -1.0; p_coords[38] = 0.0; + p_coords[39] = 1.0; p_coords[40] = -1.0; p_coords[41] = 0.0; + p_coords[42] = 1.0; p_coords[43] = 1.0; p_coords[44] = 0.0; + p_coords[45] = -1.0; p_coords[46] = 1.0; p_coords[47] = 0.0; + p_coords[48] = 0.0; p_coords[49] = -1.0; p_coords[50] = 1.0; + p_coords[51] = 1.0; p_coords[52] = 0.0; p_coords[53] = 1.0; + p_coords[54] = 0.0; p_coords[55] = 1.0; p_coords[56] = 1.0; + p_coords[57] = -1.0; p_coords[58] = 0.0; p_coords[59] = 1.0; + p_coords[60] = 0.0; p_coords[61] = 0.0; p_coords[62] = 0.0; + p_coords[63] = 0.0; p_coords[64] = 0.0; p_coords[65] = -1.0; + p_coords[66] = 0.0; p_coords[67] = 0.0; p_coords[68] = 1.0; + p_coords[69] = -1.0; p_coords[70] = 0.0; p_coords[71] = 0.0; + p_coords[72] = 1.0; p_coords[73] = 0.0; p_coords[74] = 0.0; + p_coords[75] = 0.0; p_coords[76] = -1.0; p_coords[77] = 0.0; + p_coords[78] = 0.0; p_coords[79] = 1.0; p_coords[80] = 0.0; + } + void shape_fcn(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for (int ip(0); ip < nint; ++ip) + { + const double x = p_coords[3*ip + 0]; + const double y = p_coords[3*ip + 1]; + const double z = p_coords[3*ip + 2]; + result[ip*27 + 0] = 0.125*(-1. + x)*x*(-1. + y)*y*(-1. + z)*z; + result[ip*27 + 1] = 0.125*x*(1.+ x)*(-1. + y)*y*(-1. + z)*z; + result[ip*27 + 2] = 0.125*x*(1.+ x)*y*(1.+ y)*(-1. + z)*z; + result[ip*27 + 3] = 0.125*(-1. + x)*x*y*(1.+ y)*(-1. + z)*z; + result[ip*27 + 4] = 0.125*(-1. + x)*x*(-1. + y)*y*z*(1.+ z); + result[ip*27 + 5] = 0.125*x*(1.+ x)*(-1. + y)*y*z*(1.+ z); + result[ip*27 + 6] = 0.125*x*(1.+ x)*y*(1.+ y)*z*(1.+ z); + result[ip*27 + 7] = 0.125*(-1. + x)*x*y*(1.+ y)*z*(1.+ z); + result[ip*27 + 8] = 0.25*(1. - x)*(1. + x)*(-1. + y)*y*(-1. + z)*z; + result[ip*27 + 9] = 0.25*x*(1.+ x)*(1. - y)*(1. + y)*(-1. + z)*z; + result[ip*27 + 10] = 0.25*(1. - x)*(1. + x)*y*(1.+ y)*(-1. + z)*z; + result[ip*27 + 11] = 0.25*(-1. + x)*x*(1. - y)*(1. + y)*(-1. + z)*z; + result[ip*27 + 12] = 0.25*(-1. + x)*x*(-1. + y)*y*(1. - z)*(1. + z); + result[ip*27 + 13] = 0.25*x*(1.+ x)*(-1. + y)*y*(1. - z)*(1. + z); + result[ip*27 + 14] = 0.25*x*(1.+ x)*y*(1.+ y)*(1. - z)*(1. + z); + result[ip*27 + 15] = 0.25*(-1. + x)*x*y*(1.+ y)*(1. - z)*(1. + z); + result[ip*27 + 16] = 0.25*(1. - x)*(1. + x)*(-1. + y)*y*z*(1.+ z); + result[ip*27 + 17] = 0.25*x*(1.+ x)*(1. - y)*(1. + y)*z*(1.+ z); + result[ip*27 + 18] = 0.25*(1. - x)*(1. + x)*y*(1.+ y)*z*(1.+ z); + result[ip*27 + 19] = 0.25*(-1. + x)*x*(1. - y)*(1. + y)*z*(1.+ z); + result[ip*27 + 20] = (1. - x)*(1. + x)*(1. - y)*(1. + y)*(1. - z)*(1. + z); + result[ip*27 + 21] = 0.5*(1. - x)*(1. + x)*(1. - y)*(1. + y)*(-1. + z)*z; + result[ip*27 + 22] = 0.5*(1. - x)*(1. + x)*(1. - y)*(1. + y)*z*(1.+ z); + result[ip*27 + 23] = 0.5*(-1. + x)*x*(1. - y)*(1. + y)*(1. - z)*(1. + z); + result[ip*27 + 24] = 0.5*x*(1.+ x)*(1. - y)*(1. + y)*(1. - z)*(1. + z); + result[ip*27 + 25] = 0.5*(1. - x)*(1. + x)*(-1. + y)*y*(1. - z)*(1. + z); + result[ip*27 + 26] = 0.5*(1. - x)*(1. + x)*y*(1.+ y)*(1. - z)*(1. + z); + } + } + void shape_fcn_deriv(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for ( int ip(0); ip < nint; ++ip ) + { + const double x = p_coords[3*ip + 0]; + const double y = p_coords[3*ip + 1]; + const double z = p_coords[3*ip + 2]; + result[ip*81 + 0] = (-0.125 + 0.25*x)*(-1. + y)*y*(-1. + z)*z; + result[ip*81 + 1] = (-1. + x)*x*(-0.125 + 0.25*y)*(-1. + z)*z; + result[ip*81 + 2] = (-1. + x)*x*(-1. + y)*y*(-0.125 + 0.25*z); + result[ip*81 + 3] = (0.125 + 0.25*x)*(-1. + y)*y*(-1. + z)*z; + result[ip*81 + 4] = x*(1. + x)*(-0.125 + 0.25*y)*(-1. + z)*z; + result[ip*81 + 5] = x*(1. + x)*(-1. + y)*y*(-0.125 + 0.25*z); + result[ip*81 + 6] = (0.125 + 0.25*x)*y*(1. + y)*(-1. + z)*z; + result[ip*81 + 7] = x*(1. + x)*(0.125 + 0.25*y)*(-1. + z)*z; + result[ip*81 + 8] = x*(1. + x)*y*(1. + y)*(-0.125 + 0.25*z); + result[ip*81 + 9] = (-0.125 + 0.25*x)*y*(1. + y)*(-1. + z)*z; + result[ip*81 + 10] = (-1. + x)*x*(0.125 + 0.25*y)*(-1. + z)*z; + result[ip*81 + 11] = (-1. + x)*x*y*(1. + y)*(-0.125 + 0.25*z); + result[ip*81 + 12] = (-0.125 + 0.25*x)*(-1. + y)*y*z*(1. + z); + result[ip*81 + 13] = (-1. + x)*x*(-0.125 + 0.25*y)*z*(1. + z); + result[ip*81 + 14] = (-1. + x)*x*(-1. + y)*y*(0.125 + 0.25*z); + result[ip*81 + 15] = (0.125 + 0.25*x)*(-1. + y)*y*z*(1. + z); + result[ip*81 + 16] = x*(1. + x)*(-0.125 + 0.25*y)*z*(1. + z); + result[ip*81 + 17] = x*(1. + x)*(-1. + y)*y*(0.125 + 0.25*z); + result[ip*81 + 18] = (0.125 + 0.25*x)*y*(1. + y)*z*(1. + z); + result[ip*81 + 19] = x*(1. + x)*(0.125 + 0.25*y)*z*(1. + z); + result[ip*81 + 20] = x*(1. + x)*y*(1. + y)*(0.125 + 0.25*z); + result[ip*81 + 21] = (-0.125 + 0.25*x)*y*(1. + y)*z*(1. + z); + result[ip*81 + 22] = (-1. + x)*x*(0.125 + 0.25*y)*z*(1. + z); + result[ip*81 + 23] = (-1. + x)*x*y*(1. + y)*(0.125 + 0.25*z); + result[ip*81 + 24] = -0.5*x*(-1. + y)*y*(-1. + z)*z; + result[ip*81 + 25] = (1. - x)*(1. + x)*(-0.25 + 0.5*y)*(-1. + z)*z; + result[ip*81 + 26] = (1. - x)*(1. + x)*(-1. + y)*y*(-0.25 + 0.5*z); + result[ip*81 + 27] = (0.25 + 0.5*x)*(1. - y)*(1. + y)*(-1. + z)*z; + result[ip*81 + 28] = x*(1. + x)*(-0.5*y)*(-1. + z)*z; + result[ip*81 + 29] = x*(1. + x)*(1. - y)*(1. + y)*(-0.25 + 0.5*z); + result[ip*81 + 30] = -0.5*x*y*(1. + y)*(-1. + z)*z; + result[ip*81 + 31] = (1. - x)*(1. + x)*(0.25 + 0.5*y)*(-1. + z)*z; + result[ip*81 + 32] = (1. - x)*(1. + x)*y*(1. + y)*(-0.25 + 0.5*z); + result[ip*81 + 33] = (-0.25 + 0.5*x)*(1. - y)*(1. + y)*(-1. + z)*z; + result[ip*81 + 34] = (-1. + x)*x*(-0.5*y)*(-1. + z)*z; + result[ip*81 + 35] = (-1. + x)*x*(1. - y)*(1. + y)*(-0.25 + 0.5*z); + result[ip*81 + 36] = (-0.25 + 0.5*x)*(-1. + y)*y*(1. - z)*(1. + z); + result[ip*81 + 37] = (-1. + x)*x*(-0.25 + 0.5*y)*(1. - z)*(1. + z); + result[ip*81 + 38] = (-1. + x)*x*(-1. + y)*y*(-0.5*z); + result[ip*81 + 39] = (0.25 + 0.5*x)*(-1. + y)*y*(1. - z)*(1. + z); + result[ip*81 + 40] = x*(1. + x)*(-0.25 + 0.5*y)*(1. - z)*(1. + z); + result[ip*81 + 41] = x*(1. + x)*(-1. + y)*y*(-0.5*z); + result[ip*81 + 42] = (0.25 + 0.5*x)*y*(1. + y)*(1. - z)*(1. + z); + result[ip*81 + 43] = x*(1. + x)*(0.25 + 0.5*y)*(1. - z)*(1. + z); + result[ip*81 + 44] = x*(1. + x)*y*(1. + y)*(-0.5*z); + result[ip*81 + 45] = (-0.25 + 0.5*x)*y*(1. + y)*(1. - z)*(1. + z); + result[ip*81 + 46] = (-1. + x)*x*(0.25 + 0.5*y)*(1. - z)*(1. + z); + result[ip*81 + 47] = (-1. + x)*x*y*(1. + y)*(-0.5*z); + result[ip*81 + 48] = -0.5*x*(-1. + y)*y*z*(1. + z); + result[ip*81 + 49] = (1. - x)*(1. + x)*(-0.25 + 0.5*y)*z*(1. + z); + result[ip*81 + 50] = (1. - x)*(1. + x)*(-1. + y)*y*(0.25 + 0.5*z); + result[ip*81 + 51] = (0.25 + 0.5*x)*(1. - y)*(1. + y)*z*(1. + z); + result[ip*81 + 52] = x*(1. + x)*(-0.5*y)*z*(1. + z); + result[ip*81 + 53] = x*(1. + x)*(1. - y)*(1. + y)*(0.25 + 0.5*z); + result[ip*81 + 54] = -0.5*x*y*(1. + y)*z*(1. + z); + result[ip*81 + 55] = (1. - x)*(1. + x)*(0.25 + 0.5*y)*z*(1. + z); + result[ip*81 + 56] = (1. - x)*(1. + x)*y*(1. + y)*(0.25 + 0.5*z); + result[ip*81 + 57] = (-0.25 + 0.5*x)*(1. - y)*(1. + y)*z*(1. + z); + result[ip*81 + 58] = (-1. + x)*x*(-0.5*y)*z*(1. + z); + result[ip*81 + 59] = (-1. + x)*x*(1. - y)*(1. + y)*(0.25 + 0.5*z); + result[ip*81 + 60] = -2.*x*(1. - y)*(1. + y)*(1. - z)*(1. + z); + result[ip*81 + 61] = (1. - x)*(1. + x)*(-2.*y)*(1. - z)*(1. + z); + result[ip*81 + 62] = (1. - x)*(1. + x)*(1. - y)*(1. + y)*(-2.*z); + result[ip*81 + 63] = -x*(1. - y)*(1. + y)*(-1. + z)*z; + result[ip*81 + 64] = (1. - x)*(1. + x)*(-y)*(-1. + z)*z; + result[ip*81 + 65] = (1. - x)*(1. + x)*(1. - y)*(1. + y)*(-0.5 + z); + result[ip*81 + 66] = -x*(1. - y)*(1. + y)*z*(1. + z); + result[ip*81 + 67] = (1. - x)*(1. + x)*(-y)*z*(1. + z); + result[ip*81 + 68] = (1. - x)*(1. + x)*(1. - y)*(1. + y)*(0.5 + z); + result[ip*81 + 69] = (-0.5 + x)*(1. - y)*(1. + y)*(1. - z)*(1. + z); + result[ip*81 + 70] = (-1. + x)*x*(-y)*(1. - z)*(1. + z); + result[ip*81 + 71] = (-1. + x)*x*(1. - y)*(1. + y)*(-z); + result[ip*81 + 72] = (0.5 + x)*(1. - y)*(1. + y)*(1. - z)*(1. + z); + result[ip*81 + 73] = x*(1. + x)*(-y)*(1. - z)*(1. + z); + result[ip*81 + 74] = x*(1. + x)*(1. - y)*(1. + y)*(-z); + result[ip*81 + 75] = -x*(-1. + y)*y*(1. - z)*(1. + z); + result[ip*81 + 76] = (1. - x)*(1. + x)*(-0.5 + y)*(1. - z)*(1. + z); + result[ip*81 + 77] = (1. - x)*(1. + x)*(-1. + y)*y*(-z); + result[ip*81 + 78] = -x*y*(1. + y)*(1. - z)*(1. + z); + result[ip*81 + 79] = (1. - x)*(1. + x)*(0.5 + y)*(1. - z)*(1. + z); + result[ip*81 + 80] = (1. - x)*(1. + x)*y*(1. + y)*(-z); + } + } +}; + +class Basis_WEDGE_6 : public Basis +{ +public: + Basis_WEDGE_6() : Basis(1) {} + + void nodal_parametric_coordinates(double* ATTR_RESTRICT p_coords) const + { + p_coords[ 0] = 0.0; p_coords[ 1] = 0.0; p_coords[ 2] = -1.0; + p_coords[ 3] = 1.0; p_coords[ 4] = 0.0; p_coords[ 5] = -1.0; + p_coords[ 6] = 0.0; p_coords[ 7] = 1.0; p_coords[ 8] = -1.0; + p_coords[ 9] = 0.0; p_coords[10] = 0.0; p_coords[11] = 1.0; + p_coords[12] = 1.0; p_coords[13] = 0.0; p_coords[14] = 1.0; + p_coords[15] = 0.0; p_coords[16] = 1.0; p_coords[17] = 1.0; + } + void shape_fcn(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for (int ip(0); ip < nint; ++ip) + { + const double x = p_coords[3*ip + 0]; + const double y = p_coords[3*ip + 1]; + const double z = p_coords[3*ip + 2]; + const double t = 1. - x - y; + + result[ip*6 + 0] = 0.5 * t * (1.0 - z); + result[ip*6 + 1] = 0.5 * x * (1.0 - z); + result[ip*6 + 2] = 0.5 * y * (1.0 - z); + result[ip*6 + 3] = 0.5 * t * (1.0 + z); + result[ip*6 + 4] = 0.5 * x * (1.0 + z); + result[ip*6 + 5] = 0.5 * y * (1.0 + z); + } + } + void shape_fcn_deriv(const int nint, const double* ATTR_RESTRICT p_coords, double* ATTR_RESTRICT result) const + { + for ( int ip(0); ip < nint; ++ip ) + { + const double x = p_coords[3*ip + 0]; + const double y = p_coords[3*ip + 1]; + const double z = p_coords[3*ip + 2]; + const double t = 1. - x - y; + + result[ip*18 + 0] = -0.5 * (1.0 - z); + result[ip*18 + 1] = -0.5 * (1.0 - z); + result[ip*18 + 2] = -0.5 * t; + result[ip*18 + 3] = 0.5 * (1.0 - z); + result[ip*18 + 4] = 0.; + result[ip*18 + 5] = -0.5 * x; + result[ip*18 + 6] = 0.; + result[ip*18 + 7] = 0.5 * (1.0 - z); + result[ip*18 + 8] = -0.5 * y; + result[ip*18 + 9] = -0.5 * (1.0 + z); + result[ip*18 + 10] = -0.5 * (1.0 + z); + result[ip*18 + 11] = 0.5 * t; + result[ip*18 + 12] = 0.5 * (1.0 + z); + result[ip*18 + 13] = 0.; + result[ip*18 + 14] = 0.5 * x; + result[ip*18 + 15] = 0.; + result[ip*18 + 16] = 0.5 * (1.0 + z); + result[ip*18 + 17] = 0.5 * y; + } + } +}; + +} // end namespace krino + +#endif // Akri_MasterElementBasis_h diff --git a/packages/krino/krino/master_element/Akri_MasterElementCalc.cpp b/packages/krino/krino/master_element/Akri_MasterElementCalc.cpp new file mode 100644 index 000000000000..fa29448343ac --- /dev/null +++ b/packages/krino/krino/master_element/Akri_MasterElementCalc.cpp @@ -0,0 +1,456 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include + +namespace krino { + +void +MasterElementCalc::scalar_gradient( + const int nint, //: number of intg points + const int nelem, //: number of elements to process + const int ndims, + const int nnodes, + const double* gradop, //: (nvec,npe,nelem,nint) + const double* det_J, //: (nelem,nint) + const double* sfield, //: (npe,nelem) + double* vector ) //: (nvec,nelem,nint) +{ + for ( int ip(0); ip < nint; ++ip ) { + for ( int elem(0); elem < nelem; ++elem) { + for ( int dim(0); dim < ndims; ++dim ) { + double & val = vector[ip*ndims + dim]; + val = 0.0; + for ( int node(0); node < nnodes; ++node ) { + val += gradop[( (ip*nelem + elem)*nnodes + node)*ndims + dim] * sfield[ node ]; + } + } + } + } +} + +void +MasterElementCalc::determinant( + const int num_elem_dims, + const int num_coord_dims, + const int nint, + const int npe_g, + const double* deriv_g, // (num_elem_dims,npe_g,nint) + const int nelem, + const double* coords, // (num_coord_dims,npe,nelem) + double* det_J, // (nelem,nint) + double* error ) // (nelem) +{ + if (num_elem_dims != num_coord_dims) + { + if (2 == num_elem_dims) + { + ThrowAssert(3 == num_coord_dims); + MasterElementCalc::determinant_element2d_in_3d(nint, npe_g, deriv_g, nelem, coords, det_J, error); + } + else + { + ThrowAssert(1 == num_elem_dims); + if (2 == num_coord_dims) + { + MasterElementCalc::determinant_element1d_in_2d(nint, npe_g, deriv_g, nelem, coords, det_J, error); + } + else + { + MasterElementCalc::determinant_element1d_in_3d(nint, npe_g, deriv_g, nelem, coords, det_J, error); + } + } + } + else + { + ThrowAssert(num_elem_dims >= 2 && num_elem_dims <= 3); + if (2 == num_elem_dims) MasterElementCalc::determinant_2d(nint, npe_g, deriv_g, nelem, coords, det_J, error); + else MasterElementCalc::determinant_3d(nint, npe_g, deriv_g, nelem, coords, det_J, error); + } +} + +void +MasterElementCalc::gradient_operator( + const int num_elem_dims, + const int nint, + const int npe_g, + const double* deriv_g, // (nvec,npe_g,nint) + const int npe_f, + const double* deriv_f, // (nvec,npe_f,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* gradop, // (nvec,npe,nelem,nint) + double* det_J, // (nelem,nint) + double* error) +{ + ThrowAssert(num_elem_dims >= 2 && num_elem_dims <= 3); + if (2 == num_elem_dims) gradient_operator_2d(nint, npe_g, deriv_g, npe_f, deriv_f, nelem, coords, gradop, det_J, error); + else gradient_operator_3d(nint, npe_g, deriv_g, npe_f, deriv_f, nelem, coords, gradop, det_J, error); +} + +void +MasterElementCalc::determinant_2d( + const int nint, + const int npe, + const double* deriv, // (nvec,npe_g,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error) +{ + auto c2d = [coords,npe](int d, int i, int e) { return coords[d + 2*(i + npe*e)]; }; + auto d2d = [deriv,npe](int d, int n, int q) { return deriv[d + 2*(n + npe*q)]; }; + auto detj = [det_J,nelem](int e, int q) -> double& { return det_J[e+nelem*q]; }; + + for (int elem(0); elem < nelem; ++elem) error[elem] = 0.; + + for (int ke(0); ke < nelem; ++ke) { + for ( int ki(0); ki < nint; ++ki ) { + double dx_ds0 = 0.; + double dx_ds1 = 0.; + double dy_ds0 = 0.; + double dy_ds1 = 0.; + + for ( int kn(0); kn < npe; ++kn ) { + dx_ds0 += d2d(0,kn,ki)*c2d(0,kn,ke); + dx_ds1 += d2d(1,kn,ki)*c2d(0,kn,ke); + + dy_ds0 += d2d(0,kn,ki)*c2d(1,kn,ke); + dy_ds1 += d2d(1,kn,ki)*c2d(1,kn,ke); + } + + detj(ke,ki) = dx_ds0*dy_ds1 - dy_ds0*dx_ds1; + + if ( detj(ke,ki) <= 0. ) + { + error[ke] = 1.; + } + } + } +} + +void +MasterElementCalc::determinant_3d( + const int nint, + const int npe, + const double* deriv, // (nvec,npe_g,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error) +{ + auto c3d = [coords,npe](int d, int i, int e) { return coords[d + 3*(i + npe*e)]; }; + auto d3d = [deriv,npe](int d, int n, int q) { return deriv[d + 3*(n + npe*q)]; }; + auto detj = [det_J,nelem](int e, int q) -> double& { return det_J[e+nelem*q]; }; + + for (int elem(0); elem < nelem; ++elem) error[elem] = 0.; + + for (int ke(0); ke < nelem; ++ke) { + for ( int ki(0); ki < nint; ++ki ) { + double dx_ds0 = 0.; + double dx_ds1 = 0.; + double dx_ds2 = 0.; + double dy_ds0 = 0.; + double dy_ds1 = 0.; + double dy_ds2 = 0.; + double dz_ds0 = 0.; + double dz_ds1 = 0.; + double dz_ds2 = 0.; + + for ( int kn(0); kn < npe; ++kn ) { + dx_ds0 += d3d(0,kn,ki)*c3d(0,kn,ke); + dx_ds1 += d3d(1,kn,ki)*c3d(0,kn,ke); + dx_ds2 += d3d(2,kn,ki)*c3d(0,kn,ke); + + dy_ds0 += d3d(0,kn,ki)*c3d(1,kn,ke); + dy_ds1 += d3d(1,kn,ki)*c3d(1,kn,ke); + dy_ds2 += d3d(2,kn,ki)*c3d(1,kn,ke); + + dz_ds0 += d3d(0,kn,ki)*c3d(2,kn,ke); + dz_ds1 += d3d(1,kn,ki)*c3d(2,kn,ke); + dz_ds2 += d3d(2,kn,ki)*c3d(2,kn,ke); + } + + detj(ke,ki) = dx_ds0*( dy_ds1*dz_ds2 - dz_ds1*dy_ds2 ) + + dy_ds0*( dz_ds1*dx_ds2 - dx_ds1*dz_ds2 ) + + dz_ds0*( dx_ds1*dy_ds2 - dy_ds1*dx_ds2 ); + + if ( detj(ke,ki) <= 0. ) + { + error[ke] = 1.; + } + } + } +} + +void +MasterElementCalc::determinant_element2d_in_3d( + const int nint, + const int npe, + const double* deriv, // (nvec,npe_g,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error) +{ + auto c3d = [coords,npe](int d, int i, int e) { return coords[d + 3*(i + npe*e)]; }; + auto d2d = [deriv,npe](int d, int n, int q) { return deriv[d + 2*(n + npe*q)]; }; + auto detj = [det_J,nelem](int e, int q) -> double& { return det_J[e+nelem*q]; }; + + for (int elem(0); elem < nelem; ++elem) error[elem] = 0.; + + for (int ke(0); ke < nelem; ++ke) { + for ( int ki(0); ki < nint; ++ki ) { + double dx_ds0 = 0.; + double dx_ds1 = 0.; + double dy_ds0 = 0.; + double dy_ds1 = 0.; + double dz_ds0 = 0.; + double dz_ds1 = 0.; + + for ( int kn(0); kn < npe; ++kn ) { + dx_ds0 += d2d(0,kn,ki)*c3d(0,kn,ke); + dx_ds1 += d2d(1,kn,ki)*c3d(0,kn,ke); + + dy_ds0 += d2d(0,kn,ki)*c3d(1,kn,ke); + dy_ds1 += d2d(1,kn,ki)*c3d(1,kn,ke); + + dz_ds0 += d2d(0,kn,ki)*c3d(2,kn,ke); + dz_ds1 += d2d(1,kn,ki)*c3d(2,kn,ke); + } + + const double detXY = dx_ds0*dy_ds1 - dx_ds1*dy_ds0; + const double detYZ = dy_ds0*dz_ds1 - dy_ds1*dz_ds0; + const double detXZ =-dx_ds0*dz_ds1 + dx_ds1*dz_ds0; + + detj(ke,ki) = std::sqrt(detXY*detXY + detYZ*detYZ + detXZ*detXZ); + + if ( detj(ke,ki) <= 0. ) + { + error[ke] = 1.; + } + } + } +} + +void +MasterElementCalc::determinant_element1d_in_3d( + const int nint, + const int npe, + const double* deriv, // (nvec,npe_g,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error) +{ + auto c3d = [coords,npe](int d, int i, int e) { return coords[d + 3*(i + npe*e)]; }; + auto d1d = [deriv,npe](int d, int n, int q) { return deriv[d + n + npe*q]; }; + auto detj = [det_J,nelem](int e, int q) -> double& { return det_J[e+nelem*q]; }; + + for (int elem(0); elem < nelem; ++elem) error[elem] = 0.; + + for (int ke(0); ke < nelem; ++ke) { + for ( int ki(0); ki < nint; ++ki ) { + double dx_ds = 0.; + double dy_ds = 0.; + double dz_ds = 0.; + + for ( int kn(0); kn < npe; ++kn ) { + dx_ds += d1d(0,kn,ki)*c3d(0,kn,ke); + dy_ds += d1d(0,kn,ki)*c3d(1,kn,ke); + dz_ds += d1d(0,kn,ki)*c3d(2,kn,ke); + } + + detj(ke,ki) = std::sqrt(dx_ds*dx_ds + dy_ds*dy_ds + dz_ds*dz_ds); + + if ( detj(ke,ki) <= 0. ) + { + error[ke] = 1.; + } + } + } +} + +void +MasterElementCalc::determinant_element1d_in_2d( + const int nint, + const int npe, + const double* deriv, // (nvec,npe_g,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error) +{ + auto c2d = [coords,npe](int d, int i, int e) { return coords[d + 2*(i + npe*e)]; }; + auto d1d = [deriv,npe](int d, int n, int q) { return deriv[d + n + npe*q]; }; + auto detj = [det_J,nelem](int e, int q) -> double& { return det_J[e+nelem*q]; }; + + for (int elem(0); elem < nelem; ++elem) error[elem] = 0.; + + for (int ke(0); ke < nelem; ++ke) { + for ( int ki(0); ki < nint; ++ki ) { + double dx_ds = 0.; + double dy_ds = 0.; + + for ( int kn(0); kn < npe; ++kn ) { + dx_ds += d1d(0,kn,ki)*c2d(0,kn,ke); + dy_ds += d1d(0,kn,ki)*c2d(1,kn,ke); + } + + detj(ke,ki) = std::sqrt(dx_ds*dx_ds + dy_ds*dy_ds); + + if ( detj(ke,ki) <= 0. ) + { + error[ke] = 1.; + } + } + } +} + +void +MasterElementCalc::gradient_operator_2d( + const int nint, + const int npe_g, + const double* deriv_g, // (nvec,npe_g,nint) + const int npe_f, + const double* deriv_f, // (nvec,npe_f,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* gradop, // (nvec,npe,nelem,nint) + double* det_J, // (nelem,nint) + double* error) +{ + auto c2d = [coords,npe_g](int d, int i, int e) { return coords[d + 2*(i + npe_g*e)]; }; + auto d2d = [deriv_g,npe_g](int d, int n, int q) { return deriv_g[d + 2*(n + npe_g*q)]; }; + auto d2df = [deriv_f,npe_f](int d, int n, int q) { return deriv_f[d + 2*(n + npe_f*q)]; }; + auto detj = [det_J,nelem](int e, int q) -> double& { return det_J[e+nelem*q]; }; + auto g2d = [gradop,npe_f,nelem](int d, int n, int e, int q) -> double& { return gradop[d + 2*(n + npe_f*(e+nelem*q))]; }; + + for (int elem(0); elem < nelem; ++elem) error[elem] = 0.; + + for (int ke(0); ke < nelem; ++ke) { + for ( int ki(0); ki < nint; ++ki ) { + double dx_ds0 = 0.; + double dx_ds1 = 0.; + double dy_ds0 = 0.; + double dy_ds1 = 0.; + + for ( int kn(0); kn < npe_g; ++kn ) { + dx_ds0 = dx_ds0+d2d(0,kn,ki)*c2d(0,kn,ke); + dx_ds1 = dx_ds1+d2d(1,kn,ki)*c2d(0,kn,ke); + + dy_ds0 = dy_ds0+d2d(0,kn,ki)*c2d(1,kn,ke); + dy_ds1 = dy_ds1+d2d(1,kn,ki)*c2d(1,kn,ke); + } + + detj(ke,ki) = dx_ds0*dy_ds1 - dy_ds0*dx_ds1; + + double denom = 0.0; + if ( detj(ke,ki) <= 0. ) + { + error[ke] = 1.; + } + else + { + denom = 1./detj(ke,ki); + } + + // compute the gradient operators at the integration station - + for ( int kn(0); kn < npe_f; ++kn ) { + g2d(0,kn,ki,ke) = denom * ( d2df(0,kn,ki)*dy_ds1 - d2df(1,kn,ki)*dy_ds0 ); + g2d(1,kn,ki,ke) = denom * ( d2df(1,kn,ki)*dx_ds0 - d2df(0,kn,ki)*dx_ds1 ); + } + } + } +} + +void +MasterElementCalc::gradient_operator_3d( + const int nint, + const int npe_g, + const double* deriv_g, // (nvec,npe_g,nint) + const int npe_f, + const double* deriv_f, // (nvec,npe_f,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* gradop, // (nvec,npe,nelem,nint) + double* det_J, // (nelem,nint) + double* error) +{ + auto c3d = [coords,npe_g](int d, int i, int e) { return coords[d + 3*(i + npe_g*e)]; }; + auto d3d = [deriv_g,npe_g](int d, int n, int q) { return deriv_g[d + 3*(n + npe_g*q)]; }; + auto d3df = [deriv_f,npe_f](int d, int n, int q) { return deriv_f[d + 3*(n + npe_f*q)]; }; + auto detj = [det_J,nelem](int e, int q) -> double& { return det_J[e+nelem*q]; }; + auto g3d = [gradop,npe_f,nelem](int d, int n, int e, int q) -> double& { return gradop[d + 3*(n + npe_f*(e+nelem*q))]; }; + + for (int elem(0); elem < nelem; ++elem) error[elem] = 0.; + + for (int ke(0); ke < nelem; ++ke) { + for ( int ki(0); ki < nint; ++ki ) { + double dx_ds0 = 0.; + double dx_ds1 = 0.; + double dx_ds2 = 0.; + double dy_ds0 = 0.; + double dy_ds1 = 0.; + double dy_ds2 = 0.; + double dz_ds0 = 0.; + double dz_ds1 = 0.; + double dz_ds2 = 0.; + + for ( int kn(0); kn < npe_g; ++kn ) { + dx_ds0 = dx_ds0+d3d(0,kn,ki)*c3d(0,kn,ke); + dx_ds1 = dx_ds1+d3d(1,kn,ki)*c3d(0,kn,ke); + dx_ds2 = dx_ds2+d3d(2,kn,ki)*c3d(0,kn,ke); + + dy_ds0 = dy_ds0+d3d(0,kn,ki)*c3d(1,kn,ke); + dy_ds1 = dy_ds1+d3d(1,kn,ki)*c3d(1,kn,ke); + dy_ds2 = dy_ds2+d3d(2,kn,ki)*c3d(1,kn,ke); + + dz_ds0 = dz_ds0+d3d(0,kn,ki)*c3d(2,kn,ke); + dz_ds1 = dz_ds1+d3d(1,kn,ki)*c3d(2,kn,ke); + dz_ds2 = dz_ds2+d3d(2,kn,ki)*c3d(2,kn,ke); + } + + detj(ke,ki) = dx_ds0*( dy_ds1*dz_ds2 - dz_ds1*dy_ds2 ) + + dy_ds0*( dz_ds1*dx_ds2 - dx_ds1*dz_ds2 ) + + dz_ds0*( dx_ds1*dy_ds2 - dy_ds1*dx_ds2 ); + + double denom = 0.0; + if ( detj(ke,ki) <= 0. ) + { + error[ke] = 1.; + } + else + { + denom = 1./detj(ke,ki); + } + + // compute the gradient operators at the integration station - + for ( int kn(0); kn < npe_f; ++kn ) { + g3d(0,kn,ki,ke) = denom * + ( d3df(0,kn,ki)*(dy_ds1*dz_ds2 - dz_ds1*dy_ds2) + + d3df(1,kn,ki)*(dz_ds0*dy_ds2 - dy_ds0*dz_ds2) + + d3df(2,kn,ki)*(dy_ds0*dz_ds1 - dz_ds0*dy_ds1) ); + + g3d(1,kn,ki,ke) = denom * + ( d3df(0,kn,ki)*(dz_ds1*dx_ds2 - dx_ds1*dz_ds2) + + d3df(1,kn,ki)*(dx_ds0*dz_ds2 - dz_ds0*dx_ds2) + + d3df(2,kn,ki)*(dz_ds0*dx_ds1 - dx_ds0*dz_ds1) ); + + g3d(2,kn,ki,ke) = denom * + ( d3df(0,kn,ki)*(dx_ds1*dy_ds2 - dy_ds1*dx_ds2) + + d3df(1,kn,ki)*(dy_ds0*dx_ds2 - dx_ds0*dy_ds2) + + d3df(2,kn,ki)*(dx_ds0*dy_ds1 - dy_ds0*dx_ds1) ); + } + } + } +} + +} // namespace krino diff --git a/packages/krino/krino/master_element/Akri_MasterElementCalc.hpp b/packages/krino/krino/master_element/Akri_MasterElementCalc.hpp new file mode 100644 index 000000000000..f5d99e687f9f --- /dev/null +++ b/packages/krino/krino/master_element/Akri_MasterElementCalc.hpp @@ -0,0 +1,122 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_MasterElementCalc_h +#define Akri_MasterElementCalc_h + +namespace krino { + +class MasterElementCalc +{ +public: + static void scalar_gradient( + const int nint, //: number of intg points + const int nelem, //: number of elements to process + const int ndims, + const int nnodes, + const double* gradop, //: (nvec,npe,nelem,nint) + const double* det_J, //: (nelem,nint) + const double* sfield, //: (npe,nelem) + double* vector ); //: (nvec,nelem,nint) + + static void determinant( + const int num_elem_dims, + const int num_coord_dims, + const int nint, + const int npe_g, + const double* deriv_g, // (nvec,npe_g,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error ); // (nelem) + + static void gradient_operator( + const int num_elem_dims, + const int nint, + const int npe_g, + const double* deriv_g, // (nvec,npe_g,nint) + const int npe_f, + const double* deriv_f, // (nvec,npe_f,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* gradop, // (nvec,npe,nelem,nint) + double* det_J, // (nelem,nint) + double* error); // (nelem) + + static void determinant_2d( + const int nint, + const int npe, + const double* deriv, // (nvec,npe_g,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error); // (nelem) + static void determinant_3d( + const int nint, + const int npe, + const double* deriv, // (nvec,npe_g,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error); // (nelem) + + static void determinant_element2d_in_3d( + const int nint, + const int npe, + const double* deriv, // (nvec,npe_g,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error); // (nelem) + + static void determinant_element1d_in_3d( + const int nint, + const int npe, + const double* deriv, // (nvec,npe_g,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error); // (nelem) + + static void determinant_element1d_in_2d( + const int nint, + const int npe, + const double* deriv, // (nvec,npe_g,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error); // (nelem) + + static void gradient_operator_2d( + const int nint, + const int npe_g, + const double* deriv_g, // (nvec,npe_g,nint) + const int npe_f, + const double* deriv_f, // (nvec,npe_f,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* gradop, // (nvec,npe,nelem,nint) + double* det_J, // (nelem,nint) + double* error); // (nelem) + + static void gradient_operator_3d( + const int nint, + const int npe_g, + const double* deriv_g, // (nvec,npe_g,nint) + const int npe_f, + const double* deriv_f, // (nvec,npe_f,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* gradop, // (nvec,npe,nelem,nint) + double* det_J, // (nelem,nint) + double* error); // (nelem) +}; + +} // end namespace krino + +#endif // Akri_MasterElementCalc_h diff --git a/packages/krino/krino/master_element/Akri_MasterElementHybrid.cpp b/packages/krino/krino/master_element/Akri_MasterElementHybrid.cpp new file mode 100644 index 000000000000..d8b7f86c13b7 --- /dev/null +++ b/packages/krino/krino/master_element/Akri_MasterElementHybrid.cpp @@ -0,0 +1,181 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include // for get_cell_topology +#include + +#ifdef __INTEL_COMPILER +#include +#else +//FieldContainer has shadowed variables +//this disables the checking on GCC only +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#include +#pragma GCC diagnostic pop +#endif +#include +#include +#include +#include +#include +#include + +namespace krino { + +MasterElementHybrid::MasterElementHybrid( + stk::topology topology, + std::unique_ptr basis) +: m_topology(topology), + m_Basis(std::move(basis)) +{ + // set the cubature + Intrepid::DefaultCubatureFactory cubatureFactory; + Teuchos::RCP> intrepidCubature = cubatureFactory.create(stk::mesh::get_cell_topology(topology), 2*m_Basis->degree()); + m_numIntgPts = intrepidCubature->getNumPoints(); + + m_numNodes = topology.num_nodes(); + m_numElemDims = intrepidCubature->getDimension(); + + // Allocate reference data + m_shapeFuncs.resize(m_numIntgPts*m_numNodes); + m_pointGrads.resize(m_numIntgPts*m_numNodes*m_numElemDims ); + m_refPoints.resize(m_numIntgPts*m_numElemDims); + m_refWeights.resize(m_numIntgPts); + m_refCoords.resize(m_numNodes*m_numElemDims); + + // retrieve the cubature points and weights + std::vector refPointsDims = {m_numIntgPts, m_numElemDims}; + Intrepid::FieldContainer refPointsFC( refPointsDims, m_refPoints.data() ); + std::vector refWeightsDims = {m_numIntgPts}; + Intrepid::FieldContainer refWeightsFC( refWeightsDims, m_refWeights.data() ); + intrepidCubature->getCubature(refPointsFC, refWeightsFC); + + // compute the reference values and gradients at the integration points + m_Basis->shape_fcn(m_numIntgPts, m_refPoints.data(), m_shapeFuncs.data()); + m_Basis->shape_fcn_deriv(m_numIntgPts, m_refPoints.data(), m_pointGrads.data()); + m_Basis->nodal_parametric_coordinates(m_refCoords.data()); + + m_centroidParCoords.resize(m_numElemDims, 0.); + for(int n=0; n < m_numNodes; ++n) + { + for(int d=0; d < m_numElemDims; ++d) + { + m_centroidParCoords[d] += m_refCoords[n*m_numElemDims + d]; + } + } +} + +void +MasterElementHybrid::determinant( + const int numCoordDims, + const int nelem, + const double* coords, // (numCoordDims,npe,nelem) + double* det_J, // (nelem,nint) + double* error ) const // (nelem) +{ + MasterElementCalc::determinant(m_numElemDims, numCoordDims, m_numIntgPts, m_numNodes, m_pointGrads.data(), nelem, coords, det_J, error); +} + +void +MasterElementHybrid::shape_fcn( + const int nint, // returns array(npe,nint) + const double* p_coords, + double* result) const +{ + m_Basis->shape_fcn(nint, p_coords, result); +} + +void +MasterElementHybrid::shape_fcn_deriv( + const int nint, + const double* p_coords, + double* result ) const +{ + m_Basis->shape_fcn_deriv(nint, p_coords, result); +} + + +void +MasterElementHybrid::interpolate_point( + const int npar_coord, + const double * par_coord, // (npar_coord) + const int ncomp_field, + const double * field, // (ncomp_field,num_nodes) + double * result ) const // (ncomp_field) +{ + std::vector shape(m_numNodes); + shape_fcn(1, par_coord, shape.data()); + for ( int comp(0); comp < ncomp_field; ++comp ) { + result[ comp ] = 0.0; + for ( int node(0); node < m_numNodes; ++node ) { + result[ comp ] += shape[node] * field[ ncomp_field * node + comp ]; + } + } +} + +void +MasterElementHybrid::scalar_gradient( + const int nelem, //: number of elements to process + const double* gradop, //: (nvec,npe,nelem,nint) + const double* det_J, //: (nelem,nint) + const double* sfield, //: (npe,nelem) + double* vector ) const //: (nvec,nelem,nint) +{ + MasterElementCalc::scalar_gradient(m_numIntgPts, nelem, m_numElemDims, m_numNodes, gradop, det_J, sfield, vector); +} + +void +MasterElementHybrid::scalar_gradient( + const int nint, //: number of intg points + const int nelem, //: number of elements to process + const double* gradop, //: (nvec,npe,nelem,nint) + const double* det_J, //: (nelem,nint) + const double* sfield, //: (npe,nelem) + double* vector ) const //: (nvec,nelem,nint) +{ + MasterElementCalc::scalar_gradient(nint, nelem, m_numElemDims, m_numNodes, gradop, det_J, sfield, vector); +} + +void +MasterElementHybrid::determinant( + const int numCoordDims, + const int nint, + const int npe_g, + const double* deriv_g, // (m_numElemDims,npe_g,nint) + const int nelem, + const double* coords, // (numCoordDims,npe,nelem) + double* det_J, // (nelem,nint) + double* error ) const // (nelem) +{ + MasterElementCalc::determinant(m_numElemDims, numCoordDims, nint, npe_g, deriv_g, nelem, coords, det_J, error); +} + +void +MasterElementHybrid::gradient_operator( + const int numCoordDims, + const int nint, + const int npe_g, + const double* deriv_g, // (nvec,npe_g,nint) + const int npe_f, + const double* deriv_f, // (nvec,npe_f,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* gradop, // (nvec,npe,nelem,nint) + double* det_J, // (nelem,nint) + double* error) const +{ + ThrowRequireMsg(m_numElemDims == numCoordDims, "MasterElementHybrid::gradient_operator does not support lower rank elements in higher dimensions (e.g. BAR,QUAD,TRI in 3D)."); + MasterElementCalc::gradient_operator(m_numElemDims, nint, npe_g, deriv_g, npe_f, deriv_f, nelem, coords, gradop, det_J, error); +} + +} // namespace krino diff --git a/packages/krino/krino/master_element/Akri_MasterElementHybrid.hpp b/packages/krino/krino/master_element/Akri_MasterElementHybrid.hpp new file mode 100644 index 000000000000..e0bac17fb04b --- /dev/null +++ b/packages/krino/krino/master_element/Akri_MasterElementHybrid.hpp @@ -0,0 +1,137 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_MasterElementHybrid_h +#define Akri_MasterElementHybrid_h + +#include +#include +#include + +namespace krino { class Basis; } + +namespace krino { + +class MasterElementHybrid +{ +public: + static const MasterElementHybrid & getMasterElement(stk::topology t); + + MasterElementHybrid( + stk::topology topology, + std::unique_ptr basis); + + // Copy and assignment are not allowed + MasterElementHybrid( const MasterElementHybrid & ) = delete; + MasterElementHybrid & operator=( const MasterElementHybrid & ) = delete; + + stk::topology get_topology() const { return m_topology; } + unsigned topology_dimension() const { return m_topology.dimension(); } + + // returns the number of integration points + unsigned num_intg_pts() const { return m_numIntgPts; } + + // returns the number of nodes + unsigned num_nodes() const { return m_numNodes; } + + //: Query the integration weights + const double* intg_weights() const { return m_refWeights.data(); } + + //: Query the integration points/stations + const double* intg_pt_locations() const { return m_refPoints.data(); } + + const double * nodal_parametric_coordinates() const { return m_refCoords.data(); } + const double * centroid_parametric_coordinates() const { return m_centroidParCoords.data(); } + + void determinant( + const int numCoordDims, + const int nelem, + const double* coords, // (numCoordDims,npe,nelem) + double* det_J, // (nelem,nint) + double* error ) const; // (nelem) + void determinant( + const int numCoordDims, + const int nint, + const int npe_g, + const double* deriv_g, // (m_numElemDims,npe_g,nint) + const int nelem, + const double* coords, // (numCoordDims,npe,nelem) + double* det_J, // (nelem,nint) + double* error ) const ; // (nelem) + + //: Returns the values of the nodal interpolation shape functions + //: at the integration stations. + const double* shape_fcn() const { return m_shapeFuncs.data(); } + void shape_fcn( + const int nint, // returns array(npe,nint) + const double* p_coords, + double* result) const; + + //: Returns the derivatives of the nodal interpolation shape functions + //: with respect to the local parametric coordinates at the integration + //: stations. + const double* shape_fcn_deriv() const { return m_pointGrads.data(); } + void shape_fcn_deriv( + const int nint, + const double* p_coords, + double* result ) const; + + void interpolate_point( + const int npar_coord, + const double * par_coord, // (npar_coord) + const int ncomp_field, + const double * field, // (ncomp_field,num_nodes) + double * result ) const; // (ncomp_field) + + void gradient_operator( + const int numCoordDims, + const int nint, + const int npe_g, + const double* deriv_g, // (numElemDims,npe_g,nint) + const int npe_f, + const double* deriv_f, // (numElemDims,npe_f,nint) + const int nelem, + const double* coords, // (numElemDims,npe,nelem) + double* gradop, // (numElemDims,npe,nelem,nint) + double* det_J, // (nelem,nint) + double* error) const ; + + void scalar_gradient( + const int nelem, //: number of elements to process + const double* gradop, //: (nvec,npe,nelem,nint) + const double* det_J, //: (nelem,nint) + const double* sfield, //: (npe,nelem) + double* vector ) const ; //: (nvec,nelem,nint) + void scalar_gradient( + const int nint, //: number of intg points + const int nelem, //: number of elements to process + const double* gradop, //: (nvec,npe,nelem,nint) + const double* det_J, //: (nelem,nint) + const double* sfield, //: (npe,nelem) + double* vector ) const ; //: (nvec,nelem,nint) + +private: + stk::topology m_topology; + int m_numNodes; + int m_numElemDims; + int m_numIntgPts; + + std::unique_ptr m_Basis; + + // Local FieldContainers + std::vector m_shapeFuncs; + std::vector m_pointGrads; + std::vector m_refPoints; + std::vector m_refWeights; + std::vector m_refCoords; + std::vector m_centroidParCoords; +}; + +} // end namespace krino + +#endif // Akri_MasterElementHybrid_h diff --git a/packages/krino/krino/master_element/Akri_MasterElementIntrepid.cpp b/packages/krino/krino/master_element/Akri_MasterElementIntrepid.cpp new file mode 100644 index 000000000000..3b026bb7189c --- /dev/null +++ b/packages/krino/krino/master_element/Akri_MasterElementIntrepid.cpp @@ -0,0 +1,287 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include "Akri_MasterElementCalc.hpp" + +#ifdef __INTEL_COMPILER +#include +#else +//FieldContainer has shadowed variables +//this disables the checking on GCC only +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#include +#pragma GCC diagnostic pop +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // for get_cell_topology + +namespace krino { + +const MasterElementIntrepid & +MasterElementIntrepid::getMasterElement(stk::topology t, const unsigned spatial_dimension) +{ + static std::vector> all_master_elems(stk::topology::BEGIN_TOPOLOGY + stk::topology::NUM_TOPOLOGIES); + std::unique_ptr & master_elem = all_master_elems[t()]; + if (nullptr == master_elem.get()) + { + std::unique_ptr>> basis; + switch(t()) + { + case stk::topology::LINE_2: + basis = std::make_unique>>(); + break; + case stk::topology::LINE_3: + basis = std::make_unique>>(2, Intrepid::POINTTYPE_SPECTRAL); + break; + case stk::topology::TRI_3: + basis = std::make_unique>>(); + break; + case stk::topology::TRI_6: + basis = std::make_unique>>(); + break; + case stk::topology::QUAD_4: + basis = std::make_unique>>(); + break; + case stk::topology::QUAD_9: + basis = std::make_unique>>(); + break; + case stk::topology::TRI_3_2D: + basis = std::make_unique>>(); + break; + case stk::topology::TRI_6_2D: + basis = std::make_unique>>(); + break; + case stk::topology::QUAD_4_2D: + basis = std::make_unique>>(); + break; + case stk::topology::QUAD_9_2D: + basis = std::make_unique>>(); + break; + case stk::topology::TET_4: + basis = std::make_unique>>(); + break; + case stk::topology::TET_10: + basis = std::make_unique>>(); + break; + case stk::topology::HEX_8: + basis = std::make_unique>>(); + break; + case stk::topology::HEX_27: + basis = std::make_unique>>(); + break; + default: + throw std::runtime_error("Element topology not found in MasterElementIntrepid::build: " + t.name()); + break; + } + master_elem = std::make_unique(t, std::move(basis), spatial_dimension); + } + return *master_elem; +} + +MasterElementIntrepid::MasterElementIntrepid( + stk::topology topology, + std::unique_ptr > > basis, + unsigned spatial_dimension) +: m_topology(topology), + m_numCoordDims(spatial_dimension), + m_intrepidBasis(std::move(basis)) +{ + shards::CellTopology cellType = stk::mesh::get_cell_topology(topology); + + // set the cubature + Intrepid::DefaultCubatureFactory cubatureFactory; + Teuchos::RCP> intrepidCubature = cubatureFactory.create(cellType, 2*m_intrepidBasis->getDegree()); + m_numIntgPts = intrepidCubature->getNumPoints(); + + m_numNodes = topology.num_nodes(); + m_numElemDims = intrepidCubature->getDimension(); + + // Allocate reference data + m_shapeFuncs.resize(m_numIntgPts*m_numNodes); + m_pointGrads.resize(m_numIntgPts*m_numNodes*m_numElemDims ); + m_refPoints.resize(m_numIntgPts*m_numElemDims); + m_refWeights.resize(m_numIntgPts); + m_refCoords.resize(m_numNodes*m_numElemDims); + + // retrieve the cubature points and weights + std::vector refPointsDims = {m_numIntgPts, m_numElemDims}; + Intrepid::FieldContainer refPointsFC( refPointsDims, m_refPoints.data() ); + std::vector refWeightsDims = {m_numIntgPts}; + Intrepid::FieldContainer refWeightsFC( refWeightsDims, m_refWeights.data() ); + intrepidCubature->getCubature(refPointsFC, refWeightsFC); + + // compute the refernce values and gradients at the integration points + Intrepid::FieldContainer pointVals(m_numNodes, m_numIntgPts); + Intrepid::FieldContainer pointGrads(m_numNodes, m_numIntgPts, m_numElemDims); + m_intrepidBasis->getValues(pointVals, refPointsFC, Intrepid::OPERATOR_VALUE); + m_intrepidBasis->getValues(pointGrads, refPointsFC, Intrepid::OPERATOR_GRAD); + + // re-order shape functions for consistency with other master elements + for ( int ip(0); ip < m_numIntgPts; ++ip ) { + for ( int node(0); node < m_numNodes; ++node ) { + m_shapeFuncs[ip*m_numNodes + node] = pointVals(node, ip); + } + } + for ( int ip(0); ip < m_numIntgPts; ++ip ) { + for ( int node(0); node < m_numNodes; ++node ) { + for ( int dim(0); dim < m_numElemDims; ++dim ) { + m_pointGrads[(ip*m_numNodes + node)*m_numElemDims + dim] = pointGrads(node, ip, dim); + } + } + } + + for ( int node(0); node < m_numNodes; ++node ) { + const double * node_coords = Intrepid::CellTools::getReferenceNode( cellType, node ); + for ( int dim(0); dim < m_numElemDims; ++dim ) { + m_refCoords[node*m_numElemDims + dim] = node_coords[dim]; + } + } +} + +void +MasterElementIntrepid::determinant( + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error ) const // (nelem) +{ + determinant(m_numIntgPts, m_numNodes, m_pointGrads.data(), nelem, coords, det_J, error); +} + +void +MasterElementIntrepid::shape_fcn( + const int nint, // returns array(npe,nint) + const double* p_coords, + double* result) const +{ + Intrepid::FieldContainer pointVals(m_numNodes, nint); + + // create the pcoordVec FC from the p_coords ptr and it's dimensions + std::vector pcoordDims = { nint, m_numElemDims }; + Intrepid::FieldContainer pcoordVec( pcoordDims, const_cast< double * >(p_coords) ); + + // compute the shape function values at the integration points + m_intrepidBasis->getValues(pointVals, pcoordVec, Intrepid::OPERATOR_VALUE); + + // re-order shape functions for consistency with other master elements + for ( int ip(0); ip < nint; ++ip ) { + for ( int node(0); node < m_numNodes; ++node ) { + result[ip*m_numNodes + node] = pointVals(node,ip); + } + } +} + +void +MasterElementIntrepid::shape_fcn_deriv( + const int nint, + const double* p_coords, + double* result ) const +{ + Intrepid::FieldContainer resultVec(m_numNodes, nint, m_numElemDims); + + // create the pcoordVec FC from the p_coords ptr and it's dimensions + std::vector pcoordDims = { nint, m_numElemDims }; + Intrepid::FieldContainer pcoordVec( pcoordDims, const_cast< double * >(p_coords) ); + + m_intrepidBasis->getValues(resultVec, pcoordVec, Intrepid::OPERATOR_GRAD); + + // re-order shape functions for consistency with other master elements + for ( int ip(0); ip < nint; ++ip ) { + for ( int node(0); node < m_numNodes; ++node ) { + for ( int dim(0); dim < m_numElemDims; ++dim ) { + result[(ip*m_numNodes + node)*m_numElemDims + dim] = resultVec(node,ip,dim); + } + } + } +} + +void +MasterElementIntrepid::interpolate_point( + const int npar_coord, + const double * par_coord, // (npar_coord) + const int ncomp_field, + const double * field, // (num_nodes,ncomp_field) + double * result ) const // (ncomp_field) +{ + std::vector shape(m_numNodes); + shape_fcn(1, par_coord, shape.data()); + for ( int comp(0); comp < ncomp_field; ++comp ) { + result[ comp ] = 0.0; + for ( int node(0); node < m_numNodes; ++node ) { + result[ comp ] += shape[node] * field[ m_numNodes * comp + node ]; + } + } +} + +void +MasterElementIntrepid::scalar_gradient( + const int nelem, //: number of elements to process + const double* gradop, //: (nvec,npe,nelem,nint) + const double* det_J, //: (nelem,nint) + const double* sfield, //: (npe,nelem) + double* vector ) const //: (nvec,nelem,nint) +{ + MasterElementCalc::scalar_gradient(m_numIntgPts, nelem, m_numElemDims, m_numNodes, gradop, det_J, sfield, vector); +} + +void +MasterElementIntrepid::scalar_gradient( + const int nint, //: number of intg points + const int nelem, //: number of elements to process + const double* gradop, //: (nvec,npe,nelem,nint) + const double* det_J, //: (nelem,nint) + const double* sfield, //: (npe,nelem) + double* vector ) const //: (nvec,nelem,nint) +{ + MasterElementCalc::scalar_gradient(nint, nelem, m_numElemDims, m_numNodes, gradop, det_J, sfield, vector); +} + +void +MasterElementIntrepid::determinant( + const int nint, + const int npe_g, + const double* deriv_g, // (nvec,npe_g,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error ) const // (nelem) +{ + MasterElementCalc::determinant(m_numElemDims, m_numCoordDims, nint, npe_g, deriv_g, nelem, coords, det_J, error); +} + +void +MasterElementIntrepid::gradient_operator( + const int nint, + const int npe_g, + const double* deriv_g, // (nvec,npe_g,nint) + const int npe_f, + const double* deriv_f, // (nvec,npe_f,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* gradop, // (nvec,npe,nelem,nint) + double* det_J, // (nelem,nint) + double* error) const +{ + ThrowRequireMsg(m_numElemDims == m_numCoordDims, "MasterElementHybrid::gradient_operator does not support lower rank elements in higher dimensions (e.g. BAR,QUAD,TRI in 3D)."); + MasterElementCalc::gradient_operator(m_numElemDims, nint, npe_g, deriv_g, npe_f, deriv_f, nelem, coords, gradop, det_J, error); +} + +} // namespace krino diff --git a/packages/krino/krino/master_element/Akri_MasterElementIntrepid.hpp b/packages/krino/krino/master_element/Akri_MasterElementIntrepid.hpp new file mode 100644 index 000000000000..c35933b1d337 --- /dev/null +++ b/packages/krino/krino/master_element/Akri_MasterElementIntrepid.hpp @@ -0,0 +1,135 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_MasterElementIntrepid_h +#define Akri_MasterElementIntrepid_h + +#include +#include + +namespace Intrepid { template class FieldContainer; } +namespace Intrepid { template class Basis; } + +namespace krino { + +class MasterElementIntrepid +{ +public: + MasterElementIntrepid( + stk::topology topology, + std::unique_ptr > > basis, + unsigned spatial_dimension); + + // Copy and assignment are not allowed + MasterElementIntrepid( const MasterElementIntrepid & ) = delete; + MasterElementIntrepid & operator=( const MasterElementIntrepid & ) = delete; + + static const MasterElementIntrepid & getMasterElement(stk::topology t, const unsigned spatial_dimension); + + stk::topology get_topology() const { return m_topology; } + unsigned dimension() const { return m_topology.dimension(); } + + // returns the number of integration points + unsigned num_intg_pts() const { return m_numIntgPts; } + + // returns the number of nodes + unsigned num_nodes() const { return m_numNodes; } + + //: Query the integration weights + const double* intg_weights() const { return m_refWeights.data(); } + + //: Query the integration points/stations + const double* intg_pt_locations() const { return m_refPoints.data(); } + + const double * nodal_parametric_coordinates() const { return m_refCoords.data(); } + + void determinant( + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error ) const; // (nelem) + void determinant( + const int nint, + const int npe_g, + const double* deriv_g, // (nvec,npe_g,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* det_J, // (nelem,nint) + double* error ) const ; // (nelem) + + //: Returns the values of the nodal interpolation shape functions + //: at the integration stations. + const double* shape_fcn() const { return m_shapeFuncs.data(); } + void shape_fcn( + const int nint, // returns array(npe,nint) + const double* p_coords, + double* result) const; + + //: Returns the derivatives of the nodal interpolation shape functions + //: with respect to the local parametric coordinates at the integration + //: stations. + const double* shape_fcn_deriv() const { return m_pointGrads.data(); } + void shape_fcn_deriv( + const int nint, + const double* p_coords, + double* result ) const; + + void interpolate_point( + const int npar_coord, + const double * par_coord, // (npar_coord) + const int ncomp_field, + const double * field, // (num_nodes,ncomp_field) + double * result ) const; // (ncomp_field) + + void gradient_operator( + const int nint, + const int npe_g, + const double* deriv_g, // (nvec,npe_g,nint) + const int npe_f, + const double* deriv_f, // (nvec,npe_f,nint) + const int nelem, + const double* coords, // (nvec,npe,nelem) + double* gradop, // (nvec,npe,nelem,nint) + double* det_J, // (nelem,nint) + double* error) const ; + + void scalar_gradient( + const int nelem, //: number of elements to process + const double* gradop, //: (nvec,npe,nelem,nint) + const double* det_J, //: (nelem,nint) + const double* sfield, //: (npe,nelem) + double* vector ) const ; //: (nvec,nelem,nint) + void scalar_gradient( + const int nint, //: number of intg points + const int nelem, //: number of elements to process + const double* gradop, //: (nvec,npe,nelem,nint) + const double* det_J, //: (nelem,nint) + const double* sfield, //: (npe,nelem) + double* vector ) const ; //: (nvec,nelem,nint) + +private: + stk::topology m_topology; + int m_numNodes; + int m_numElemDims; + int m_numCoordDims; + int m_numIntgPts; + + // the Intrepid Basis + std::unique_ptr>> m_intrepidBasis; + + // Local FieldContainers + std::vector m_shapeFuncs; + std::vector m_pointGrads; + std::vector m_refPoints; + std::vector m_refWeights; + std::vector m_refCoords; +}; + +} // end namespace krino + +#endif // Akri_MasterElementIntrepid_h diff --git a/packages/krino/krino/master_element/CMakeLists.txt b/packages/krino/krino/master_element/CMakeLists.txt new file mode 100644 index 000000000000..da7d6357181c --- /dev/null +++ b/packages/krino/krino/master_element/CMakeLists.txt @@ -0,0 +1,19 @@ +SET(HEADERS "") +SET(SOURCES "") + +INCLUDE_DIRECTORIES(${${PACKAGE_NAME}_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +FILE(GLOB HEADERS *.hpp) +FILE(GLOB SOURCES *.cpp) + +TRIBITS_ADD_LIBRARY( + krino_master_element_lib + HEADERS ${HEADERS} + SOURCES ${SOURCES} + ) + +INSTALL(FILES ${HEADERS} DESTINATION + ${CMAKE_INSTALL_PREFIX}/${${PROJECT_NAME}_INSTALL_INCLUDE_DIR}/krino_master_element_lib) + diff --git a/packages/krino/krino/parser/Akri_CDFEM_Options_Parser.cpp b/packages/krino/krino/parser/Akri_CDFEM_Options_Parser.cpp new file mode 100644 index 000000000000..f1eb515d80bf --- /dev/null +++ b/packages/krino/krino/parser/Akri_CDFEM_Options_Parser.cpp @@ -0,0 +1,139 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include + +#include + +namespace krino { + +void +CDFEM_Options_Parser::parse(const YAML::Node & region_node, RegionInterface & region) +{ + const YAML::Node cdfem_node = YAML_Parser::get_map_if_present(region_node, "cdfem_options"); + if ( cdfem_node ) + { + CDFEM_Support & cdfem_support = CDFEM_Support::get(region.get_stk_mesh_meta_data()); + + std::string cdfem_edge_degeneracy_handling_string; + if (YAML_Parser::get_if_present(cdfem_node, "cdfem_edge_degeneracy_handling", cdfem_edge_degeneracy_handling_string)) + { + std::transform(cdfem_edge_degeneracy_handling_string.begin(), cdfem_edge_degeneracy_handling_string.end(), cdfem_edge_degeneracy_handling_string.begin(), ::toupper); + static std::map valid_entries = + { {"SNAP_TO_NODE", SNAP_TO_NODE}, {"SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS", SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE} }; + auto it = valid_entries.find(cdfem_edge_degeneracy_handling_string); + if (it == valid_entries.end()) + { + stk::RuntimeDoomedAdHoc() << "Invalid cdfem_edge_degeneracy_handling type: " << YAML_Parser::info(cdfem_node); + } + else + { + cdfem_support.set_cdfem_edge_degeneracy_handling( it->second ); + } + } + + double cdfem_edge_tol = 0.0; + if (YAML_Parser::get_if_present(cdfem_node, "cdfem_edge_tolerance", cdfem_edge_tol)) + { + const double minimum_edge_tol = 1.e-12; + if (cdfem_edge_tol < minimum_edge_tol) + { + krinolog << "Using minimum edge tolerance of " << minimum_edge_tol << " instead of specified tolerance of " << cdfem_edge_tol << stk::diag::dendl; + cdfem_edge_tol = minimum_edge_tol; + } + cdfem_support.set_cdfem_edge_tol( cdfem_edge_tol ); + } + + std::string cdfem_simplex_generation_method_string; + if (YAML_Parser::get_if_present(cdfem_node, "cdfem_simplex_generation_method", cdfem_simplex_generation_method_string)) + { + std::transform(cdfem_simplex_generation_method_string.begin(), cdfem_simplex_generation_method_string.end(), cdfem_simplex_generation_method_string.begin(), ::toupper); + static std::map valid_entries = { + {"CUT_QUADS_BY_GLOBAL_IDENTIFIER", CUT_QUADS_BY_GLOBAL_IDENTIFIER}, + {"CUT_QUADS_BY_LARGEST_ANGLE", CUT_QUADS_BY_LARGEST_ANGLE}, + {"CUT_QUADS_BY_NEAREST_EDGE_CUT", CUT_QUADS_BY_NEAREST_EDGE_CUT} + }; + auto it = valid_entries.find(cdfem_simplex_generation_method_string); + if (it == valid_entries.end()) + { + stk::RuntimeDoomedAdHoc() << "Invalid cdfem_simplex_generation_method type: " << YAML_Parser::info(cdfem_node); + } + else + { + cdfem_support.set_simplex_generation_method( it->second ); + } + } + + int num_init_decomp_cycles = 0; + if (YAML_Parser::get_if_present(cdfem_node, "number_of_initial_decomposition_cycles", num_init_decomp_cycles)) + { + cdfem_support.set_num_initial_decomposition_cycles( num_init_decomp_cycles ); + } + + bool interface_refinement_specified = false; + int interface_minimum_refinement_level = 0; + if(YAML_Parser::get_if_present(cdfem_node, "cdfem_interface_minimum_refinement_level", interface_minimum_refinement_level)) + { + interface_refinement_specified = true; + } + + int interface_maximum_refinement_level = 0; + if(YAML_Parser::get_if_present(cdfem_node, "cdfem_interface_maximum_refinement_level", interface_maximum_refinement_level)) + { + interface_refinement_specified = true; + } + + if (interface_refinement_specified) + { + cdfem_support.activate_interface_refinement( interface_minimum_refinement_level, interface_maximum_refinement_level ); + } + + int nonconformal_adapt_levels = 0; + if (YAML_Parser::get_if_present(cdfem_node, "cdfem_nonconformal_adaptivity_levels", nonconformal_adapt_levels)) + { + cdfem_support.activate_nonconformal_adaptivity( nonconformal_adapt_levels ); + } + + int post_adapt_refine_levels = 0; + if (YAML_Parser::get_if_present(cdfem_node, "post_adaptivity_uniform_refinement_levels", post_adapt_refine_levels)) + { + cdfem_support.set_post_adapt_refinement_levels( post_adapt_refine_levels ); + } + + uint64_t nonconformal_adapt_target_element_count = 0; + if (YAML_Parser::get_if_present(cdfem_node, "nonconformal_adaptivity_target_element_count", nonconformal_adapt_target_element_count)) + { + cdfem_support.activate_nonconformal_adapt_target_count( nonconformal_adapt_target_element_count ); + } + + int post_cdfem_refinement_levels = 0; + if (YAML_Parser::get_if_present(cdfem_node, "post_cdfem_refinement_levels", post_cdfem_refinement_levels)) + { + cdfem_support.set_post_cdfem_refinement_levels( post_cdfem_refinement_levels ); + } + + const YAML::Node post_cdfem_refinement_blocks_seq = YAML_Parser::get_sequence_if_present(cdfem_node, "post_cdfem_refinement_blocks"); + if (post_cdfem_refinement_blocks_seq) + { + std::vector post_cdfem_refinement_blocks; + for ( auto && post_cdfem_refinement_block_node : post_cdfem_refinement_blocks_seq ) + { + const std::string post_cdfem_refinement_block = post_cdfem_refinement_block_node.as(); + post_cdfem_refinement_blocks.push_back(post_cdfem_refinement_block); + } + cdfem_support.set_post_cdfem_refinement_blocks( post_cdfem_refinement_blocks ); + } + } +} + +} // namespace krino diff --git a/packages/krino/krino/parser/Akri_CDFEM_Options_Parser.hpp b/packages/krino/krino/parser/Akri_CDFEM_Options_Parser.hpp new file mode 100644 index 000000000000..dfdf2a05e559 --- /dev/null +++ b/packages/krino/krino/parser/Akri_CDFEM_Options_Parser.hpp @@ -0,0 +1,21 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_CDFEM_Options_Parser_h +#define Akri_CDFEM_Options_Parser_h + +namespace YAML { class Node; } +namespace krino { class RegionInterface; } + +namespace krino { +namespace CDFEM_Options_Parser { + void parse(const YAML::Node & node, RegionInterface & region); +} +} + +#endif // Akri_CDFEM_Options_Parser_h diff --git a/packages/krino/krino/parser/Akri_IC_Parser.cpp b/packages/krino/krino/parser/Akri_IC_Parser.cpp new file mode 100644 index 000000000000..f8199ab14a45 --- /dev/null +++ b/packages/krino/krino/parser/Akri_IC_Parser.cpp @@ -0,0 +1,457 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace krino { + +namespace { + +Sphere * +parse_sphere(const YAML::Node & ic_node) +{ + std::string name; + YAML_Parser::get_if_present(ic_node, "name", name); + + std::vector center; + if (YAML_Parser::get_if_present(ic_node, "center", center)) + { + if (center.size() != 3) + { + stk::RuntimeDoomedAdHoc() << "Expecting 3 real values for center for IC sphere.\n"; + } + } + else + { + stk::RuntimeDoomedAdHoc() << "Missing center for IC sphere.\n"; + } + + double sign = 1.0; + if (YAML_Parser::get_if_present(ic_node, "sign", sign)) + { + if (sign != -1.0 && sign == 1.0) + { + stk::RuntimeDoomedAdHoc() << "Sign for sphere must be -1 or 1.\n"; + } + } + + double radius = 0.0; + if (!YAML_Parser::get_if_present(ic_node, "radius", radius)) + { + stk::RuntimeDoomedAdHoc() << "Missing radius for IC sphere.\n"; + } + + return new Sphere(name, Vector3d(center.data()), radius, sign); +} + +Ellipsoid * +parse_ellipsoid(const YAML::Node & ic_node) +{ + std::string name; + YAML_Parser::get_if_present(ic_node, "name", name); + + std::vector center; + if (YAML_Parser::get_if_present(ic_node, "center", center)) + { + if (center.size() != 3) + { + stk::RuntimeDoomedAdHoc() << "Expecting 3 real values for center for IC sphere.\n"; + } + } + else + { + stk::RuntimeDoomedAdHoc() << "Missing center for IC sphere.\n"; + } + + std::vector semiaxes; + if (YAML_Parser::get_if_present(ic_node, "semiaxes", semiaxes)) + { + if (semiaxes.size() != 3) + { + stk::RuntimeDoomedAdHoc() << "Expecting 3 real values for semiaxes for IC ellipsoid.\n"; + } + } + else + { + stk::RuntimeDoomedAdHoc() << "Missing semiaxes for IC ellipsoid.\n"; + } + + double sign = 1.0; + if (YAML_Parser::get_if_present(ic_node, "sign", sign)) + { + if (sign != -1.0 && sign == 1.0) + { + stk::RuntimeDoomedAdHoc() << "Sign for ellipsoid must be -1 or 1.\n"; + } + } + + std::vector rotationVec; + if (YAML_Parser::get_if_present(ic_node, "rotation", rotationVec)) + { + if (semiaxes.size() != 3) + { + stk::RuntimeDoomedAdHoc() << "Expecting 3 real values for rotation for IC ellipsoid.\n"; + } + } + + return new Ellipsoid(name, center, semiaxes, rotationVec, sign); +} + +Plane * +parse_plane(const YAML::Node & ic_node) +{ + std::string name; + YAML_Parser::get_if_present(ic_node, "name", name); + + std::vector normal; + if (YAML_Parser::get_if_present(ic_node, "normal", normal)) + { + if (normal.size() != 3) + { + stk::RuntimeDoomedAdHoc() << "Expecting 3 real values for center for IC plane.\n"; + } + } + else + { + stk::RuntimeDoomedAdHoc() << "Missing normal for IC plane.\n"; + } + + double multiplier = 1.0; + YAML_Parser::get_if_present(ic_node, "multiplier", multiplier); + + double sign = 1.0; + if (YAML_Parser::get_if_present(ic_node, "sign", sign)) + { + if (sign != -1.0 && sign == 1.0) + { + stk::RuntimeDoomedAdHoc() << "Sign for IC plane must be -1 or 1.\n"; + } + } + multiplier *= sign; + + double offset = 0.0; + if (!YAML_Parser::get_if_present(ic_node, "offset", offset)) + { + stk::RuntimeDoomedAdHoc() << "Missing offset for IC plane.\n"; + } + + return new Plane(name, normal.data(), offset, multiplier); +} + +Cylinder * +parse_cylinder(const YAML::Node & ic_node) +{ + std::string name; + YAML_Parser::get_if_present(ic_node, "name", name); + + std::vector p1; + if (YAML_Parser::get_if_present(ic_node, "p1", p1)) + { + if (p1.size() != 3) + { + stk::RuntimeDoomedAdHoc() << "Expecting 3 real values for p1 for IC cylinder.\n"; + } + } + else + { + stk::RuntimeDoomedAdHoc() << "Missing normal for IC cylinder.\n"; + } + + std::vector p2; + if (YAML_Parser::get_if_present(ic_node, "p2", p2)) + { + if (p2.size() != 3) + { + stk::RuntimeDoomedAdHoc() << "Expecting 3 real values for center for IC cylinder.\n"; + } + } + else + { + stk::RuntimeDoomedAdHoc() << "Missing p2 for IC cylinder.\n"; + } + + double radius = 0.0; + if (!YAML_Parser::get_if_present(ic_node, "radius", radius)) + { + stk::RuntimeDoomedAdHoc() << "Missing radius for IC cylinder.\n"; + } + + double sign = 1.0; + if (YAML_Parser::get_if_present(ic_node, "sign", sign)) + { + if (sign != -1.0 && sign == 1.0) + { + stk::RuntimeDoomedAdHoc() << "Sign for IC cylinder must be -1 or 1.\n"; + } + } + + return new Cylinder(name, p1.data(), p2.data(), radius, sign); +} + +std::unique_ptr +parse_binder(const YAML::Node & ic_node) +{ + std::string binder_type; + if (!YAML_Parser::get_if_present(ic_node, "type", binder_type)) + { + stk::RuntimeDoomedAdHoc() << "Missing type for Binder IC.\n"; + } + + double interface_size = 0.0; + double smooth_bridge_size = 0.0; + double smooth_bridge_offset = 0.0; + double other_ls_scale_factor = 0.0; + int ibinder_type = 0; + bool root_smooth_bridge = false; + + if (binder_type == "interface") + { + ibinder_type = 0; + if (!YAML_Parser::get_if_present(ic_node, "interface_size", interface_size)) + { + stk::RuntimeDoomedAdHoc() << "Missing interface_size for IC binder.\n"; + } + } + else if (binder_type == "smooth_bridge") + { + ibinder_type = 1; + YAML_Parser::get_if_present(ic_node, "smooth_bridge_size", smooth_bridge_size); + YAML_Parser::get_if_present(ic_node, "smooth_bridge_offset", smooth_bridge_offset); + YAML_Parser::get_if_present(ic_node, "other_ls_scale_factor", other_ls_scale_factor); + YAML_Parser::get_if_present(ic_node, "root_smooth_bridge", root_smooth_bridge); + + if (smooth_bridge_size < 0.0) + { + stk::RuntimeDoomedAdHoc() << "IC binder: Smooth bridge size should not be negative.\n"; + } + + if (smooth_bridge_size != 0.0) + { + if (smooth_bridge_offset <= 0.0) + { + stk::RuntimeDoomedAdHoc() << "IC binder: Smooth bridge offset should be greater than zero when a bridge size is specified.\n"; + } + if (other_ls_scale_factor <= 0.0) + { + stk::RuntimeDoomedAdHoc() << "IC binder: Scaling of other level sets should be greater than zero when a bridge size is specified. Typical values are O(1e2).\n"; + } + } + } + else + { + stk::RuntimeDoomedAdHoc() << "Binder IC type should be either interface or smooth_bridge. \n"; + } + + return std::make_unique(interface_size, smooth_bridge_size, smooth_bridge_offset, other_ls_scale_factor, ibinder_type, root_smooth_bridge); +} + +Faceted_Surface * +parse_facets(const YAML::Node & ic_node, const stk::diag::Timer &parent_timer) +{ + std::string surface_name; + YAML_Parser::get_if_present(ic_node, "name", surface_name); + + std::string facet_filename; + if (!YAML_Parser::get_if_present(ic_node, "filename", facet_filename)) + { + stk::RuntimeDoomedAdHoc() << "Missing filename for IC facets.\n"; + } + + std::string facet_format; + if (!YAML_Parser::get_if_present(ic_node, "format", facet_format)) + { + stk::RuntimeDoomedAdHoc() << "Missing format for IC facets.\n"; + } + + bool scaleSpecified = false; + double scale = 1.0; + if (YAML_Parser::get_if_present(ic_node, "scale", scale)) + { + scaleSpecified = true; + if (scale <= 0.0) + { + stk::RuntimeDoomedAdHoc() << "Scale for IC facets must be >= 0.\n"; + } + } + + Vector3d scaleVec{scale, scale, scale}; + std::vector scaleComponents = {"scaleX","scaleY","scaleZ"}; + for (int i=0; i<3; i++) + { + if (YAML_Parser::get_if_present(ic_node, scaleComponents[i], scaleVec[i])) + { + if (scaleSpecified) + stk::RuntimeDoomedAdHoc() << "Cannot specify both scale and " << scaleComponents[i] << "\n"; + if (scaleVec[i] <= 0.0) + stk::RuntimeDoomedAdHoc() << scaleComponents[i] << " for IC facets must be >= 0.\n"; + } + } + + double sign = 1.0; + if (YAML_Parser::get_if_present(ic_node, "sign", sign)) + { + if (sign != -1.0 && sign == 1.0) + { + stk::RuntimeDoomedAdHoc() << "Sign for IC facets must be -1 or 1.\n"; + } + } + + std::transform(facet_format.begin(), facet_format.end(), facet_format.begin(), ::toupper); + Faceted_Surface_From_File * surface = nullptr; + + if (facet_format == "STL") + surface = new STLSurface(surface_name, parent_timer, facet_filename, sign, scaleVec); + else if (facet_format == "FAC") + surface = new FACSurface(surface_name, parent_timer, facet_filename, sign, scaleVec); + else if (facet_format == "PLY") + surface = new PLYSurface(surface_name, parent_timer, facet_filename, sign, scaleVec); + else if (facet_format == "EXO") + surface = new EXOSurface(surface_name, parent_timer, facet_filename, sign, scaleVec); + else + stk::RuntimeDoomedAdHoc() << "Unrecognized facet format: " << facet_format; + + return surface; +} + +MeshSurface * +parse_mesh_surface(const YAML::Node & ic_node, LevelSet & ls) +{ + std::string surface_name; + ThrowRequire(YAML_Parser::get_if_present(ic_node, "mesh", surface_name)); + + double sign = 1.0; + if (YAML_Parser::get_if_present(ic_node, "sign", sign)) + { + if (sign != -1.0 && sign == 1.0) + { + stk::RuntimeDoomedAdHoc() << "Sign for IC facets must be -1 or 1.\n"; + } + } + + ThrowErrorMsgIf(!ls.aux_meta().has_part(surface_name), "Could not locate a surface named " << surface_name); + const stk::mesh::Part & io_part = ls.aux_meta().get_part(surface_name); + + const stk::mesh::Field* coords = reinterpret_cast*>(&LevelSet::get_current_coordinates(ls.meta()).field()); + ThrowRequire(nullptr != coords); + + const stk::mesh::Selector surface_selector = stk::mesh::Selector(io_part); + return new MeshSurface(ls.meta(), *coords, surface_selector, sign); +} + +void +parse_composition_method(const YAML::Node & ic_node, IC_Alg& ic_alg) +{ + // This is a little strange because composition looks like an IC, but really sets a flag + std::string composition_method; + ThrowRequire(YAML_Parser::get_if_present(ic_node, "composition_method", composition_method)); + + std::transform(composition_method.begin(), composition_method.end(), composition_method.begin(), ::toupper); + + if (composition_method == "MINIMUM_SIGNED_DISTANCE") + ic_alg.set_composition_method(Composite_Surface::MINIMUM_SIGNED_DISTANCE); + else if (composition_method == "MAXIMUM_SIGNED_DISTANCE") + ic_alg.set_composition_method(Composite_Surface::MAXIMUM_SIGNED_DISTANCE); + else + stk::RuntimeDoomedAdHoc() << "Unrecognized composition_method: " << composition_method; +} + +void +parse_IC(const YAML::Node & ic_node, LevelSet &ls) +{ + if (ic_node.Type() == YAML::NodeType::Scalar) + { + // support random specified as Scalar (no :) + std::string ic_type = ic_node.as(); + if (ic_type == "random") + { + Random * random = new Random(0); + ls.get_IC_alg().addSurface(random); + } + else if (ic_type == "analytic_isosurface") + { + Analytic_Isosurface * surf = new Analytic_Isosurface(); + ls.get_IC_alg().addSurface(surf); + } + else + { + stk::RuntimeDoomedAdHoc() << "Unrecognized Levelset IC type: " << YAML_Parser::info(ic_node); + } + return; + } + + if ( YAML_Parser::get_null_if_present(ic_node, "sphere") ) + { + ls.get_IC_alg().addSurface(parse_sphere(ic_node)); + } + else if ( YAML_Parser::get_null_if_present(ic_node, "ellipsoid") ) + { + ls.get_IC_alg().addSurface(parse_ellipsoid(ic_node)); + } + else if ( YAML_Parser::get_null_if_present(ic_node, "plane") ) + { + ls.get_IC_alg().addSurface(parse_plane(ic_node)); + } + else if ( YAML_Parser::get_null_if_present(ic_node, "cylinder") ) + { + ls.get_IC_alg().addSurface(parse_cylinder(ic_node)); + } + else if ( YAML_Parser::get_null_if_present(ic_node, "facets") ) + { + ls.get_IC_alg().addSurface(parse_facets(ic_node, ls.get_timer())); + } + else if ( YAML_Parser::get_scalar_if_present(ic_node, "mesh") ) + { + ls.get_IC_alg().addSurface(parse_mesh_surface(ic_node, ls)); + } + else if ( YAML_Parser::get_null_if_present(ic_node, "random") ) + { + int seed = 0; + YAML_Parser::get_if_present(ic_node, "seed", seed); + Random * random = new Random(seed); + ls.get_IC_alg().addSurface(random); + } + else if ( YAML_Parser::get_null_if_present(ic_node, "binder") ) + { + ls.get_IC_alg().addCalculator(parse_binder(ic_node)); + } + else if ( YAML_Parser::get_scalar_if_present(ic_node, "composition_method") ) + { + parse_composition_method(ic_node, ls.get_IC_alg()); + } + else + { + stk::RuntimeDoomedAdHoc() << "Unrecognized Levelset IC type: " << YAML_Parser::info(ic_node); + } +} + +} + +void +IC_Parser::parse(const YAML::Node & node, LevelSet & ls) +{ + const YAML::Node ic_nodes = YAML_Parser::get_sequence_if_present(node, "initial_conditions"); + if ( ic_nodes ) + { + for ( auto && ic_node : ic_nodes ) + { + parse_IC(ic_node, ls); + } + } +} + +} // namespace krino diff --git a/packages/krino/krino/parser/Akri_IC_Parser.hpp b/packages/krino/krino/parser/Akri_IC_Parser.hpp new file mode 100644 index 000000000000..833d49664d92 --- /dev/null +++ b/packages/krino/krino/parser/Akri_IC_Parser.hpp @@ -0,0 +1,21 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_IC_Parser_h +#define Akri_IC_Parser_h + +namespace YAML { class Node; } +namespace krino { class LevelSet; } + +namespace krino { +namespace IC_Parser { + void parse(const YAML::Node & node, LevelSet & ls); +} +} + +#endif // Akri_IC_Parser_h diff --git a/packages/krino/krino/parser/Akri_LevelSet_Parser.cpp b/packages/krino/krino/parser/Akri_LevelSet_Parser.cpp new file mode 100644 index 000000000000..8196db1a77e1 --- /dev/null +++ b/packages/krino/krino/parser/Akri_LevelSet_Parser.cpp @@ -0,0 +1,167 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace krino { + +namespace { + +void +register_blocks_for_level_set(RegionInterface & reg, LevelSet & ls) +{ + const std::string composite_name = ls.get_composite_name(); + if (!composite_name.empty()) + { + LS_SideTag::declare_composite(ls.get_identifier(), LevelSet::get_identifier(composite_name)); + } + + Phase_Support & phase_support = Phase_Support::get(reg.get_stk_mesh_meta_data()); + const PhaseVec & mesh_phases = Phase_Support::get_phases(reg.name_of_input_mesh()); + const std::vector ls_phases = Phase_Support::get_level_set_phases(mesh_phases, ls); + const std::vector decomposed_blocks = phase_support.get_blocks_decomposed_by_levelset(ls_phases); + phase_support.register_blocks_for_level_set(&ls, decomposed_blocks); +} + +} + +void +LevelSet_Parser::parse(const YAML::Node & region_node, RegionInterface & region) +{ + const YAML::Node ls_nodes = YAML_Parser::get_sequence_if_present(region_node, "level_set_interfaces"); + if ( ls_nodes ) + { + for ( auto && ls_node : ls_nodes ) + { + std::string ls_name; + YAML_Parser::get_if_present(ls_node, "name", ls_name); + if (ls_name.empty()) + { + stk::RuntimeDoomedAdHoc() << "Blank or missing levelset name.\n"; + } + LevelSet & ls = LevelSet::build(region.get_stk_mesh_meta_data(), ls_name, region.getRegionTimer()); + + std::string distance_name; + if (krino::YAML_Parser::get_if_present(ls_node, "distance_variable", distance_name)) + { + ls.set_distance_name(distance_name); + } + + std::vector extension_velocity; + if (krino::YAML_Parser::get_if_present(ls_node, "extension_velocity", extension_velocity)) + { + if (extension_velocity.size() != ls.spatial_dimension) + { + stk::RuntimeDoomedAdHoc() << "Expecting " << ls.spatial_dimension << " real values for extension_velocity for level set " << ls_name << ".\n"; + } + ls.set_extension_velocity(Vector3d(extension_velocity.data(), extension_velocity.size())); + } + + double narrow_band_multiplier = 0.0; + if (YAML_Parser::get_if_present(ls_node, "narrow_band_element_size_multiplier", narrow_band_multiplier)) + { + if( narrow_band_multiplier < 0. ) + { + stk::RuntimeDoomedAdHoc() << "Error: Narrow band element size multiplier must be >= 0.\n"; + } + else if ( narrow_band_multiplier < 1. ) + { + stk::RuntimeWarningAdHoc() << "Narrow band element size multiplier is less than 1. " + << "Except in certain cases of adaptive refinement around the interface, this will produce errors in the distance field." + << std::endl; + } + ls.narrow_band_multiplier(narrow_band_multiplier); + } + + std::string redistance_method_name; + if (YAML_Parser::get_if_present(ls_node, "redistance_method", redistance_method_name)) + { + std::transform(redistance_method_name.begin(), redistance_method_name.end(), redistance_method_name.begin(), ::toupper); + Redistance_Method redistance_method = CLOSEST_POINT; + if (redistance_method_name == "CLOSEST_POINT") + redistance_method = CLOSEST_POINT; + else if (redistance_method_name == "FAST_MARCHING") + redistance_method = FAST_MARCHING; + else + stk::RuntimeWarningAdHoc() << "Unrecognized redistance method: " << redistance_method_name << std::endl; + + ls.set_redistance_method(redistance_method); + } + + bool perform_initial_redistance; + if (YAML_Parser::get_if_present(ls_node, "perform_initial_redistance", perform_initial_redistance)) + { + ls.perform_initial_redistance(perform_initial_redistance); + } + + double initial_offset_distance = 0.0; + if (YAML_Parser::get_if_present(ls_node, "initial_offset_distance", initial_offset_distance)) + { + ls.set_ic_offset(initial_offset_distance); + } + + double initial_scale_factor = 1.0; + if (YAML_Parser::get_if_present(ls_node, "initial_scale_factor", initial_scale_factor)) + { + ls.set_ic_scale(initial_scale_factor); + } + + double narrow_band_size = 0.0; + if (YAML_Parser::get_if_present(ls_node, "narrow_band_size", narrow_band_size)) + { + if( narrow_band_size < 0. ) + { + stk::RuntimeDoomedAdHoc() << "Error: Narrow band size must be >= 0.\n"; + } + ls.narrow_band_size(narrow_band_size); + } + + const YAML::Node comp_dist_surfs = YAML_Parser::get_sequence_if_present(ls_node, "compute_surface_distance"); + if ( comp_dist_surfs ) + { + std::vector compute_distance_surfaces; + for ( auto && comp_dist_surf : comp_dist_surfs ) + { + const std::string surface_name = comp_dist_surf.as(); + + ThrowErrorMsgIf( !ls.aux_meta().has_part(surface_name), + "Could not locate a surface named " << surface_name); + + stk::mesh::Part & io_part = ls.aux_meta().get_part(surface_name); + ThrowErrorMsgIf( ls.meta().side_rank() != io_part.primary_entity_rank(), + "Part " << surface_name << " is not a side-rank part."); + + ls.get_compute_surface_distance_parts().push_back(&io_part); + compute_distance_surfaces.push_back(surface_name); + } + ThrowErrorMsgIf( ls.get_compute_surface_distance_parts().empty(), + "Please specify surfaces for compute surface distance."); + } + + IC_Parser::parse(ls_node, ls); + + register_blocks_for_level_set(region, ls); + } + } +} + +} // namespace krino diff --git a/packages/krino/krino/parser/Akri_LevelSet_Parser.hpp b/packages/krino/krino/parser/Akri_LevelSet_Parser.hpp new file mode 100644 index 000000000000..8016393f28f7 --- /dev/null +++ b/packages/krino/krino/parser/Akri_LevelSet_Parser.hpp @@ -0,0 +1,21 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_LevelSet_Parser_h +#define Akri_LevelSet_Parser_h + +namespace YAML { class Node; } +namespace krino { class RegionInterface; } + +namespace krino { +namespace LevelSet_Parser { + void parse(const YAML::Node & node, RegionInterface & region); +} +} + +#endif // Akri_LevelSet_Parser_h diff --git a/packages/krino/krino/parser/Akri_MeshInput_Parser.cpp b/packages/krino/krino/parser/Akri_MeshInput_Parser.cpp new file mode 100644 index 000000000000..d455924ffa11 --- /dev/null +++ b/packages/krino/krino/parser/Akri_MeshInput_Parser.cpp @@ -0,0 +1,181 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include + +#include +#include +#include +#include + +#include + +namespace krino { + +void +MeshInput_Parser::parse(const YAML::Node & base_node) +{ + const YAML::Node fem_nodes = YAML_Parser::get_sequence_if_present(base_node, "finite_element_models"); + if (!fem_nodes) return; + + for ( auto && fem_node : fem_nodes ) + { + std::string model_name; + YAML_Parser::get_if_present(fem_node, "name", model_name); + if (model_name.empty()) + { + stk::RuntimeDoomedAdHoc() << "Missing finite element model name.\n"; + } + std::shared_ptr options = MeshInputOptions::get_or_create(model_name); + + const bool has_generated_mesh = parse_generated_mesh(fem_node, *options); + + std::string mesh_name; + YAML_Parser::get_if_present(fem_node, "mesh", mesh_name); + if (!mesh_name.empty() && !has_generated_mesh) + { + options->set_filename(mesh_name); + } + else if (!has_generated_mesh) + { + stk::RuntimeDoomedAdHoc() << "Must specify input mesh name or generated mesh options.\n"; + } + + std::string decomposition_method; + if (YAML_Parser::get_if_present(fem_node, "decomposition_method", decomposition_method)) + { + // no checking here for validity + std::transform(decomposition_method.begin(), decomposition_method.end(), decomposition_method.begin(), ::toupper); + options->set_decomposition_method(decomposition_method); + } + + Phase_Parser::parse(fem_node, model_name); + } +} + +bool +MeshInput_Parser::parse_generated_mesh(const YAML::Node & fem_node, MeshInputOptions & options) +{ + const YAML::Node generated_mesh_node = YAML_Parser::get_map_if_present(fem_node, "generated_mesh"); + if (!generated_mesh_node) return false; + + double mesh_size = 0.0; + if (!YAML_Parser::get_if_present(generated_mesh_node, "mesh_size", mesh_size) || mesh_size < 0.0) + { + stk::RuntimeDoomedAdHoc() << "Missing or invalid mesh_size for generated_mesh.\n"; + } + options.set_generated_mesh_size(mesh_size); + + std::vector domain; + if (YAML_Parser::get_if_present(generated_mesh_node, "domain", domain)) + { + if (options.get_generated_mesh_domain_type() != MeshInputOptions::NO_GENERATED_MESH) + { + stk::RuntimeDoomedAdHoc() << "Must only specify domain or interface_bounding_box_with_dimension options.\n"; + } + options.set_generated_mesh_domain_type(MeshInputOptions::GENERATED_MESH_FOR_SPECIFIED_DOMAIN); + if (domain.size() != 4 && domain.size() != 6) + { + stk::RuntimeDoomedAdHoc() << "Domain must be a vector of length 4 for 2D or 6 for 3D (xmin,ymin,zmin, xmax,ymax,zmax).\n"; + } + options.set_generated_mesh_domain(domain); + } + + int interface_spatial_dim = 0; + if (YAML_Parser::get_if_present(generated_mesh_node, "interface_bounding_box_with_dimension", interface_spatial_dim)) + { + if (options.get_generated_mesh_domain_type() != MeshInputOptions::NO_GENERATED_MESH) + { + stk::RuntimeDoomedAdHoc() << "Must only specify domain or interface_bounding_box options.\n"; + } + if (interface_spatial_dim != 2 && interface_spatial_dim != 3) + { + stk::RuntimeDoomedAdHoc() << "interface_bounding_box_with_dimension only support 2 or 3 dimensions.\n"; + } + const MeshInputOptions::GeneratedMeshDomainType mesh_type = + interface_spatial_dim == 2 ? + MeshInputOptions::GENERATED_2D_MESH_FOR_INTERFACE_BOUNDING_BOX : + MeshInputOptions::GENERATED_3D_MESH_FOR_INTERFACE_BOUNDING_BOX; + options.set_generated_mesh_domain_type(mesh_type); + } + + std::string generated_mesh_element_type_string; + if (YAML_Parser::get_if_present(generated_mesh_node, "element_type", generated_mesh_element_type_string)) + { + std::transform(generated_mesh_element_type_string.begin(), generated_mesh_element_type_string.end(), generated_mesh_element_type_string.begin(), ::toupper); + static std::map valid_entries = + { {"TRIANGLE", stk::topology::TRIANGLE_3_2D}, + {"TRI", stk::topology::TRIANGLE_3_2D}, + {"QUADRILATERAL", stk::topology::QUADRILATERAL_4_2D}, + {"QUAD", stk::topology::QUADRILATERAL_4_2D}, + {"HEXAHEDRON", stk::topology::HEXAHEDRON_8}, + {"HEX", stk::topology::HEXAHEDRON_8}, + {"TETRAHEDRON", stk::topology::TETRAHEDRON_4}, + {"TET", stk::topology::TETRAHEDRON_4} }; + auto it = valid_entries.find(generated_mesh_element_type_string); + if (it == valid_entries.end()) + { + stk::RuntimeDoomedAdHoc() << "Invalid cdfem_simplex_generation_method type: " << YAML_Parser::info(generated_mesh_node); + } + else + { + options.set_generated_mesh_element_type( it->second ); + if (options.get_generated_mesh_spatial_dimension() != (int)options.get_generated_mesh_element_type().dimension()) + { + stk::RuntimeDoomedAdHoc() << "Mismatch in spatial dimension for generated mesh element type and domain specification. "; + } + } + } + else + { + stk::topology default_topology = + (options.get_generated_mesh_spatial_dimension() == 2) ? + stk::topology::TRIANGLE_3_2D : + stk::topology::TETRAHEDRON_4; + options.set_generated_mesh_element_type( default_topology ); + } + + std::string generated_mesh_type_string; + if (YAML_Parser::get_if_present(generated_mesh_node, "mesh_type", generated_mesh_type_string)) + { + std::transform(generated_mesh_type_string.begin(), generated_mesh_type_string.end(), generated_mesh_type_string.begin(), ::toupper); + static std::map valid_entries = + { {"CUBIC", BoundingBoxMeshStructureType::CUBIC_BOUNDING_BOX_MESH}, + {"BCC", BoundingBoxMeshStructureType::BCC_BOUNDING_BOX_MESH}, + {"FLAT_WALLED_BCC", BoundingBoxMeshStructureType::FLAT_WALLED_BCC_BOUNDING_BOX_MESH}, + {"TRIANGULAR_LATTICE", BoundingBoxMeshStructureType::TRIANGULAR_LATTICE_BOUNDING_BOX_MESH}, + {"FLAT_WALLED_TRIANGULAR_LATTICE", BoundingBoxMeshStructureType::FLAT_WALLED_TRIANGULAR_LATTICE_BOUNDING_BOX_MESH}}; + auto it = valid_entries.find(generated_mesh_type_string); + if (it == valid_entries.end()) + { + stk::RuntimeDoomedAdHoc() << "Invalid mesh_type: " << YAML_Parser::info(generated_mesh_node); + } + else + { + options.set_generated_mesh_structure_type(it->second); + if ((BoundingBoxMeshStructureType::BCC_BOUNDING_BOX_MESH == it->second || BoundingBoxMeshStructureType::FLAT_WALLED_BCC_BOUNDING_BOX_MESH == it->second) && options.get_generated_mesh_spatial_dimension() != 3) + { + stk::RuntimeDoomedAdHoc() << "BCC meshes only supported in 3D"; + } + if ((BoundingBoxMeshStructureType::TRIANGULAR_LATTICE_BOUNDING_BOX_MESH == it->second || BoundingBoxMeshStructureType::FLAT_WALLED_TRIANGULAR_LATTICE_BOUNDING_BOX_MESH == it->second) && options.get_generated_mesh_spatial_dimension() != 2) + { + stk::RuntimeDoomedAdHoc() << "BCC meshes only supported in 2D"; + } + } + } + + if (options.get_generated_mesh_domain_type() == MeshInputOptions::NO_GENERATED_MESH) + { + stk::RuntimeDoomedAdHoc() << "Must specify domain or interface_bounding_box_with_dimension for generated_mesh.\n"; + } + return true; +} + + +} // namespace krino diff --git a/packages/krino/krino/parser/Akri_MeshInput_Parser.hpp b/packages/krino/krino/parser/Akri_MeshInput_Parser.hpp new file mode 100644 index 000000000000..1dcd2342aa5d --- /dev/null +++ b/packages/krino/krino/parser/Akri_MeshInput_Parser.hpp @@ -0,0 +1,22 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_MeshInput_Parser_h +#define Akri_MeshInput_Parser_h + +namespace YAML { class Node; } +namespace krino { class MeshInputOptions; } + +namespace krino { +namespace MeshInput_Parser { + void parse(const YAML::Node & node); + bool parse_generated_mesh(const YAML::Node & fem_node, MeshInputOptions & options); +} +} + +#endif // Akri_MeshInput_Parser_h diff --git a/packages/krino/krino/parser/Akri_Phase_Parser.cpp b/packages/krino/krino/parser/Akri_Phase_Parser.cpp new file mode 100644 index 000000000000..64f74ac76a99 --- /dev/null +++ b/packages/krino/krino/parser/Akri_Phase_Parser.cpp @@ -0,0 +1,101 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include + +#include + +namespace krino { + +void +Phase_Parser::parse(const YAML::Node & fem_node, const std::string & fem_model_name) +{ + PhaseVec & mesh_phases = Phase_Support::get_phases(fem_model_name); + + const YAML::Node subdom_nodes = YAML_Parser::get_sequence_if_present(fem_node, "subdomains"); + + if (subdom_nodes) + { + for ( auto && subdom_node : subdom_nodes ) + { + std::string subdom_name; + YAML_Parser::get_if_present(subdom_node, "name", subdom_name); + if (subdom_name.empty()) + { + stk::RuntimeDoomedAdHoc() << "Missing subdomain name in finite element model " << fem_model_name; + } + mesh_phases.push_back(NamedPhase(subdom_name)); + + const YAML::Node where_nodes = YAML_Parser::get_sequence_if_present(subdom_node, "level_set_regions"); + + if (where_nodes) + { + for ( auto && where_node : where_nodes ) + { + std::string negative_ls_name; + if (YAML_Parser::get_if_present(where_node, "negative_level_set", negative_ls_name)) + { + mesh_phases.back().tag().add(LevelSet::get_identifier(negative_ls_name),-1); + } + + std::string positive_ls_name; + if (YAML_Parser::get_if_present(where_node, "positive_level_set", positive_ls_name)) + { + mesh_phases.back().tag().add(LevelSet::get_identifier(positive_ls_name),+1); + } + } + } + + std::string smallest_ls_name; + if (YAML_Parser::get_if_present(subdom_node, "smallest_level_set", smallest_ls_name)) + { + if ( where_nodes ) + { + stk::RuntimeDoomedAdHoc() << "Cannot combine \"negative_level_set|positive_level_set\" syntax with \"smallest_level_set\" syntax."; + } + Phase_Support::set_one_levelset_per_phase(true); + mesh_phases.back().tag().add(LevelSet::get_identifier(smallest_ls_name),-1); + } + } + } + + const YAML::Node part_nodes = YAML_Parser::get_sequence_if_present(fem_node, "parts"); + + if (part_nodes) + { + std::map> & FEmodel_block_phase_names = Phase_Support::get_block_phases_by_name(fem_model_name); + + for ( auto && part_node : part_nodes ) + { + std::string part_name; + YAML_Parser::get_if_present(part_node, "name", part_name); + if (part_name.empty()) + { + stk::RuntimeDoomedAdHoc() << "Missing part name in finite element model " << fem_model_name; + } + + const YAML::Node part_subdom_nodes = YAML_Parser::get_sequence_if_present(part_node, "subdomains"); + if (part_subdom_nodes) + { + std::vector & block_phase_names = FEmodel_block_phase_names[part_name]; + for ( auto && subdom_node : part_subdom_nodes ) + { + const std::string subdom_name = subdom_node.as(); + block_phase_names.push_back(subdom_name); + } + } + } + } +} + +} // namespace krino diff --git a/packages/krino/krino/parser/Akri_Phase_Parser.hpp b/packages/krino/krino/parser/Akri_Phase_Parser.hpp new file mode 100644 index 000000000000..53c0f08b7c0f --- /dev/null +++ b/packages/krino/krino/parser/Akri_Phase_Parser.hpp @@ -0,0 +1,22 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Phase_Parser_h +#define Akri_Phase_Parser_h + +#include + +namespace YAML { class Node; } + +namespace krino { +namespace Phase_Parser { + void parse(const YAML::Node & fem_node, const std::string & fem_model_name); +} +} + +#endif // Akri_Phase_Parser_h diff --git a/packages/krino/krino/parser/Akri_Region_Parser.cpp b/packages/krino/krino/parser/Akri_Region_Parser.cpp new file mode 100644 index 000000000000..56310b64bade --- /dev/null +++ b/packages/krino/krino/parser/Akri_Region_Parser.cpp @@ -0,0 +1,84 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace krino { + +void +Region_Parser::parse(const YAML::Node & simulation_node, Simulation & simulation) +{ + const YAML::Node region_nodes = YAML_Parser::get_sequence_if_present(simulation_node, "regions"); + if ( !region_nodes ) return; + + for ( auto && region_node : region_nodes ) + { + std::string region_name; + YAML_Parser::get_if_present(region_node, "name", region_name); + if (region_name.empty()) + { + stk::RuntimeDoomedAdHoc() << "Blank or missing region name.\n"; + } + Region * region = new Region(simulation, region_name ); + + int initial_refinement_levels = 0; + if (YAML_Parser::get_if_present(region_node, "initial_uniform_refinement_levels", initial_refinement_levels)) + { + region->set_initial_refinement_levels(initial_refinement_levels); + } + + bool use_32bit_ids = false; + YAML_Parser::get_if_present(region_node, "use_32bit_ids", use_32bit_ids); + + bool force_64bit_ids = !use_32bit_ids; + YAML_Parser::get_if_present(region_node, "force_64bit_ids", force_64bit_ids); + + if (use_32bit_ids && force_64bit_ids) + { + stk::RuntimeDoomedAdHoc() << "Can't specify options use_32bit_ids=true and force_64bit_ids=true together in region " << region_name << "\n"; + } + + std::string fem_model_name; + YAML_Parser::get_if_present(region_node, "finite_element_model", fem_model_name); + if (fem_model_name.empty()) + { + stk::RuntimeDoomedAdHoc() << "Blank or missing finite element model for region " << region_name << "\n"; + } + region->associate_input_mesh(fem_model_name, use_32bit_ids, force_64bit_ids); + + RegionInterface::set_currently_parsed_region(region->get_stk_mesh_meta_data(), region->name(), region->getRegionTimer(), region->name_of_input_mesh(), region->get_input_io_region()); + + Phase_Support & phase_support = krino::Phase_Support::get(region->get_stk_mesh_meta_data()); + const PhaseVec & mesh_phases = Phase_Support::get_phases(region->name_of_input_mesh()); + const PartnamePhasenameMap & mesh_block_phase_names = Phase_Support::get_block_phases_by_name(region->name_of_input_mesh()); + phase_support.determine_block_phases(mesh_phases, mesh_block_phase_names); + + + const Block_Surface_Connectivity block_surf_info(region->get_stk_mesh_meta_data()); + phase_support.set_input_block_surface_connectivity(block_surf_info); + phase_support.setup_phases(mesh_phases); + + LevelSet_Parser::parse(region_node, RegionInterface::get_currently_parsed_region()); + CDFEM_Options_Parser::parse(region_node, RegionInterface::get_currently_parsed_region()); + ResultsOutput_Parser::parse(region_node, *region); + } +} + +} // namespace krino diff --git a/packages/krino/krino/parser/Akri_Region_Parser.hpp b/packages/krino/krino/parser/Akri_Region_Parser.hpp new file mode 100644 index 000000000000..a860592c7b53 --- /dev/null +++ b/packages/krino/krino/parser/Akri_Region_Parser.hpp @@ -0,0 +1,21 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Region_Parser_h +#define Akri_Region_Parser_h + +namespace YAML { class Node; } +namespace krino { class Simulation; } + +namespace krino { +namespace Region_Parser { + void parse(const YAML::Node & node, Simulation & simulation); +} +} + +#endif // Akri_Region_Parser_h diff --git a/packages/krino/krino/parser/Akri_ResultsOutput_Parser.cpp b/packages/krino/krino/parser/Akri_ResultsOutput_Parser.cpp new file mode 100644 index 000000000000..a94751ac5e60 --- /dev/null +++ b/packages/krino/krino/parser/Akri_ResultsOutput_Parser.cpp @@ -0,0 +1,72 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include + +#include + +namespace krino { + +void +ResultsOutput_Parser::parse(const YAML::Node & region_node, Region & region) +{ + const YAML::Node results_node = YAML_Parser::get_map_if_present(region_node, "output"); + if ( results_node ) + { + ResultsOutputOptions * options = region.get_results_options(); + + std::string results_database_name; + if (YAML_Parser::get_if_present(results_node, "database_name", results_database_name)) + { + options->set_filename(results_database_name); + } + else + { + stk::RuntimeDoomedAdHoc() << "Missing results database_name for region " << region.name() << ".\n"; + } + + std::string results_title; + if (YAML_Parser::get_if_present(results_node, "title", results_title)) + { + options->set_title(results_title); + } + + bool compose_results; + if (YAML_Parser::get_if_present(results_node, "compose_results", compose_results)) + { + if (compose_results) options->add_property(Ioss::Property("COMPOSE_RESULTS", 1)); + } + + int output_frequency = 1; + if (YAML_Parser::get_if_present(results_node, "output_frequency", output_frequency)) + { + options->add_step_increment(0, output_frequency); + } + + const YAML::Node nodal_variable_nodes = YAML_Parser::get_sequence_if_present(results_node, "nodal_variables"); + for ( auto && variable_node : nodal_variable_nodes ) + { + const std::string field_name = variable_node.as(); + options->add_nodal_field(field_name, field_name); + } + + const YAML::Node element_variable_nodes = YAML_Parser::get_sequence_if_present(results_node, "element_variables"); + for ( auto && variable_node : element_variable_nodes ) + { + const std::string field_name = variable_node.as(); + options->add_element_field(field_name, field_name); + } + } +} + +} // namespace krino diff --git a/packages/krino/krino/parser/Akri_ResultsOutput_Parser.hpp b/packages/krino/krino/parser/Akri_ResultsOutput_Parser.hpp new file mode 100644 index 000000000000..352a0947d673 --- /dev/null +++ b/packages/krino/krino/parser/Akri_ResultsOutput_Parser.hpp @@ -0,0 +1,21 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_ResultsOutput_Parser_h +#define Akri_ResultsOutput_Parser_h + +namespace YAML { class Node; } +namespace krino { class Region; } + +namespace krino { +namespace ResultsOutput_Parser { + void parse(const YAML::Node & node, Region & region); +} +} + +#endif // Akri_ResultsOutput_Parser_h diff --git a/packages/krino/krino/parser/Akri_Simulation_Parser.cpp b/packages/krino/krino/parser/Akri_Simulation_Parser.cpp new file mode 100644 index 000000000000..be02d91cbf15 --- /dev/null +++ b/packages/krino/krino/parser/Akri_Simulation_Parser.cpp @@ -0,0 +1,54 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include + +#include + +namespace krino { + +void +Simulation_Parser::parse(const YAML::Node & node) +{ + const YAML::Node sim_node = YAML_Parser::get_map_if_present(node, "simulation"); + if ( sim_node ) + { + Simulation & simulation = Simulation::build("krino simulation"); + + double start_time = 0.0; + if (YAML_Parser::get_if_present(sim_node, "start_time", start_time)) + { + simulation.set_current_time(start_time); + } + + double stop_time = 0.0; + if (YAML_Parser::get_if_present(sim_node, "stop_time", stop_time)) + { + simulation.set_stop_time(stop_time); + } + + double time_step = 0.0; + if (YAML_Parser::get_if_present(sim_node, "time_step", time_step)) + { + simulation.set_time_step(time_step); + } + + Region_Parser::parse(sim_node, simulation); + } + else + { + stk::RuntimeDoomedAdHoc() << "Missing simulation.\n"; + } +} + +} // namespace krino diff --git a/packages/krino/krino/parser/Akri_Simulation_Parser.hpp b/packages/krino/krino/parser/Akri_Simulation_Parser.hpp new file mode 100644 index 000000000000..45c02b36ca47 --- /dev/null +++ b/packages/krino/krino/parser/Akri_Simulation_Parser.hpp @@ -0,0 +1,20 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Simulation_Parser_h +#define Akri_Simulation_Parser_h + +namespace YAML { class Node; } + +namespace krino { +namespace Simulation_Parser { + void parse(const YAML::Node & node); +} +} + +#endif // Akri_Simulation_Parser_h diff --git a/packages/krino/krino/parser/Akri_YAML.hpp b/packages/krino/krino/parser/Akri_YAML.hpp new file mode 100644 index 000000000000..1a5de9994874 --- /dev/null +++ b/packages/krino/krino/parser/Akri_YAML.hpp @@ -0,0 +1,48 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_KRINO_PARSER_AKRI_YAML_HPP_ +#define KRINO_KRINO_PARSER_AKRI_YAML_HPP_ +#include + +#ifdef KRINO_HAVE_YAML + +#ifdef __INTEL_COMPILER +#include +#else +//YAML has shadowed variables +//this disables the checking on GCC only +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#include +#pragma GCC diagnostic pop +#endif + +#else +// Fake struct to mimic YAML Node if we don't have YAML +namespace YAML { + struct NodeType { + enum value { Undefined, Null, Scalar, Sequence, Map }; + }; + + struct Node { + NodeType::value Type() const { return NodeType::Null; } + + template const T as() const { return T(); } + + explicit operator bool() const { return false; } + + Node * begin() const { return nullptr; } + Node * end() const { return nullptr; } + + Node operator[](std::string) const { return Node(); } + }; +} +#endif + +#endif /* KRINO_KRINO_PARSER_AKRI_YAML_HPP_ */ diff --git a/packages/krino/krino/parser/Akri_YAML_Parser.cpp b/packages/krino/krino/parser/Akri_YAML_Parser.cpp new file mode 100644 index 000000000000..9f1e4df98276 --- /dev/null +++ b/packages/krino/krino/parser/Akri_YAML_Parser.cpp @@ -0,0 +1,235 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#ifdef KRINO_HAVE_YAML + +static void emit(YAML::Emitter& emout, const YAML::Node & node) { + // recursive depth first + YAML::NodeType::value type = node.Type(); + std::string out; + switch (type) + { + case YAML::NodeType::Scalar: + out = node.as(); + emout << out; + break; + case YAML::NodeType::Sequence: + emout << YAML::BeginSeq; + for (unsigned int i = 0; i < node.size(); ++i) { + const YAML::Node & subnode = node[i]; + emit(emout, subnode); + } + emout << YAML::EndSeq; + break; + case YAML::NodeType::Map: + emout << YAML::BeginMap ; + for (auto && entry : node) { + const YAML::Node & key = entry.first; + const YAML::Node & value = entry.second; + out = key.as(); + emout << YAML::Key << out; + emout << YAML::Value; + emit(emout, value); + } + emout << YAML::EndMap ; + break; + case YAML::NodeType::Null: + emout << " (empty) "; + break; + default: + std::cerr << "Warning: emit: unknown/unsupported node type" << std::endl; + break; + } +} + +/// uses Emitter to print node to stream +static void emit(std::ostream& sout, const YAML::Node & node) { + YAML::Emitter out; + emit(out,node); + sout << out.c_str() << std::endl; +} + +static YAML::Node parse_yaml(std::istream & input_stream) +{ + return YAML::Load(input_stream); + YAML::Node doc; + + try { + doc = YAML::Load(input_stream); + } + catch (YAML::ParserException &e) { + throw std::runtime_error(e.what()); + } + + return doc; +} + +static std::string line_info(const YAML::Node & node) { + std::ostringstream sout; + sout << "(line,column) = (" + << node.Mark().line+1 << ", " + << node.Mark().column+1 << ")"; + return sout.str(); +} + +#else + +static YAML::Node parse_yaml(std::istream & input_stream) { throw std::runtime_error("YAML parser not enabled in krino build."); } +static void emit(std::ostream& sout, const YAML::Node & node) {} +static std::string line_info(const YAML::Node & node) { return std::string(); } + +#endif + +namespace krino{ +namespace YAML_Parser { + +std::string info(const YAML::Node & node) { + std::ostringstream sout; + sout << "Node at " << line_info(node) << " => \n" ; + emit(sout, node); + return sout.str(); +} + +const YAML::Node +get_if_present(const YAML::Node& node, const std::string& key) +{ + static std::string types[] = {"Null", "Scalar", "Sequence", "Map"}; + std::ostringstream err_msg; + + if (node.Type() == YAML::NodeType::Null || node.Type() == YAML::NodeType::Scalar) + { + emit(err_msg, node); + err_msg << "Check structure of input file."; + stk::RuntimeDoomedAdHoc() + << "parsing within non-searchable node of type = " << types[node.Type()] + << " for key= " << key + << " at " << line_info(node) + << " node= " << err_msg.str() << std::endl; + YAML::Node empty; + return empty; + } + return node[key]; +} + +const YAML::Node +get_type_if_present(const YAML::Node& node, const std::string& key, YAML::NodeType::value type) +{ + static std::string types[] = {"Null", "Scalar", "Sequence", "Map"}; + std::ostringstream err_msg; + + const YAML::Node value = get_if_present(node, key); + if (value && (value.Type() != type)) + { + emit(err_msg, node); + err_msg << "Check indentation of input file."; + stk::RuntimeDoomedAdHoc() + << "parsing expected type " << types[type] << " got type = " << types[value.Type()] + << " for key= " << key + << " at " << line_info(node) + << " node= " << err_msg.str() << std::endl; + } + return value; +} + +// template specialization +template<> +bool get_if_present(const YAML::Node & node, const std::string& key, bool& result) +{ + std::string resultString; + const bool haveNode = get_if_present(node, key, resultString); + + if (haveNode) + { + result = false; + std::transform(resultString.begin(), resultString.end(), resultString.begin(), ::toupper); + if (resultString == "TRUE" || resultString == "YES" || resultString == "1") + result = true; + else if (resultString == "FALSE" || resultString == "NO" || resultString == "0") + result = false; + else + { + std::ostringstream err_msg; + emit(err_msg, node); + stk::RuntimeDoomedAdHoc() << "Failed to parse boolean " + << " for key= " << key + << " at " << line_info(node) + << " node= " << err_msg.str() << std::endl; + } + return true; + } + return false; +} + + +void parse() +{/* %TRACE% */ Trace trace__("krino::YAML_Parser::parse()"); /* %TRACE% */ + const std::string inputFileName = stk::EnvData::getInputFileName(); + const std::string &aprepro = sierra::Env::get_param("aprepro"); + const bool use_aprepro = aprepro.empty() || aprepro != "off"; + const std::string &aprepro_defines = sierra::Env::get_param("define"); + + // parallel version of input file + mpi_filebuf input_parallel_file(use_aprepro, aprepro_defines); + if (!input_parallel_file.open(stk::EnvData::parallel_comm(), 0, std::ios::in, inputFileName.c_str())) { + throw std::runtime_error("failed to open input file"); + } + if(use_aprepro) { + sierra::Env::outputP0()< +#include +#include + +namespace krino{ +namespace YAML_Parser { + +void parse(); +std::string info(const YAML::Node & node); + +const YAML::Node +get_if_present(const YAML::Node& node, const std::string& key); + +/// these can be used to check and ensure a type of yaml node is as expected +const YAML::Node +get_type_if_present(const YAML::Node& node, const std::string& key, YAML::NodeType::value type); + +inline const YAML::Node +get_null_if_present(const YAML::Node& node, const std::string& key) {return get_type_if_present(node, key, YAML::NodeType::Null); } + +inline const YAML::Node +get_scalar_if_present(const YAML::Node& node, const std::string& key) {return get_type_if_present(node, key, YAML::NodeType::Scalar); } + +inline const YAML::Node +get_sequence_if_present(const YAML::Node& node, const std::string& key) {return get_type_if_present(node, key, YAML::NodeType::Sequence); } + +inline const YAML::Node +get_map_if_present(const YAML::Node& node, const std::string& key) {return get_type_if_present(node, key, YAML::NodeType::Map); } + +/// Doesn't change @param result unless the @param key is present in the @param node +template +bool get_if_present(const YAML::Node & node, const std::string& key, T& result) +{ + const YAML::Node value = get_if_present(node, key); + if (value) + { + result = value.as(); + return true; + } + return false; +} + +template<> +bool get_if_present(const YAML::Node & node, const std::string& key, bool& result); + +} // namespace parser +} // namespace krino + +#endif // Akri_YAML_Parser_h diff --git a/packages/krino/krino/parser/CMakeLists.txt b/packages/krino/krino/parser/CMakeLists.txt new file mode 100644 index 000000000000..66cbc42a19c1 --- /dev/null +++ b/packages/krino/krino/parser/CMakeLists.txt @@ -0,0 +1,19 @@ +SET(HEADERS "") +SET(SOURCES "") + +INCLUDE_DIRECTORIES(${${PACKAGE_NAME}_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +FILE(GLOB HEADERS *.hpp) +FILE(GLOB SOURCES *.cpp) + +TRIBITS_ADD_LIBRARY( + krino_parser_lib + HEADERS ${HEADERS} + SOURCES ${SOURCES} + DEPLIBS krino_lib krino_region_lib) + +INSTALL(FILES ${HEADERS} DESTINATION + ${CMAKE_INSTALL_PREFIX}/${${PROJECT_NAME}_INSTALL_INCLUDE_DIR}/krino_parser_lib) + diff --git a/packages/krino/krino/rebalance_utils/Akri_RebalanceUtils.cpp b/packages/krino/krino/rebalance_utils/Akri_RebalanceUtils.cpp new file mode 100644 index 000000000000..432c925d4a24 --- /dev/null +++ b/packages/krino/krino/rebalance_utils/Akri_RebalanceUtils.cpp @@ -0,0 +1,230 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include + +namespace krino { +namespace rebalance_utils { + +namespace { + +class MultipleCriteriaSettings : public stk::balance::GraphCreationSettings +{ +public: + MultipleCriteriaSettings(stk::mesh::BulkData & stkMeshBulkData, + const std::vector *> critFields, + const double default_weight = 0.0) + : m_stkMeshBulkData(stkMeshBulkData), + m_critFields(critFields), + m_defaultWeight(default_weight) + { + method = "rcb"; + setUseNodeBalancer(true); + setNodeBalancerTargetLoadBalance(getImbalanceTolerance()); + setNodeBalancerMaxIterations(10); + } + virtual ~MultipleCriteriaSettings() = default; + + virtual double + getGraphEdgeWeight(stk::topology element1Topology, stk::topology element2Topology) const override + { + return 1.0; + } + virtual bool areVertexWeightsProvidedViaFields() const override { return true; } + virtual bool includeSearchResultsInGraph() const override { return false; } + virtual int getGraphVertexWeight(stk::topology type) const override { return 1; } + virtual double getImbalanceTolerance() const override { return 1.05; } + virtual void setDecompMethod(const std::string & input_method) override { method = input_method; } + virtual std::string getDecompMethod() const override { return method; } + virtual int getNumCriteria() const override { return m_critFields.size(); } + virtual bool isMultiCriteriaRebalance() const override { return true; } + virtual bool shouldFixMechanisms() const override { return false; } + + virtual double getGraphVertexWeight(stk::mesh::Entity entity, int criteria_index) const override + { + ThrowRequireWithSierraHelpMsg( + criteria_index >= 0 && static_cast(criteria_index) < m_critFields.size()); + const double * weight = stk::mesh::field_data(*m_critFields[criteria_index], entity); + if (weight != nullptr) + { + ThrowRequireWithSierraHelpMsg(*weight >= 0); + return *weight; + } + else + { + return m_defaultWeight; + } + } + +protected: + MultipleCriteriaSettings() = delete; + MultipleCriteriaSettings(const MultipleCriteriaSettings &) = delete; + MultipleCriteriaSettings & operator=(const MultipleCriteriaSettings &) = delete; + + const stk::mesh::BulkData & m_stkMeshBulkData; + const std::vector *> m_critFields; + const double m_defaultWeight; +}; + +class CDFEMRebalance final : public MultipleCriteriaSettings +{ +public: + CDFEMRebalance(stk::mesh::BulkData & bulk_data, + CDMesh * cdmesh, + const std::string & coordinates_field_name, + const std::vector *> & weights_fields, + const double imbalance_threshold, + const double default_weight = 0.) + : MultipleCriteriaSettings(bulk_data, weights_fields, default_weight), + my_cdmesh(cdmesh), + my_bulk_data(bulk_data), + my_coordinates_field_name(coordinates_field_name), + my_imbalance_threshold(imbalance_threshold) + {} + + ~CDFEMRebalance() = default; + + double getImbalanceTolerance() const override { return my_imbalance_threshold; } + std::string getCoordinateFieldName() const override { return my_coordinates_field_name; } + + void modifyDecomposition(stk::balance::DecompositionChangeList & decomp_changes) const override; + bool shouldPrintMetrics() const override { return true; } + bool isIncrementalRebalance() const override { return true; } + + virtual double getGraphVertexWeight(stk::mesh::Entity entity, int criteria_index) const override + { + double scaleVertexWeightForTestingDueToSmallMesh = 12; + return scaleVertexWeightForTestingDueToSmallMesh*MultipleCriteriaSettings::getGraphVertexWeight(entity, criteria_index); + } + +private: + CDMesh * my_cdmesh; + stk::mesh::BulkData & my_bulk_data; + std::string my_coordinates_field_name; + double my_imbalance_threshold; +}; + +void CDFEMRebalance::modifyDecomposition(stk::balance::DecompositionChangeList & decomp_changes) const +{ + /* Algorithm: + * 1) Go through change list and update destinations of all adaptivity children to match their + * root parent. Also move family trees with the parent+child entities. + * 2) Go through change list and remove any CDFEM child elements whose parents aren't being moved, + * as well as update the destination of all CDFEM children with parents that are being moved to + * the parent destination. + */ + + impl::update_rebalance_for_adaptivity(decomp_changes, my_bulk_data); + + if(my_cdmesh) + { + impl::update_rebalance_for_cdfem(decomp_changes, my_bulk_data, *my_cdmesh); + } +} + +} // anonymous namespace + +void +update_parent_child_rebalance_weights(const stk::mesh::BulkData & bulk_data, + stk::mesh::Field & element_weights_field, + const CDMesh * cdmesh) +{ + // First sum CDFEM child weights to their CDFEM parents, then adaptivity children to + // their parents. Adaptivity intermediate parents should not have their weights + // summed, they should be 0. + if(cdmesh) + { + impl::accumulate_cdfem_child_weights_to_parents(bulk_data, element_weights_field, *cdmesh); + } + + impl::accumulate_adaptivity_child_weights_to_parents(bulk_data, element_weights_field); +} + +bool rebalance_mesh(stk::mesh::BulkData & bulk_data, + CDMesh * cdmesh, + const std::string & element_weights_field_name, + const std::string & coordinates_field_name, + const std::vector & selections_to_rebalance_separately, + const std::string & decomp_method, + const double imbalance_threshold) +{ + const auto & meta = bulk_data.mesh_meta_data(); + auto weights_base = meta.get_field(stk::topology::ELEMENT_RANK, element_weights_field_name); + ThrowRequireMsg(weights_base, + "Failed to find element rank field " << element_weights_field_name + << " to use for rebalance weights."); + const auto element_weights_field = static_cast *>(weights_base); + + update_parent_child_rebalance_weights(bulk_data, *element_weights_field, cdmesh); + + ThrowAssert(impl::check_family_tree_element_and_side_ownership(bulk_data)); + + CDFEMRebalance balancer( + bulk_data, cdmesh, coordinates_field_name, {element_weights_field}, imbalance_threshold); + balancer.setDecompMethod(decomp_method); + const bool rebalanced = + stk::balance::balanceStkMesh(balancer, bulk_data, selections_to_rebalance_separately); + + ThrowAssert(impl::check_family_tree_element_and_side_ownership(bulk_data)); + + if(cdmesh) + { + cdmesh->rebuild_after_rebalance(); + } + + return rebalanced; +} + +bool rebalance_mesh(stk::mesh::BulkData & bulk_data, + CDMesh * cdmesh, + const std::vector & element_weights_field_names, + const std::string & coordinates_field_name, + const std::string & decomp_method, + const double imbalance_threshold) +{ + const auto & meta = bulk_data.mesh_meta_data(); + + std::vector *> weights_fields; + for (auto && field_name : element_weights_field_names) + { + auto weights_base = meta.get_field(stk::topology::ELEMENT_RANK, field_name); + ThrowRequireMsg(weights_base, + "Failed to find element rank field " << field_name << " to use for rebalance weights."); + const auto element_weights_field = static_cast *>(weights_base); + + update_parent_child_rebalance_weights(bulk_data, *element_weights_field, cdmesh); + weights_fields.push_back(element_weights_field); + } + + ThrowAssert(impl::check_family_tree_element_and_side_ownership(bulk_data)); + + CDFEMRebalance balancer( + bulk_data, cdmesh, coordinates_field_name, weights_fields, imbalance_threshold); + balancer.setDecompMethod(decomp_method); + const bool rebalanced = stk::balance::balanceStkMesh(balancer, bulk_data); + if (rebalanced) + stk::balance::balanceStkMeshNodes(balancer, bulk_data); + + ThrowAssert(impl::check_family_tree_element_and_side_ownership(bulk_data)); + + if (cdmesh) + { + cdmesh->rebuild_after_rebalance(); + } + + return rebalanced; +} +} +} + diff --git a/packages/krino/krino/rebalance_utils/Akri_RebalanceUtils.hpp b/packages/krino/krino/rebalance_utils/Akri_RebalanceUtils.hpp new file mode 100644 index 000000000000..dc77cc83328f --- /dev/null +++ b/packages/krino/krino/rebalance_utils/Akri_RebalanceUtils.hpp @@ -0,0 +1,46 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_REBALANCE_UTILS_INCLUDE_AKRI_REBALANCEUTILS_H_ +#define KRINO_REBALANCE_UTILS_INCLUDE_AKRI_REBALANCEUTILS_H_ + +#include +#include +#include + +namespace krino { class CDMesh; } + +namespace krino { +namespace rebalance_utils { + +// This function will call STK rebalance to rebalance the specified selections of the +// mesh based on the ELEMENT_RANK field element_weights_field. If cdmesh != nullptr the +// rebalance operation will ensure that all CDFEM child elements are moved to the same +// processor as their parent elements. The weights field will be adjusted +// such that the parent elements are weighted with the sum of their child +// weights, and the child weights are 0 for both CDFEM and adaptivity parents/children. +bool rebalance_mesh(stk::mesh::BulkData & bulk_data, + CDMesh * cdmesh, + const std::string & element_weights_field_name, + const std::string & coordinates_field_name, + const std::vector & selections_to_rebalance_separately, + const std::string & decomp_method = "parmetis", + const double imbalance_threshold = 1.05); + +// This version handles multiple criteria rebalancing with different weights for each +// criterion. +bool rebalance_mesh(stk::mesh::BulkData & bulk_data, + CDMesh * cdmesh, + const std::vector & element_weights_field_names, + const std::string & coordinates_field_name, + const std::string & decomp_method = "parmetis", + const double imbalance_threshold = 1.05); +} +} + +#endif /* KRINO_REBALANCE_UTILS_INCLUDE_AKRI_REBALANCEUTILS_H_ */ diff --git a/packages/krino/krino/rebalance_utils/Akri_RebalanceUtils_Impl.cpp b/packages/krino/krino/rebalance_utils/Akri_RebalanceUtils_Impl.cpp new file mode 100644 index 000000000000..2b6f450d3546 --- /dev/null +++ b/packages/krino/krino/rebalance_utils/Akri_RebalanceUtils_Impl.cpp @@ -0,0 +1,348 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino { +namespace rebalance_utils { +namespace impl { + +void set_family_tree_destinations(stk::balance::DecompositionChangeList & decomp_changes, const stk::mesh::BulkData & bulk_data) +{ + // At this point decomp_changes has all adaptivity parent + child elements that are moving + // and their dest procs match. + // First iterate the element family trees and set their destinations. + auto changes = decomp_changes.get_all_partition_changes(); + for (auto && change : changes) + { + const auto elem = change.first; + const auto dest_proc = change.second; + + const auto * fts = bulk_data.begin(elem, stk::topology::CONSTRAINT_RANK); + const int num_fts = bulk_data.num_connectivity(elem, stk::topology::CONSTRAINT_RANK); + for (int i = 0; i < num_fts; ++i) + { + if (bulk_data.bucket(elem).owned()) + { + ThrowRequire(!decomp_changes.has_entity(fts[i]) || + decomp_changes.get_entity_destination(fts[i]) == dest_proc); + decomp_changes.set_entity_destination(fts[i], dest_proc); + } + } + } + + std::vector child_sides; + const auto side_rank = bulk_data.mesh_meta_data().side_rank(); + changes = decomp_changes.get_all_partition_changes(); + for (auto && change : changes) + { + const auto elem = change.first; + const auto dest_proc = change.second; + + if (bulk_data.entity_rank(elem) != stk::topology::ELEMENT_RANK || + is_refinement_child(bulk_data, elem) || !has_refinement_children(bulk_data, elem)) + continue; + + const auto * root_sides = bulk_data.begin(elem, side_rank); + const int num_root_sides = bulk_data.num_connectivity(elem, side_rank); + for (int i = 0; i < num_root_sides; ++i) + { + if (!bulk_data.bucket(root_sides[i]).owned()) continue; + + get_refinement_all_children(bulk_data, root_sides[i], child_sides); + child_sides.push_back(root_sides[i]); + + for (auto && child_side : child_sides) + { + ThrowRequire(bulk_data.bucket(child_side).owned()); + const auto * fts = bulk_data.begin(child_side, stk::topology::CONSTRAINT_RANK); + const int num_fts = bulk_data.num_connectivity(child_side, stk::topology::CONSTRAINT_RANK); + for (int j = 0; j < num_fts; ++j) + { + const auto ft = fts[j]; + if (!decomp_changes.has_entity(ft) || + decomp_changes.get_entity_destination(ft) > dest_proc) + { + decomp_changes.set_entity_destination(ft, dest_proc); + } + } + } + } + } +} + +void +update_rebalance_for_adaptivity(stk::balance::DecompositionChangeList & decomp_changes, + const stk::mesh::BulkData & bulk_data) +{ + auto all_changes = decomp_changes.get_all_partition_changes(); + + // First pass remove all refinement children from the list of changes. + // Second pass will set their destinations all to the destination of their root parent + // if it is moving. + for(auto && change : all_changes) + { + stk::mesh::Entity entity = change.first; + if(is_refinement_child(bulk_data, entity)) + { + decomp_changes.delete_entity(entity); + } + } + + all_changes = decomp_changes.get_all_partition_changes(); + stk::mesh::EntityVector adapt_children; + for(auto && change : all_changes) + { + stk::mesh::Entity entity = change.first; + const auto dest = change.second; + + get_refinement_all_children(bulk_data, entity, adapt_children); + for(auto && child : adapt_children) + { + decomp_changes.set_entity_destination(child, dest); + } + } + + set_family_tree_destinations(decomp_changes, bulk_data); + + all_changes = decomp_changes.get_all_partition_changes(); + for(auto && change : all_changes) + { + stk::mesh::Entity entity = change.first; + const auto dest = change.second; + + if (bulk_data.entity_rank(entity) == stk::topology::CONSTRAINT_RANK) + { + auto side_rank = bulk_data.mesh_meta_data().side_rank(); + const auto * conn_sides = bulk_data.begin(entity, side_rank); + const int num_sides = bulk_data.num_connectivity(entity, side_rank); + for (int i = 0; i < num_sides; ++i) + { + ThrowRequire(!decomp_changes.has_entity(conn_sides[i])); + const auto * conn_fts = bulk_data.begin(conn_sides[i], stk::topology::CONSTRAINT_RANK); + const int num_fts = + bulk_data.num_connectivity(conn_sides[i], stk::topology::CONSTRAINT_RANK); + for (int j = 0; j < num_fts; ++j) + { + ThrowErrorMsgIf(decomp_changes.get_entity_destination(conn_fts[j]) != dest, + "Family Tree:\n" << debug_entity(bulk_data, conn_fts[j]) + << "\nHas destination proc = " + << decomp_changes.get_entity_destination(conn_fts[j]) + << ", which is different than " << dest + << " the destination for family tree:\n" + << debug_entity(bulk_data, entity) << "\n\n"); + } + } + } + } +} + +void +update_rebalance_for_cdfem(stk::balance::DecompositionChangeList & decomp_changes, + const stk::mesh::BulkData & bulk_data, + const CDMesh & cdmesh) +{ + auto all_changes = decomp_changes.get_all_partition_changes(); + std::vector subelements; + const auto & cdfem_parent_part = cdmesh.get_parent_part(); + const auto & cdfem_child_part = cdmesh.get_child_part(); + for(auto && change : all_changes) + { + stk::mesh::Entity entity = change.first; + const auto dest = change.second; + + if(bulk_data.bucket(entity).member(cdfem_parent_part)) + { + const auto * mesh_elem = cdmesh.find_mesh_element(bulk_data.identifier(entity)); + ThrowRequire(mesh_elem); + + subelements.clear(); + mesh_elem->get_subelements(subelements); + + for(auto && subelem : subelements) + { + auto subelem_entity = subelem->entity(); + ThrowRequire(bulk_data.is_valid(subelem_entity)); + + decomp_changes.set_entity_destination(subelem_entity, dest); + } + } + else if(bulk_data.bucket(entity).member(cdfem_child_part)) + { + const auto parent_elem = cdmesh.get_parent_element(entity); + if(!decomp_changes.has_entity(parent_elem)) decomp_changes.delete_entity(entity); + } + } +} + +void +accumulate_cdfem_child_weights_to_parents(const stk::mesh::BulkData & bulk_data, + stk::mesh::Field & element_weights_field, + const CDMesh & cdmesh) +{ + auto selector = + stk::mesh::selectField(element_weights_field) & cdmesh.get_child_part() & + bulk_data.mesh_meta_data().locally_owned_part(); + const auto & buckets = bulk_data.get_buckets(stk::topology::ELEMENT_RANK, selector); + for(auto && b_ptr : buckets) + { + double * weight_data = stk::mesh::field_data(element_weights_field, *b_ptr); + const int b_size = b_ptr->size(); + for(int i=0; i < b_size; ++i) + { + const auto child_elem = (*b_ptr)[i]; + const auto parent = cdmesh.get_parent_element(child_elem); + double * parent_weight_data = stk::mesh::field_data(element_weights_field, parent); + ThrowRequire(parent_weight_data); + *parent_weight_data += weight_data[i]; + weight_data[i] = 0.; + } + } +} + +void accumulate_adaptivity_child_weights_to_parents( + const stk::mesh::BulkData & bulk_data, stk::mesh::Field & element_weights_field) +{ + auto selector = stk::mesh::selectField(element_weights_field) & + bulk_data.mesh_meta_data().locally_owned_part(); + std::vector all_children; + const auto & buckets = bulk_data.get_buckets(stk::topology::ELEMENT_RANK, selector); + for (auto && b_ptr : buckets) + { + for (auto && elem : *b_ptr) + { + if (is_refinement_child(bulk_data, elem)) continue; + + all_children.clear(); + get_refinement_all_children(bulk_data, elem, all_children); + + double child_weights_sum = 0.; + for (auto && child : all_children) + { + double & child_weight = *stk::mesh::field_data(element_weights_field, child); + child_weights_sum += child_weight; + child_weight = 0.; + } + double & parent_weight = *stk::mesh::field_data(element_weights_field, elem); + parent_weight += child_weights_sum; + } + } +} + +namespace +{ +bool is_owned(const stk::mesh::BulkData & mesh, const stk::mesh::Entity entity) +{ + const bool owned_part = mesh.bucket(entity).member(mesh.mesh_meta_data().locally_owned_part()); + const bool owner_rank = mesh.parallel_rank() == mesh.parallel_owner_rank(entity); + ThrowErrorMsgIf(owned_part != owner_rank, + "Mismatch between parallel_owner_rank and locally_owned_part membership for:\n" + << debug_entity(mesh, entity) << "\n"); + return owner_rank; +} +} + +bool check_family_tree_element_and_side_ownership(const stk::mesh::BulkData & bulk_data) +{ + const auto & meta = bulk_data.mesh_meta_data(); + bool passed = true; + + const auto & locally_owned_part = meta.locally_owned_part(); + const auto & elem_buckets = bulk_data.get_buckets(stk::topology::ELEMENT_RANK, locally_owned_part); + for(auto && b_ptr : elem_buckets) + { + for(auto && elem : *b_ptr) + { + const auto * fts = bulk_data.begin(elem, stk::topology::CONSTRAINT_RANK); + const int num_fts = bulk_data.num_connectivity(elem, stk::topology::CONSTRAINT_RANK); + for(int i=0; i < num_fts; ++i) + { + if(!is_owned(bulk_data, fts[i])) + { + krinolog << "Found non-owned family tree connected to owned element.\n"; + krinolog << "Family tree:\n" << debug_entity(bulk_data, fts[i]) << "\n"; + passed = false; + } + } + } + } + const auto side_rank = meta.side_rank(); + const auto & side_buckets = bulk_data.get_buckets(side_rank, locally_owned_part); + for(auto && b_ptr : side_buckets) + { + for(auto && side : *b_ptr) + { + const auto * fts = bulk_data.begin(side, stk::topology::CONSTRAINT_RANK); + const int num_fts = bulk_data.num_connectivity(side, stk::topology::CONSTRAINT_RANK); + for(int i=0; i < num_fts; ++i) + { + if(!is_owned(bulk_data, fts[i])) + { + krinolog << "Found non-owned family tree connected to owned side.\n"; + krinolog << "Family tree:\n" << debug_entity(bulk_data, fts[i]) << "\n"; + passed = false; + } + } + } + } + + const auto & ft_buckets = bulk_data.get_buckets(stk::topology::CONSTRAINT_RANK, locally_owned_part); + for(auto && b_ptr : ft_buckets) + { + for(auto && ft : *b_ptr) + { + const auto * elems = bulk_data.begin(ft, stk::topology::ELEMENT_RANK); + const int num_elems = bulk_data.num_connectivity(ft, stk::topology::ELEMENT_RANK); + for(int i=0; i < num_elems; ++i) + { + if(!is_owned(bulk_data, elems[i])) + { + krinolog << "Found non-owned element connected to owned family tree.\n"; + krinolog << "Element ID:\n" << bulk_data.identifier(elems[i]) << "\n"; + krinolog << "Family tree:\n" << debug_entity(bulk_data, ft) << "\n"; + passed = false; + } + } + const auto * sides = bulk_data.begin(ft, side_rank); + const int num_sides = bulk_data.num_connectivity(ft, side_rank); + for(int i=0; i < num_sides; ++i) + { + if(!is_owned(bulk_data, sides[i])) + { + krinolog << "Found non-owned side connected to owned family tree.\n"; + krinolog << "Element ID:\n" << bulk_data.identifier(sides[i]) << "\n"; + krinolog << "Family tree:\n" << debug_entity(bulk_data, ft) << "\n"; + passed = false; + } + } + } + } + + return passed; +} + +} +} +} diff --git a/packages/krino/krino/rebalance_utils/Akri_RebalanceUtils_Impl.hpp b/packages/krino/krino/rebalance_utils/Akri_RebalanceUtils_Impl.hpp new file mode 100644 index 000000000000..85865baa15a4 --- /dev/null +++ b/packages/krino/krino/rebalance_utils/Akri_RebalanceUtils_Impl.hpp @@ -0,0 +1,44 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_REBALANCE_UTILS_INCLUDE_AKRI_REBALANCEUTILS_IMPL_H_ +#define KRINO_REBALANCE_UTILS_INCLUDE_AKRI_REBALANCEUTILS_IMPL_H_ + +#include + +namespace stk { namespace balance { class DecompositionChangeList; } } +namespace stk { namespace mesh { class BulkData; } } +namespace krino { class CDMesh; } + +namespace krino { +namespace rebalance_utils { +namespace impl { + +void +update_rebalance_for_adaptivity(stk::balance::DecompositionChangeList & decomp, + const stk::mesh::BulkData & bulk_data); + +void +update_rebalance_for_cdfem(stk::balance::DecompositionChangeList & decomp, + const stk::mesh::BulkData & bulk_data, + const CDMesh & cdmesh); + +void +accumulate_cdfem_child_weights_to_parents(const stk::mesh::BulkData & bulk_data, + stk::mesh::Field & element_weights_field, + const CDMesh & cdmesh); + +void accumulate_adaptivity_child_weights_to_parents( + const stk::mesh::BulkData & bulk_data, stk::mesh::Field & element_weights_field); + +bool check_family_tree_element_and_side_ownership(const stk::mesh::BulkData & bulk_data); +} +} +} + +#endif /* KRINO_REBALANCE_UTILS_INCLUDE_AKRI_REBALANCEUTILS_IMPL_H_ */ diff --git a/packages/krino/krino/rebalance_utils/CMakeLists.txt b/packages/krino/krino/rebalance_utils/CMakeLists.txt new file mode 100644 index 000000000000..b2820a96ae3b --- /dev/null +++ b/packages/krino/krino/rebalance_utils/CMakeLists.txt @@ -0,0 +1,19 @@ +SET(HEADERS "") +SET(SOURCES "") + +INCLUDE_DIRECTORIES(${${PACKAGE_NAME}_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +FILE(GLOB HEADERS *.hpp) +FILE(GLOB SOURCES *.cpp) + +TRIBITS_ADD_LIBRARY( + krino_rebalance_utils_lib + HEADERS ${HEADERS} + SOURCES ${SOURCES} + DEPLIBS krino_lib) + +INSTALL(FILES ${HEADERS} DESTINATION + ${CMAKE_INSTALL_PREFIX}/${${PROJECT_NAME}_INSTALL_INCLUDE_DIR}/krino_rebalance_utils_lib) + diff --git a/packages/krino/krino/region/Akri_Region.cpp b/packages/krino/krino/region/Akri_Region.cpp new file mode 100644 index 000000000000..ebf7ac13eade --- /dev/null +++ b/packages/krino/krino/region/Akri_Region.cpp @@ -0,0 +1,664 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino{ + +//-------------------------------------------------------------------------------- + +Region::Region(Simulation & owning_simulation, const std::string & regionName) +: my_simulation(owning_simulation), + my_meta(nullptr), + my_bulk(nullptr), + my_name(regionName), + my_timerRegion(std::string("Region ") + regionName, sierra::Diag::TIMER_REGION, my_simulation.get_timer()), + my_timerInitialize("Initialize", my_timerRegion), + my_timerExecute("Execute", my_timerRegion), + my_timerMeshInput("Mesh input", my_timerRegion), + my_timerMeshOutput("Mesh output", my_timerRegion), + my_output_file_index(0), + my_output_file_created(false), + my_initial_refinement_levels(0) +{ /* %TRACE[ON]% */ Trace trace__("krino::Region::Region()"); /* %TRACE% */ + my_simulation.add_region(this); + myIOBroker = std::make_unique(stk::EnvData::parallel_comm()); + + std::vector entity_rank_names = stk::mesh::entity_rank_names(); + entity_rank_names.push_back("FAMILY_TREE"); + stk_IO().set_rank_name_vector(entity_rank_names); + + my_results_options = std::make_unique(); +} +//-------------------------------------------------------------------------------- +Region::~Region() +{ +} + +static bool locally_has_64bit_ids_in_use_for_nodes_or_elements(const stk::mesh::BulkData & mesh) +{ + const uint64_t max32bitId = ~0U; + for (auto entityRank : {stk::topology::NODE_RANK, stk::topology::ELEMENT_RANK}) + for (auto && bucketPtr : mesh.buckets(entityRank)) + for (auto && entity : *bucketPtr) + if (mesh.identifier(entity) > max32bitId) + return true; + + return false; +} + +static bool cdfem_mesh_displacements_requested_in_results_fields(const std::string & cdfemMeshDisplacementsFieldName, const std::set & resultsFields) +{ + for (auto && resultsField : resultsFields) + if (stk::equal_case(resultsField.first, cdfemMeshDisplacementsFieldName)) + return true; + return false; +} + +//-------------------------------------------------------------------------------- +void Region::commit() +{ /* %TRACE[ON]% */ Trace trace__("krino::Region::commit()"); /* %TRACE% */ + + auto & meta = get_stk_mesh_meta_data(); + LevelSet::setup(meta); + krino::CDFEM_Support & cdfem_support = krino::CDFEM_Support::get(meta); + + if (krino::CDFEM_Support::is_active(meta)) + { + if (cdfem_mesh_displacements_requested_in_results_fields(CDFEM_Support::cdfem_mesh_displacements_field_name(), my_results_options->get_nodal_fields())) + cdfem_support.register_cdfem_mesh_displacements_field(); + + if (cdfem_support.get_num_initial_decomposition_cycles() > 1) + cdfem_support.register_parent_node_ids_field(); // Needed to use piecewise linear location on previously cut edges + + cdfem_support.setup_fields(); + } + + auto & active_part = AuxMetaData::get(meta).active_part(); + stk::mesh::BulkData::AutomaticAuraOption auto_aura_option = stk::mesh::BulkData::NO_AUTO_AURA; + + if (my_initial_refinement_levels > 0 || (krino::CDFEM_Support::is_active(meta) && cdfem_support.get_interface_maximum_refinement_level() > 0) || + (krino::CDFEM_Support::is_active(meta) && cdfem_support.get_post_cdfem_refinement_levels() > 0)) + { + auto_aura_option = stk::mesh::BulkData::AUTO_AURA; + HAdapt::setup(meta, active_part, my_timerExecute); + } + + if (cdfem_support.get_cdfem_edge_degeneracy_handling() == SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE) + { + auto_aura_option = stk::mesh::BulkData::AUTO_AURA; + } + + if (krino::CDFEM_Support::is_active(meta)) + { + cdfem_support.add_interpolation_field(cdfem_support.get_coords_field()); + const LevelSetManager & region_ls = LevelSetManager::get(meta); + for(auto&& ls : region_ls) + { + cdfem_support.add_interpolation_field(ls->get_distance_field()); + } + cdfem_support.finalize_fields(); + + if(cdfem_support.get_interface_maximum_refinement_level() > 0 || + cdfem_support.get_post_cdfem_refinement_levels() > 0) + { + cdfem_support.set_nonconformal_hadapt( + [&meta](const std::string & marker_field, int debug_level) + { + HAdapt::do_adaptive_refinement(meta, marker_field); + }); + } + } + + if (nullptr != my_generated_mesh.get()) + { + set_generated_mesh_domain(); + my_generated_mesh->populate_mesh(stk::EnvData::parallel_comm(), auto_aura_option); + my_bulk = &my_generated_mesh->bulk_data(); + stk_IO().set_bulk_data( Teuchos::rcpFromRef( *my_bulk ) ); + if (my_generated_mesh->has_flat_boundaries()) + my_generated_mesh->create_domain_sides(); + } + else + { + my_bulk = new stk::mesh::BulkData(meta,stk::EnvData::parallel_comm(),auto_aura_option); + stk_IO().set_bulk_data( Teuchos::rcp( my_bulk ) ); + stk_IO().populate_bulk_data(); + } + + if (AuxMetaData::get(get_stk_mesh_meta_data()).get_assert_32bit_flag()) + { + const bool has_64bit_ids_in_use = stk::is_true_on_any_proc(my_bulk->parallel(), locally_has_64bit_ids_in_use_for_nodes_or_elements(*my_bulk)); + ThrowErrorMsgIf(has_64bit_ids_in_use, "Option use_32_bit ids is active, but input file uses 64 bit ids."); + } + else + { + my_bulk->set_large_ids_flag(true); + } + + if (my_bulk->is_automatic_aura_on() || my_bulk->parallel_size() == 1) + { + // Used for element side connectivty checks + stk::mesh::create_exposed_block_boundary_sides(*my_bulk, meta.universal_part(), {&AuxMetaData::get(meta).exposed_boundary_part()}); + } + + + + activate_all_entities(*my_bulk, active_part); + + LevelSet::post_commit_setup(meta); +} + +namespace { + +void zero_error_indicator(const LevelSetManager & region_ls, stk::mesh::BulkData & mesh) +{ + auto & meta = mesh.mesh_meta_data(); + const auto & cdfem_support = CDFEM_Support::get(meta); + const auto & aux_meta = AuxMetaData::get(meta); + auto indicator_field = + aux_meta.get_field(stk::topology::ELEMENT_RANK, + cdfem_support.get_nonconformal_adapt_indicator_name()); + + const auto & buckets = mesh.get_buckets(stk::topology::ELEMENT_RANK, + meta.locally_owned_part()); + for(auto && b_ptr : buckets) + { + auto * data = field_data(indicator_field, *b_ptr); + auto length = b_ptr->size(); + std::fill(data, data + length, 0.); + } +} + +void mark_selected_elements(const std::string & marker_field_name, const int current_refinement_level, const AuxMetaData & auxMeta, const int max_refinement_levels, const stk::mesh::Selector & selector) +{ + FieldRef marker_field = auxMeta.get_field(stk::topology::ELEMENT_RANK, marker_field_name); + const bool do_adapt = current_refinement_level < max_refinement_levels; + if (do_adapt) + { + stk::mesh::field_fill(1, marker_field, selector); + } +} + +void do_adaptive_refinement(const krino::CDFEM_Support & cdfemSupport, const LevelSetManager & region_ls, stk::mesh::BulkData & mesh) +{ + const auto target_count = cdfemSupport.get_nonconformal_adapt_target_count(); + if(target_count > 0) zero_error_indicator(region_ls, mesh); + { + stk::diag::TimeBlock adapt_timer__(cdfemSupport.get_timer_adapt()); + + const std::string & marker_name = "refine_field"; //cdfemSupport.get_nonconformal_adapt_marker_name(); + auto & h_adapt = cdfemSupport.get_nonconformal_hadapt(); + + const int num_refinement_levels = cdfemSupport.get_interface_maximum_refinement_level(); + const int num_refinement_steps = (target_count > 0) ? num_refinement_levels : 2*num_refinement_levels; + + const auto & indicator_field_name = cdfemSupport.get_nonconformal_adapt_indicator_name(); + + const bool do_post_adapt_refinement = cdfemSupport.get_post_adapt_refinement_levels() > 0; + std::function marker_function = + [&mesh, ®ion_ls, num_refinement_steps, do_post_adapt_refinement, target_count, indicator_field_name] + (const std::string & marker_field_name, int num_refinements) + { + const bool do_adapt = num_refinements < num_refinement_steps; + const bool more_to_do = do_adapt || do_post_adapt_refinement; + if(target_count > 0) zero_error_indicator(region_ls, mesh); + LevelSet::initialize(mesh.mesh_meta_data(), more_to_do); + if(!do_adapt) return; + + if(target_count == 0) + { + const LevelSetInterfaceGeometry interfaceGeometry(mesh.mesh_meta_data()); + CDMesh::mark_interface_elements_for_adaptivity(mesh, interfaceGeometry, marker_field_name, num_refinements); + } + else + { + HAdapt::mark_based_on_indicator_field(mesh, + marker_field_name, + indicator_field_name, + num_refinement_steps, + num_refinements, + target_count); + } + }; + + perform_multilevel_adaptivity(mesh, marker_name, marker_function, h_adapt, cdfem_do_not_refine_or_unrefine_selector(cdfemSupport)); + } +} + +void do_post_adapt_uniform_refinement(const Simulation & simulation, const krino::CDFEM_Support & cdfemSupport, const AuxMetaData & auxMeta, stk::mesh::BulkData & mesh) +{ + if ( cdfemSupport.get_post_adapt_refinement_levels() > 0 ) + { + if ( simulation.is_transient() ) + { + krinolog << "Can not do post-adaptivity uniform mesh refinement for transient problems..."; + } + else + { + krinolog << "Performing " << cdfemSupport.get_post_adapt_refinement_levels() << " levels of post-adaptivity uniform mesh refinement..." << std::endl; + + // Doing adaptive refinement with a uniform marker is better than doing uniform refinement here because of how + // the transition elements are handled. + const std::string & marker_name = "refine_field"; + auto & h_adapt = cdfemSupport.get_nonconformal_hadapt(); + const int num_levels = cdfemSupport.get_post_adapt_refinement_levels(); + FieldRef marker_field = auxMeta.get_field(stk::topology::ELEMENT_RANK, marker_name); + + std::function marker_function = + [&mesh, marker_field, num_levels] + (const std::string & marker_field_name, int num_refinements) + { + const bool do_adapt = num_refinements < num_levels; + LevelSet::initialize(mesh.mesh_meta_data(), do_adapt); + if (do_adapt) + { + stk::mesh::field_fill(1, marker_field); + } + }; + + perform_multilevel_adaptivity(mesh, marker_name, marker_function, h_adapt, cdfem_do_not_refine_or_unrefine_selector(cdfemSupport)); + } + } +} + +void do_post_cdfem_uniform_refinement(const Simulation & simulation, const krino::CDFEM_Support & cdfemSupport, const AuxMetaData & auxMeta, stk::mesh::BulkData & mesh) +{ + if ( cdfemSupport.get_post_cdfem_refinement_levels() > 0 ) + { + const int num_levels = cdfemSupport.get_post_cdfem_refinement_levels(); + if ( simulation.is_transient() ) + { + krinolog << "Can not do post-cdfem mesh refinement for transient problems..."; + } + else + { + krinolog << "Performing " << num_levels << " levels of post-cdfem mesh refinement..." << std::endl; + + // Doing adaptive refinement with a uniform marker is better than doing uniform refinement here because of how + // the transition elements are handled. + const std::string & marker_name = "refine_field"; + auto & h_adapt = cdfemSupport.get_nonconformal_hadapt(); + const stk::mesh::Selector & refinement_selector = cdfemSupport.get_post_cdfem_refinement_selector(); + std::function marker_function = + [&auxMeta, num_levels, &refinement_selector] + (const std::string & marker_field_name, int num_refinements) + { + mark_selected_elements(marker_field_name, num_refinements, auxMeta, num_levels, refinement_selector); + }; + + CDMesh::get_new_mesh()->delete_cdfem_parent_elements(); // Extreme work-around for the way percept messes up the active part on cdfem parents. + perform_multilevel_adaptivity(mesh, marker_name, marker_function, h_adapt); + } + } +} + +} + +//-------------------------------------------------------------------------------- +void Region::initialize() +{ /* %TRACE[ON]% */ Trace trace__("krino::Region::initialize()"); /* %TRACE% */ + stk::diag::TimeBlock timer__(my_timerInitialize); + + const bool cdfem_is_active = krino::CDFEM_Support::is_active(get_stk_mesh_meta_data()); + krino::CDFEM_Support & cdfem_support = krino::CDFEM_Support::get(get_stk_mesh_meta_data()); + const bool adapt_is_active = cdfem_support.get_interface_maximum_refinement_level() > 0 || + cdfem_support.get_post_cdfem_refinement_levels() > 0; + + const LevelSetManager & region_ls = LevelSetManager::get(get_stk_mesh_meta_data()); + + if (my_initial_refinement_levels > 0) + { + cdfem_support.set_global_ids_are_NOT_parallel_consistent(); + HAdapt::do_uniform_refinement(get_stk_mesh_meta_data(), my_initial_refinement_levels); + } + + if (cdfem_is_active) + { + auto & bulk = get_stk_mesh_bulk_data(); + if(adapt_is_active) + { + do_adaptive_refinement(cdfem_support, region_ls, bulk); + + const auto & auxMeta = AuxMetaData::get(get_stk_mesh_meta_data()); + do_post_adapt_uniform_refinement(my_simulation, cdfem_support, auxMeta, bulk); + + const LevelSetInterfaceGeometry interfaceGeometry(get_stk_mesh_meta_data()); + krino::CDMesh::decompose_mesh(bulk, interfaceGeometry, my_simulation.get_time_step_count(), {}); + + do_post_cdfem_uniform_refinement(my_simulation, cdfem_support, auxMeta, bulk); + } + else + { + if ( cdfem_support.get_post_adapt_refinement_levels() > 0 ) + { + krinolog << "Post-adaptivity uniform refinement levels selected without any adaptivity levels. Not performing additional uniform refinement." << std::endl; + } + + const int num_init_decomp_cycles = cdfem_support.get_num_initial_decomposition_cycles(); + for (int icycle=0; icycleinitialize(0.); + + if (my_simulation.is_transient()) + { + // initialize does not end with the facets constructed so manually construct them now + ls->build_facets_locally(get_stk_mesh_meta_data().universal_part()); + + // debugging + if (krinolog.shouldPrint(LOG_FACETS)) + { + ls->facets_exoii(); + } + } + } + } + + // Skip output of empty parts + for (auto && part : get_stk_mesh_meta_data().get_parts()) + { + if (stk::io::is_part_io_part(*part)) + { + uint64_t local_num_entities = stk::mesh::count_selected_entities( + *part, get_stk_mesh_bulk_data().buckets(part->primary_entity_rank())); + uint64_t global_num_entities = 0; + stk::all_reduce_sum( + get_stk_mesh_bulk_data().parallel(), &local_num_entities, &global_num_entities, 1); + if(global_num_entities == 0) + { + stk::io::remove_io_part_attribute(*part); + } + } + } +} +//-------------------------------------------------------------------------------- + +void Region::execute() +{ /* %TRACE[ON]% */ Trace trace__("krino::Region::execute()"); /* %TRACE% */ + stk::diag::TimeBlock timer__(my_timerExecute); + + // This is a hack for now to exit immediately when CDFEM is active + if (krino::CDFEM_Support::is_active(get_stk_mesh_meta_data())) + { + return; + } + + double deltaTime = time_step(); + + const LevelSetManager & region_ls = LevelSetManager::get(get_stk_mesh_meta_data()); + for(auto&& ls : region_ls) + { + ls->advance_semilagrangian(deltaTime); + } +} + +unsigned Region::spatial_dimension() const { return my_meta->spatial_dimension(); } +const stk::mesh::BulkData& Region::get_stk_mesh_bulk_data() const { ThrowRequire(my_bulk); return *my_bulk; } +stk::mesh::BulkData& Region::get_stk_mesh_bulk_data() { ThrowRequire(my_bulk); return *my_bulk; } +const stk::mesh::MetaData& Region::get_stk_mesh_meta_data() const { ThrowRequire(my_meta); return *my_meta; } +stk::mesh::MetaData& Region::get_stk_mesh_meta_data() { ThrowRequire(my_meta); return *my_meta; } +double Region::time_step() const { return my_simulation.get_time_step(); } + +stk::io::StkMeshIoBroker & Region::stk_IO() +{ + ThrowRequire(myIOBroker); + return *myIOBroker; +} + +Ioss::Region * Region::get_input_io_region() +{ + return stk_IO().get_input_io_region().get(); +} + +std::string Region::name_of_input_mesh() const +{ + return my_input_model_name; +} + +void Region::create_output_mesh() +{ + bool output_mesh = my_results_options->get_num_step_increments() != 0; + + if(!output_mesh) return; + + my_results_options->get_scheduler().set_termination_time(my_simulation.get_stop_time()); + + my_output_file_index = stk_IO().create_output_mesh(my_results_options->get_filename(), stk::io::WRITE_RESULTS, my_results_options->get_properties()); + + Teuchos::RCP active_selector = Teuchos::rcp(new stk::mesh::Selector(AuxMetaData::get(get_stk_mesh_meta_data()).active_part())); + stk_IO().set_subset_selector(my_output_file_index, active_selector); + + stk_IO().write_output_mesh(my_output_file_index); + + declare_output_variables(my_output_file_index); + + my_output_file_created = true; +} + +void +Region::declare_output_variables(size_t result_output_index) +{ + for (auto && outField : my_results_options->get_nodal_fields()) + { + const std::string & varName = outField.first; + const std::string & newName = outField.second; + stk::mesh::FieldBase *theField = get_stk_mesh_meta_data().get_field(stk::topology::NODE_RANK, varName); + if ( nullptr == theField ) + { + krinolog << "Sorry, no nodal field by the name " << varName << std::endl; + krinolog << "Available fields: " << std::endl; + for (auto && field : get_stk_mesh_meta_data().get_fields(stk::topology::NODE_RANK)) + { + krinolog << " " << field->name() << std::endl; + } + } + else + { + stk_IO().add_field(result_output_index, *theField, newName); + } + } + + for (auto && outField : my_results_options->get_element_fields()) + { + const std::string & varName = outField.first; + const std::string & newName = outField.second; + stk::mesh::FieldBase *theField = get_stk_mesh_meta_data().get_field(stk::topology::ELEMENT_RANK, varName); + if ( nullptr == theField ) + { + krinolog << "Sorry, no element field by the name " << varName << std::endl; + krinolog << "Available fields: " << std::endl; + for (auto && field : get_stk_mesh_meta_data().get_fields(stk::topology::ELEMENT_RANK)) + { + krinolog << " " << field->name() << std::endl; + } + } + else + { + stk_IO().add_field(result_output_index, *theField, newName); + } + } +} + +void Region::process_output(bool forceOutput) +{ + stk::diag::TimeBlock mesh_output_timeblock(my_timerMeshOutput); + + if(my_results_options->get_num_step_increments() == 0) return; + + stk::util::Scheduler & scheduler = my_results_options->get_scheduler(); + + if(forceOutput) scheduler.set_force_schedule(); + + const int stepCounter = my_simulation.get_time_step_count(); + const double currentTime = my_simulation.get_current_time(); + + const bool doOutput = scheduler.is_it_time(currentTime, stepCounter); + + if(!doOutput) return; + + // A couple of krino tests that just compute surface distances never call Region::initialize() + // where we normally create the output mesh. + if(!my_output_file_created) create_output_mesh(); + + stk_IO().begin_output_step(my_output_file_index, currentTime); + stk_IO().write_defined_output_fields(my_output_file_index); + stk_IO().end_output_step(my_output_file_index); +} + +void +Region::associate_input_mesh(const std::string & model_name, bool assert_32bit_ids, bool force_64bit_ids) +{ + stk::diag::TimeBlock mesh_input_timeblock(my_timerMeshInput); + + MeshInputOptions * db_options = MeshInputOptions::get(model_name); + ThrowRequireMsg(db_options != nullptr, "No finite element model found with name " << model_name); + my_input_model_name = model_name; + + ThrowRequire(db_options->is_valid()); + if (db_options->use_generated_mesh()) + { + stk::topology generated_mesh_element_type = db_options->get_generated_mesh_element_type(); + std::vector entity_rank_names = stk::mesh::entity_rank_names(); + entity_rank_names.push_back("FAMILY_TREE"); + my_generated_mesh = std::make_unique(generated_mesh_element_type,entity_rank_names); + my_meta = &my_generated_mesh->meta_data(); + stk::mesh::Field & coords_field = my_meta->declare_field>(stk::topology::NODE_RANK, "coordinates", 1); + stk::mesh::put_field_on_mesh(coords_field, my_meta->universal_part(), generated_mesh_element_type.dimension(), nullptr); + } + else + { + const std::string directory = sierra::Env::working_directory().c_str(); + const std::string filename(Ioss::Utils::local_filename(db_options->get_filename(), db_options->get_filetype(), directory)); + + stk::io::StkMeshIoBroker & stk_io = stk_IO(); + + stk_io.property_add(Ioss::Property("MAXIMUM_NAME_LENGTH", 180)); + + if (!db_options->get_decomposition_method().empty()) + { + stk_io.property_add(Ioss::Property("DECOMPOSITION_METHOD", Ioss::Utils::uppercase(db_options->get_decomposition_method()))); + } + + stk_io.add_mesh_database(db_options->get_filename(), stk::io::READ_MESH); + stk_io.create_input_mesh(); + my_meta = &stk_io.meta_data(); + AuxMetaData::create(*my_meta); + auto & active_part = AuxMetaData::get(*my_meta).active_part(); + stk::mesh::Selector activeSelector = active_part; + stk_io.set_active_selector(activeSelector); + } + + if (assert_32bit_ids) + { + AuxMetaData::get(*my_meta).set_assert_32bit_flag(); + } + if (!force_64bit_ids) + { + AuxMetaData::get(*my_meta).clear_force_64bit_flag(); + } +} + +void +Region::set_generated_mesh_domain() +{ + MeshInputOptions * db_options = MeshInputOptions::get(my_input_model_name); + ThrowRequire(db_options != nullptr); + my_generated_mesh->set_mesh_structure_type(db_options->get_generated_mesh_structure_type()); + const double mesh_size = db_options->get_generated_mesh_size(); + if (db_options->get_generated_mesh_domain_type() == MeshInputOptions::GENERATED_MESH_FOR_SPECIFIED_DOMAIN) + { + typename BoundingBoxMesh::BoundingBoxType bbox; + const std::vector & domain = db_options->get_generated_mesh_domain(); + if (db_options->get_generated_mesh_spatial_dimension() == 2) + { + ThrowRequire(domain.size() == 4); + const Vector3d min(domain[0], domain[1], 0.); + const Vector3d max(domain[2], domain[3], 0.); + ThrowRequireMsg(max[0]>min[0] && max[1]>min[1], "Invalid domain specified."); + bbox = BoundingBoxMesh::BoundingBoxType(min, max); + } + else + { + ThrowRequire(domain.size() == 6); + const Vector3d min(domain[0], domain[1], domain[2]); + const Vector3d max(domain[3], domain[4], domain[5]); + ThrowRequireMsg(max[0]>min[0] && max[1]>min[1] && max[2]>min[2], "Invalid domain specified."); + bbox = BoundingBoxMesh::BoundingBoxType(min, max); + } + my_generated_mesh->set_domain(bbox, mesh_size); + } + else + { + typename BoundingBoxMesh::BoundingBoxType domain_bbox; + const LevelSetManager & region_ls = LevelSetManager::get(get_stk_mesh_meta_data()); + for(auto&& ls : region_ls) + { + if (ls->has_IC_surfaces()) + { + BoundingBox ls_IC_surf_bbox = ls->get_IC_surface_bounding_box(); + domain_bbox.accommodate(ls_IC_surf_bbox); + } + } + + if (db_options->get_generated_mesh_spatial_dimension() == 2) + { + Vector3d min = domain_bbox.get_min(); + Vector3d max = domain_bbox.get_max(); + min[2] = 0.; + max[2] = 0.; + domain_bbox = typename BoundingBoxMesh::BoundingBoxType(min, max); + } + my_generated_mesh->set_domain(domain_bbox, mesh_size, 1); + } +} + +} // namespace krino + diff --git a/packages/krino/krino/region/Akri_Region.hpp b/packages/krino/krino/region/Akri_Region.hpp new file mode 100644 index 000000000000..7b175775ae6b --- /dev/null +++ b/packages/krino/krino/region/Akri_Region.hpp @@ -0,0 +1,81 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Region_h +#define Akri_Region_h + +#include +#include +#include + +namespace stk { namespace mesh { class MetaData; } } +namespace stk { namespace mesh { class BulkData; } } +namespace stk { namespace mesh { class Part; } } +namespace stk { namespace io { class StkMeshIoBroker; } } +namespace krino { class BoundingBoxMesh; } +namespace krino { class RegionForwarder; } +namespace krino { class ResultsOutputOptions; } +namespace krino { class Simulation; } +namespace Ioss { class Region; } + +namespace krino{ + +class Region { +public: + Region(Simulation & owning_simulation, const std::string & regionName); + virtual ~Region(); + + virtual void commit(); + virtual void initialize(); + virtual void execute(); + + double time_step() const; + const std::string & name() const { return my_name; } + unsigned spatial_dimension() const; + const stk::mesh::BulkData& get_stk_mesh_bulk_data() const; + stk::mesh::BulkData& get_stk_mesh_bulk_data(); + const stk::mesh::MetaData& get_stk_mesh_meta_data() const; + stk::mesh::MetaData& get_stk_mesh_meta_data(); + stk::diag::Timer & getRegionTimer() const { return my_timerRegion; } + stk::diag::Timer & getMeshInputTimer() const { return my_timerMeshInput; } + stk::diag::Timer & getMeshOutputTimer() const { return my_timerMeshOutput; } + + stk::io::StkMeshIoBroker & stk_IO(); + std::string name_of_input_mesh() const; + Ioss::Region * get_input_io_region(); + void associate_input_mesh(const std::string & model_name, bool assert_32bit_ids, bool force_64bit_ids); + void set_generated_mesh_domain(); + void create_output_mesh(); + void declare_output_variables(size_t result_output_index); + void process_output(bool forceOutput); + ResultsOutputOptions * get_results_options() { return my_results_options.get(); } + void set_initial_refinement_levels(int levels) { my_initial_refinement_levels = levels; } + +private: + Simulation & my_simulation; + stk::mesh::MetaData * my_meta; + stk::mesh::BulkData * my_bulk; + std::unique_ptr myIOBroker; + std::unique_ptr my_generated_mesh; + std::unique_ptr my_results_options; + std::string my_name; + std::string my_input_model_name; + + mutable stk::diag::Timer my_timerRegion; ///< Region's root timer + mutable stk::diag::Timer my_timerInitialize; ///< Initialize timer + mutable stk::diag::Timer my_timerExecute; ///< Execute timer + mutable stk::diag::Timer my_timerMeshInput; ///< Mesh input timer + mutable stk::diag::Timer my_timerMeshOutput; ///< Mesh output timer + size_t my_output_file_index; + bool my_output_file_created; + int my_initial_refinement_levels; +}; + +} // namespace krino + +#endif // Akri_Region_h diff --git a/packages/krino/krino/region/Akri_RegisterProduct.cpp b/packages/krino/krino/region/Akri_RegisterProduct.cpp new file mode 100644 index 000000000000..891ad313cb77 --- /dev/null +++ b/packages/krino/krino/region/Akri_RegisterProduct.cpp @@ -0,0 +1,30 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include + +namespace krino { + +const char * +get_product_name() +{ /* %TRACE% */ /* %TRACE% */ + return "Krino"; +} + + +void +register_product() +{ /* %TRACE% */ /* %TRACE% */ + // Register krino + stk::ProductRegistry::instance().addRegion(get_product_name()); +} + +} // namespace krino + diff --git a/packages/krino/krino/region/Akri_RegisterProduct.hpp b/packages/krino/krino/region/Akri_RegisterProduct.hpp new file mode 100644 index 000000000000..ab4d53824791 --- /dev/null +++ b/packages/krino/krino/region/Akri_RegisterProduct.hpp @@ -0,0 +1,20 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_RegisterProduct_h +#define Akri_RegisterProduct_h + +namespace krino { + +const char *get_product_name(); + +void register_product(); + +} // namespace krino + +#endif // Akri_RegisterProduct_h diff --git a/packages/krino/krino/region/Akri_ResultsOutputOptions.hpp b/packages/krino/krino/region/Akri_ResultsOutputOptions.hpp new file mode 100644 index 000000000000..2a0b1796bd2c --- /dev/null +++ b/packages/krino/krino/region/Akri_ResultsOutputOptions.hpp @@ -0,0 +1,88 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_ResultsOutputOptions_h +#define Akri_ResultsOutputOptions_h + +#include +#include +#include +#include +#include // for String +#include "stk_topology/topology.hpp" // for topology, etc +#include + +namespace stk { namespace mesh { class Part; } } +namespace stk { namespace mesh { class MetaData; } } + +namespace krino { + + typedef std::pair StepIncrement; + typedef std::pair FieldName_OutputName_Pair; + + class ResultsOutputOptions { + + public: + + ResultsOutputOptions() : + my_scheduler(), + my_numStepIncrements(0) + { + my_scheduler.set_lookahead(1); + my_filename = "default_filename"; + } + + stk::util::Scheduler & get_scheduler() { return my_scheduler; } + + void set_title(std::string title) { my_title = title; } + const std::string & get_title() const { return my_title; } + + void set_name(std::string theName) { my_name = theName; } + const std::string & get_name() const { return my_name; } + + void set_filename(std::string filename) { my_filename = filename; } + const std::string & get_filename() const { return my_filename; } + + void add_step_increment(int start, int increment) { + StepIncrement aStep(start, increment); + my_scheduler.add_interval(start, increment); + my_numStepIncrements++; + } + unsigned get_num_step_increments() const { return my_numStepIncrements; } + + void add_nodal_field(const std::string & internalName, const std::string & newName) + { + FieldName_OutputName_Pair aPair(internalName, newName); + my_nodal_fields.insert(aPair); + } + const std::set & get_nodal_fields() const { return my_nodal_fields; } + + void add_element_field(const std::string & internalName, const std::string & newName) + { + FieldName_OutputName_Pair aPair(internalName, newName); + my_element_fields.insert(aPair); + } + const std::set & get_element_fields() const { return my_element_fields; } + + void add_property(const Ioss::Property & property) { my_properties.add(property); } + Ioss::PropertyManager & get_properties() { return my_properties; } + + private: + stk::util::Scheduler my_scheduler; + unsigned my_numStepIncrements; + std::string my_name; + std::string my_title; + std::string my_filename; + std::set my_nodal_fields; + std::set my_element_fields; + Ioss::PropertyManager my_properties; + }; + +} // namespace krino + +#endif /* Akri_ResultsOutputOptions_h */ diff --git a/packages/krino/krino/region/Akri_Simulation.cpp b/packages/krino/krino/region/Akri_Simulation.cpp new file mode 100644 index 000000000000..313f6c1e4842 --- /dev/null +++ b/packages/krino/krino/region/Akri_Simulation.cpp @@ -0,0 +1,179 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace krino{ + +std::unique_ptr Simulation::the_simulation; + +Simulation & Simulation::build(const std::string & in_name) +{ + ThrowRequireMsg(!the_simulation, "Simulation already set."); + the_simulation = std::make_unique(in_name); + return *the_simulation; +} + +Simulation & Simulation::get() +{ + ThrowRequireMsg(the_simulation, "Simulation should already be set."); + return *the_simulation; +} + +Simulation::Simulation(const std::string & in_name) + : my_name(in_name), + my_timer("krino", sierra::Diag::sierraTimer()), + my_is_transient(false), + my_stop_time(0.0), + my_step_count(0), + my_current_time(0.0), + my_time_step_size(0.0) +{ + stk::diag::setEnabledTimerMetricsMask(stk::diag::METRICS_CPU_TIME | stk::diag::METRICS_WALL_TIME); +} + +Simulation::~Simulation() {} + +void Simulation::add_region(Region * region) +{ + my_regions.emplace_back(region); +} + +void Simulation::commit() +{/* %TRACE% */ Trace trace__("krino::Simulation::commit()"); /* %TRACE% */ + for (auto && region : my_regions) + { + region->commit(); + } +} + +void Simulation::execute() +{/* %TRACE% */ Trace trace__("krino::Simulation::execute()"); /* %TRACE% */ + stk::diag::TimeBlock timer__(my_timer); + + //===================================== + // start-up procedure + //===================================== + + // initial conditions + for (auto && region : my_regions) + { + region->initialize(); + } + + for (auto && region : my_regions) + { + region->process_output(false); + } + + //===================================== + // time integration + //===================================== + + while ( my_current_time < my_stop_time ) + { + static const double sqrt_epsilon = std::sqrt(std::numeric_limits::epsilon()); + if (my_current_time+my_time_step_size > my_stop_time*(1.-sqrt_epsilon)) + { + my_time_step_size = my_stop_time - my_current_time; + my_current_time = my_stop_time; + } + else + { + my_current_time += my_time_step_size; + } + ++my_step_count; + + const double percentComplete = 100.0*my_current_time/my_stop_time; + + krinolog << "Krino Simulation " << my_name + << ", step " << my_step_count + << ", time " << my_current_time + << ", time step " << my_time_step_size + << ", " << percentComplete << "% complete" + << stk::diag::dendl; + + for (auto && region : my_regions) + { + region->execute(); + } + + for (auto && region : my_regions) + { + region->process_output(false); + } + + for (auto && region : my_regions) + { + region->get_stk_mesh_bulk_data().update_field_data_states(); + } + } + + timer__.stop(); + print_performance_info(); +} + +void +Simulation::print_performance_info() const +{ + /* %TRACE% */ Trace trace__("krino::Simulation::print_timer_information()"); /* %TRACE% */ + + sierra::Env::outputP0() << sierra::Env::section_separator() << std::endl + << "Timing summary running on " << sierra::Env::parallel_size() << " processor" << (sierra::Env::parallel_size() == 1 ? "" : "s") << std::endl; + stk::diag::printTimersTable(sierra::Env::outputP0(), sierra::Diag::sierraTimer(), stk::diag::METRICS_CPU_TIME | stk::diag::METRICS_WALL_TIME, false, sierra::Env::parallel_comm()); + sierra::Env::outputP0() << std::endl << std::endl; + + { + size_t now, hwm; + stk::get_memory_usage(now, hwm); + // min, max, sum + size_t global_now[3] = {now,now,now}; + size_t global_hwm[3] = {hwm,hwm,hwm}; + + stk::all_reduce(sierra::Env::parallel_comm(), stk::ReduceSum<1>( &global_now[2] ) ); + stk::all_reduce(sierra::Env::parallel_comm(), stk::ReduceMin<1>( &global_now[0] ) ); + stk::all_reduce(sierra::Env::parallel_comm(), stk::ReduceMax<1>( &global_now[1] ) ); + + stk::all_reduce(sierra::Env::parallel_comm(), stk::ReduceSum<1>( &global_hwm[2] ) ); + stk::all_reduce(sierra::Env::parallel_comm(), stk::ReduceMin<1>( &global_hwm[0] ) ); + stk::all_reduce(sierra::Env::parallel_comm(), stk::ReduceMax<1>( &global_hwm[1] ) ); + + sierra::Env::outputP0() << "Memory Overview: " << std::endl; + + sierra::Env::outputP0() << " total (over all cores) current/high-water mark = " + << std::setw(15) << stk::human_bytes(global_now[2]) + << std::setw(15) << stk::human_bytes(global_hwm[2]) + << std::endl; + + sierra::Env::outputP0() << " min (over all cores) current/high-water mark = " + << std::setw(15) << stk::human_bytes(global_now[0]) + << std::setw(15) << stk::human_bytes(global_hwm[0]) + << std::endl; + + sierra::Env::outputP0() << " max (over all cores) current/high-water mark = " + << std::setw(15) << stk::human_bytes(global_now[1]) + << std::setw(15) << stk::human_bytes(global_hwm[1]) + << std::endl; + } +} + +} // namespace krino diff --git a/packages/krino/krino/region/Akri_Simulation.hpp b/packages/krino/krino/region/Akri_Simulation.hpp new file mode 100644 index 000000000000..62a0c300e12b --- /dev/null +++ b/packages/krino/krino/region/Akri_Simulation.hpp @@ -0,0 +1,68 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef Akri_Simulation_h +#define Akri_Simulation_h + +#include +#include +#include +#include +#include + +namespace krino{ + +class Region; + +class Simulation { +public: + static Simulation & build(const std::string & in_name); + Simulation(const std::string & in_name); // public to allow make_unique, but should be created using build() + ~Simulation(); + + void commit(); + void initialize() {} + void execute(); + + void add_region(Region * region); + + const std::string & get_name() const { return my_name; } + bool is_transient() const { return my_is_transient; } + double get_time_step() const { return my_time_step_size; } + double get_current_time() const { return my_current_time; } + double get_stop_time() const { return my_stop_time; } + int get_time_step_count() const { return my_step_count; } + + void set_time_step(const double in_time_step) { my_time_step_size = in_time_step; } + void set_current_time(const double in_time) { my_current_time = in_time; } + void set_stop_time(const double in_time) { my_is_transient = true; my_stop_time = in_time; } + + stk::diag::Timer & get_timer() const { return my_timer; } + void print_performance_info() const; + + static Simulation & get(); + static void reset() { the_simulation.reset(); } + +private: + static std::unique_ptr the_simulation; + +private: + std::string my_name; + mutable stk::diag::Timer my_timer; + bool my_is_transient; + double my_stop_time; + unsigned my_step_count; + double my_current_time; + double my_time_step_size; + std::vector> my_regions; +}; + +} // namespace krino + +#endif // Akri_Simulation_h + diff --git a/packages/krino/krino/region/Akri_Startup.cpp b/packages/krino/krino/region/Akri_Startup.cpp new file mode 100644 index 000000000000..2496ae35d651 --- /dev/null +++ b/packages/krino/krino/region/Akri_Startup.cpp @@ -0,0 +1,306 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino{ + +namespace { + +std::string +get_program_path(const char *program) +{ + // If we already have the full path, just return it + if (program[0] == '/') + return program; + + char full_path[PATH_MAX]; + if (strchr(program, '/') != nullptr) { + realpath(program, full_path); + return full_path; + } + + char *PATH = getenv("PATH"); + while (PATH && *PATH) { + // Get the character past the end of the next directory in PATH, i.e. + // either the '/' or the '\0' + char *end = strchr(PATH, ':'); + if (!end) { + end = PATH+strlen(PATH); + } + + // Set current = directory + '/' + program + strncpy(full_path, PATH, end-PATH); + full_path[end-PATH] = '/'; + strcpy(&full_path[end-PATH+1], program); + + // Check whether possible exists + if (access(full_path, X_OK) == 0) + return full_path; + + // Advance to the next directory + PATH = *end ? end+1 : end; + } + + // Not found; this shouldn't happen, but maybe the executable got deleted + // after it was invoked before we got here -- or we have some crazy + // parallel machine where the executable is inaccessible on the compute + // nodes despite it somehow having been loaded. No big deal, just return + // the non-absolute path. + return program; +} + +} // namespace + +Startup::Startup(int argc, char ** argv) + : my_flag_exit_early(false) +{ + if ( MPI_SUCCESS != MPI_Init( &argc , &argv ) ) { + throw std::runtime_error("MPI_Init failed"); + } + + stk::EnvData::instance().m_parallelComm = MPI_COMM_WORLD; + MPI_Comm_size(stk::EnvData::parallel_comm(), &stk::EnvData::instance().m_parallelSize); + MPI_Comm_rank(stk::EnvData::parallel_comm(), &stk::EnvData::instance().m_parallelRank); + + stk::register_message_type(stk::MSG_WARNING, 10000000, "Warning"); + stk::register_message_type(stk::MSG_DOOMED, 10000000, "Parser error"); + stk::register_message_type(stk::MSG_EXCEPTION, 1000000, "Exception"); + sierra::Diag::registerWriter("krinolog", krinolog, theDiagWriterParser()); + + // Register this so that -version will know this region is used. + register_product(); + + stk::set_report_handler(report_handler); + + setup_commandline_options(); + + // parse command line options + const stk::OptionsSpecification &desc = stk::get_options_specification(); + stk::ParsedOptions &parsedOptions = stk::get_parsed_options(); + stk::parse_command_line_args(argc, const_cast(argv), desc, parsedOptions); + + for (auto && writer : sierra::Diag::getWriterRegistry()) + { + if (parsedOptions.count(writer.first)) + { + writer.second.first->setPrintMask(writer.second.second->parse(parsedOptions[writer.first].as().c_str())); + } + } + + if ( parsedOptions.count("help") ) { + if (0 == stk::EnvData::parallel_rank()) + { + std::cerr << desc << std::endl; + } + my_flag_exit_early = true; + } + + if (parsedOptions.count("version")) { + if (0 == stk::EnvData::parallel_rank()) + { + std::cerr << "Version: Krino1.0" << std::endl; + } + my_flag_exit_early = true; + } + + int local_flag_exit_early = my_flag_exit_early; + MPI_Allreduce(&local_flag_exit_early, &my_flag_exit_early, 1, MPI_INT, MPI_MAX, stk::EnvData::parallel_comm()); + if (my_flag_exit_early) return; + + const std::string inputFileName = sierra::Env::get_param("input-deck"); + if (stk::EnvData::instance().m_inputFileRequired) + { + if(inputFileName == "") + { + throw std::runtime_error("No input file specified. An input file must be specified with the '-i' option"); + } + + stk::EnvData::setInputFileName(inputFileName); + } + + // setup logfile as inputFileName.log + std::string logFileName = parsedOptions["output-log"].as(); + if (logFileName == "") + { + int dotPos = inputFileName.rfind("."); + if ( -1 == dotPos ) { + // lacking extension + logFileName = inputFileName + ".log"; + } + else { + // with extension; swap with .log + logFileName = inputFileName.substr(0, dotPos) + ".log"; + } + } + + // set up output streams + const std::string output_description = + (0 == stk::EnvData::parallel_rank()) ? + "outfile=" + logFileName + " out>outfile+pout dout>out" : + "out>pout dout>out"; + std::string parallel_output_description = " pout>null"; + if (parsedOptions.count("pout")) + { + std::ostringstream s; + s << " poutfile=" << logFileName << "." << stk::EnvData::parallel_size() << "." << stk::EnvData::parallel_rank() << " pout>poutfile"; + parallel_output_description = s.str(); + } + stk::bind_output_streams(output_description+parallel_output_description); + stk::EnvData::instance().m_outputP0 = &sierra::out(); + + // output run info + const std::string program_path = get_program_path(argv[0]); + const char * build_date_time = __DATE__ " " __TIME__; + stk::EnvData::instance().m_executablePath = program_path; + + stk::EnvData::instance().m_productName = krino::get_product_name(); + stk::ProductRegistry::AttributeMap &product_attributes = stk::ProductRegistry::instance().getProductAttributeMap(sierra::Env::product_name()); + product_attributes[stk::ProductRegistry::BUILD_TIME] = build_date_time; + product_attributes[stk::ProductRegistry::EXECUTABLE] = program_path; + + sierra::Env::outputP0() + << sierra::Env::subsection_separator() + << std::endl + << "Executable Name = " << sierra::Env::product_name() << std::endl + << "Executable Version= " << stk::ProductRegistry::version() << std::endl + << "Executable Date = " << stk::ProductRegistry::instance().getProductAttribute(sierra::Env::product_name(), stk::ProductRegistry::BUILD_TIME) << std::endl + << "Executable File = " << sierra::Env::executable_file() << std::endl + << "Run Start Date = " << sierra::Env::startup_date() << std::endl << std::endl + << "Working Directory = " << sierra::Env::working_directory() << std::endl + << "Parsed Input File = " << sierra::Env::getInputFileName() << std::endl + << sierra::Env::subsection_separator() + << std::endl << std::endl ; +} + +void Startup::setup_commandline_options() +{ + stk::OptionsSpecification desc("Allowed options"); + desc.add_options() + ("help,h", "produce help message") + ("input-deck,i", "Analysis input file", stk::DefaultValue("")) + ("output-log,o", "Output log file path, one of : 'cout', 'cerr', or a file path", stk::DefaultValue("")) + ("aprepro,a", "Process (on) or don't process (off) input with aprepro. Default=on.", stk::ImplicitValue("on")) + ("define,D", "Define symbols for use in aprepro processing of input file", stk::ValueType()) + ("pout", "use separate output log file for each MPI process"); + + for (auto && writer : sierra::Diag::getWriterRegistry()) + { + std::ostringstream str; + str << "Diagnostic writer " << writer.first << std::endl; + writer.second.second->describe(str); + desc.add_options()(writer.first.c_str(), str.str().c_str(), stk::ValueType()); + } + + stk::get_options_specification().add(desc); +} + +Startup::~Startup() +{ + MPI_Finalize(); +} + +void Startup::handle_exception(const char * what, const bool is_parsing) +{ + krinolog << stk::diag::dendl; + krinolog << "Exception: " << what << stk::diag::dendl; + krinolog << stk::diag::Traceback::printTraceback(stk::diag::Traceback::snapshot()) << stk::diag::dendl; + if (is_parsing) + { + if (0 == stk::EnvData::parallel_rank()) + { + std::cerr << "*** Parser Error ***\n*** check log file for more information ***" << std::endl; + } + std::cout << std::flush; + std::cerr << std::flush; + + ::sleep(1); // Give the other processors a chance at + // catching up, seems to help hanging problems. + + MPI_Finalize(); + std::exit(1); + } + + if (0 == stk::EnvData::parallel_rank()) + { + std::cerr << "*** ABORT on P0 ***\n*** check log file for more information ***" << std::endl; + } + else + { + // give root proc chance to die first + ::sleep(2); + std::cerr << "*** ABORT on P" < +#include + +namespace krino{ + +class Startup +{ + public: + + Startup(int argc, char ** argv); + ~Startup(); + + bool exit_early() const { return my_flag_exit_early; } + void handle_exception(const char * what, const bool is_parsing); + + static void setup_commandline_options(); + static void report_handler(const char *message, int type); + +private: + int my_flag_exit_early; +}; + +} // namespace krino + +#endif // Akri_Startup_h diff --git a/packages/krino/krino/region/CMakeLists.txt b/packages/krino/krino/region/CMakeLists.txt new file mode 100644 index 000000000000..35b1a27d73b2 --- /dev/null +++ b/packages/krino/krino/region/CMakeLists.txt @@ -0,0 +1,19 @@ +SET(HEADERS "") +SET(SOURCES "") + +INCLUDE_DIRECTORIES(${${PACKAGE_NAME}_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +FILE(GLOB HEADERS *.hpp) +FILE(GLOB SOURCES *.cpp) + +TRIBITS_ADD_LIBRARY( + krino_region_lib + HEADERS ${HEADERS} + SOURCES ${SOURCES} + DEPLIBS krino_lib krino_adaptivity_interface_lib) + +INSTALL(FILES ${HEADERS} DESTINATION + ${CMAKE_INSTALL_PREFIX}/${${PROJECT_NAME}_INSTALL_INCLUDE_DIR}/krino_region_lib) + diff --git a/packages/krino/krino/unit_tests/Akri_UnitMathUtils.cpp b/packages/krino/krino/unit_tests/Akri_UnitMathUtils.cpp new file mode 100644 index 000000000000..8df8c1a1f209 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_UnitMathUtils.cpp @@ -0,0 +1,64 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include + +namespace krino { + +void expect_root(const double goldRoot, const double xTol, const std::function & f) +{ + const unsigned maxIters = 100; + const auto result = find_root(f, 0., 1., f(0.), f(1.), maxIters, xTol); + ASSERT_TRUE(result.first); + EXPECT_NEAR(goldRoot, result.second, xTol); +} + +void expect_root_newton_raphson(const double goldRoot, const double guess, const double fTol, const std::function(const double)> & f) +{ + const unsigned maxIters = 100; + const auto result = find_root_newton_raphson(f, guess, maxIters, fTol); + ASSERT_TRUE(result.first); + const auto valueAndDeriv = f(result.second); + const double xTol = fTol / std::abs(valueAndDeriv.second); + EXPECT_NEAR(0., valueAndDeriv.first, fTol); + EXPECT_NEAR(goldRoot, result.second, xTol); +} + +TEST(find_root, givenPolynomialFunction_findRootWithinTolerance) +{ + const double tol = 1.e-5; + expect_root(0.25, tol, [](const double x){ return x-0.25; }); + expect_root(0.25, tol, [](const double x){ return x*x-0.25*0.25; }); + expect_root(0.25, tol, [](const double x){ return x*x*x-0.25*0.25*0.25; }); +} + + +TEST(find_root_newton_raphson, givenPolynomialFunctionWithCorrectJacobian_findRootWithinTolerance) +{ + const double tol = 1.e-5; + const double guess = 1.; + expect_root_newton_raphson(0.25, guess, tol, [](const double x){ std::cout << "Eval at " << x << std::endl; return std::make_pair(x-0.25, 1.); }); + expect_root_newton_raphson(0.25, guess, tol, [](const double x){ std::cout << "Eval at " << x << std::endl; return std::make_pair(x*x-0.25*0.25, 2.*x); }); + expect_root_newton_raphson(0.25, guess, tol, [](const double x){ std::cout << "Eval at " << x << std::endl; return std::make_pair(x*x*x-0.25*0.25*0.25, 3.*x*x); }); +} + +TEST(find_root_newton_raphson, givenPolynomialFunctionWithWRONGJacobian_findRootWithinTolerance) +{ + const double tol = 1.e-5; + const double guess = 1.; + const double error = 0.1; // Less than 1 to make function overshoot + expect_root_newton_raphson(0.25, guess, tol, [error](const double x){ std::cout << "Eval at " << x << std::endl; return std::make_pair(x-0.25, 1.*error); }); + expect_root_newton_raphson(0.25, guess, tol, [error](const double x){ std::cout << "Eval at " << x << std::endl; return std::make_pair(x*x-0.25*0.25, 2.*x*error); }); + expect_root_newton_raphson(0.25, guess, tol, [error](const double x){ std::cout << "Eval at " << x << std::endl; return std::make_pair(x*x*x-0.25*0.25*0.25, 3.*x*x*error); }); +} + +} + diff --git a/packages/krino/krino/unit_tests/Akri_UnitTestUtils.cpp b/packages/krino/krino/unit_tests/Akri_UnitTestUtils.cpp new file mode 100644 index 000000000000..fbb85740d585 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_UnitTestUtils.cpp @@ -0,0 +1,23 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include + +namespace krino { + +void expect_eq(const Vector3d & gold, const Vector3d & result, const double relativeTol=1.e-6) +{ + const double absoluteTol = relativeTol * (gold.length() + result.length()); + for (int i=0; i<3; ++i) + EXPECT_NEAR(gold[i], result[i], absoluteTol) <<"gold: " << gold << " actual:" << result; +} + +} + + diff --git a/packages/krino/krino/unit_tests/Akri_UnitTestUtils.hpp b/packages/krino/krino/unit_tests/Akri_UnitTestUtils.hpp new file mode 100644 index 000000000000..ebb0e3b7dbe0 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_UnitTestUtils.hpp @@ -0,0 +1,19 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_UNIT_TESTS_INCLUDE_AKRI_UNITTESTUTILS_H_ +#define KRINO_UNIT_TESTS_INCLUDE_AKRI_UNITTESTUTILS_H_ +#include + +namespace krino { + +void expect_eq(const Vector3d & gold, const Vector3d & result, const double relativeTol=1.e-6); + +} + +#endif /* KRINO_UNIT_TESTS_INCLUDE_AKRI_UNITTESTUTILS_H_ */ diff --git a/packages/krino/krino/unit_tests/Akri_Unit_Analytic_CDMesh.cpp b/packages/krino/krino/unit_tests/Akri_Unit_Analytic_CDMesh.cpp new file mode 100644 index 000000000000..59ac23793422 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_Analytic_CDMesh.cpp @@ -0,0 +1,276 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino { + +static void expect_good_mesh(const stk::mesh::BulkData & mesh, const CDMesh & cdmesh) +{ + EXPECT_TRUE(check_induced_parts(mesh)); + EXPECT_TRUE(check_face_and_edge_ownership(mesh)); + EXPECT_TRUE(check_face_and_edge_relations(mesh)); + EXPECT_TRUE(check_shared_entity_nodes(mesh)); + EXPECT_TRUE(cdmesh.check_element_side_parts()); +} + +template +class AnalyticDecompositionFixture : public ::testing::Test +{ +public: + AnalyticDecompositionFixture() + : fixture(), cdfemSupport(CDFEM_Support::get(fixture.meta_data())) + { + AuxMetaData & aux_meta = AuxMetaData::get(fixture.meta_data()); + auto & vec_type = fixture.meta_data().spatial_dimension() == 2 ? FieldType::VECTOR_2D : FieldType::VECTOR_3D; + coord_field = aux_meta.register_field("coordinates", vec_type, stk::topology::NODE_RANK, 1u, 1u, fixture.meta_data().universal_part()); + cdfemSupport.set_coords_field(coord_field); + cdfemSupport.add_interpolation_field(coord_field); + + cdfemSupport.set_prolongation_model(INTERPOLATION); + } + + void setup_phase_support(const stk::mesh::PartVector & blocks) + { + PhaseTag p, n; + const LevelSet_Identifier id0(0); + p.add(id0, 1); + n.add(id0, -1); + PhaseVec named_phases{{"A", p}, {"B", n}}; + + Phase_Support & phase_support = Phase_Support::get(fixture.meta_data()); + Block_Surface_Connectivity block_surface_info; + phase_support.set_input_block_surface_connectivity(block_surface_info); + LevelSet * ls_ptr = nullptr; + phase_support.register_blocks_for_level_set(ls_ptr, blocks); + phase_support.decompose_blocks(blocks, named_phases, LS_Name_Generator()); + LS_Field lsField("LS", id0); + cdfemSupport.add_ls_field(lsField, nullptr); + } + + stk::mesh::Part & declare_input_block(const std::string & name, const stk::topology topo) + { + auto & block_part = fixture.meta_data().declare_part_with_topology(name, topo); + stk::io::put_io_part_attribute(block_part); + return block_part; + } + + void decompose_mesh(const InterfaceGeometry & interfaceGeometry) + { + NodeToCapturedDomainsMap nodesToCapturedDomains; + if (cdfemSupport.get_cdfem_edge_degeneracy_handling() == SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE) + nodesToCapturedDomains = snap_as_much_as_possible_while_maintaining_quality(krino_mesh->stk_bulk(), krino_mesh->get_active_part(), cdfemSupport.get_interpolation_fields(), interfaceGeometry, cdfemSupport.get_global_ids_are_parallel_consistent()); + interfaceGeometry.prepare_to_process_elements(krino_mesh->stk_bulk(), nodesToCapturedDomains); + + if(!krino_mesh->my_old_mesh) + { + krino_mesh->my_old_mesh = std::make_shared(fixture.bulk_data(), std::shared_ptr()); + krino_mesh->my_old_mesh->generate_nonconformal_elements(); + } + + krino_mesh->generate_nonconformal_elements(); + if (cdfemSupport.get_cdfem_edge_degeneracy_handling() == SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE) + krino_mesh->snap_nearby_intersections_to_nodes(interfaceGeometry, nodesToCapturedDomains); + krino_mesh->set_phase_of_uncut_elements(interfaceGeometry); + krino_mesh->triangulate(interfaceGeometry); + krino_mesh->my_old_mesh->stash_field_data(-1, *krino_mesh); + + + krino_mesh->decompose(); + krino_mesh->modify_mesh(); + krino_mesh->prolongation(); + } + + void commit() + { + krino_mesh = std::make_shared(fixture.bulk_data(), std::shared_ptr()); + } + + void write_results(const std::string & filename) + { + stk::io::write_mesh(filename, fixture.bulk_data()); + } + + void test_build_good_mesh(const bool doSnapping, + const InterfaceGeometry & interfaceGeometry, + const typename BoundingBoxMesh::BoundingBoxType & domain, + const double meshSize, + const std::string & filename = "") + { + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + if (parallel_size > 2) return; + + if (doSnapping) + cdfemSupport.set_cdfem_edge_degeneracy_handling(SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE); + else + cdfemSupport.set_cdfem_edge_degeneracy_handling(SNAP_TO_NODE); + + auto & block1_part = aux_meta.get_part("block_1"); + setup_phase_support({&block1_part}); + + fixture.set_domain(domain, meshSize); + fixture.populate_mesh(); + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part(),&aux_meta.active_part()}); + + commit(); + + try + { + decompose_mesh(interfaceGeometry); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + expect_good_mesh(mesh, *krino_mesh); + + std::cout << log.get_log() << std::endl; + if (!filename.empty()) + write_results(filename); + } + + MESH_FIXTURE fixture; + FieldRef coord_field; + CDFEM_Support & cdfemSupport; + std::shared_ptr krino_mesh; + LogRedirecter log; +}; + +template +class SphereDecompositionFixture : public AnalyticDecompositionFixture +{ +public: + SphereDecompositionFixture() + { + mySphereGeometry.reset(new AnalyticSurfaceInterfaceGeometry(mySphere, AuxMetaData::get(this->fixture.meta_data()).active_part(), this->cdfemSupport, Phase_Support::get(this->fixture.meta_data()))); + } +protected: + const InterfaceGeometry & get_interface_geometry() const { return *mySphereGeometry; } + typename BoundingBoxMesh::BoundingBoxType get_domain() const + { + const Vector3d extents = (2 == this->fixture.meta_data().spatial_dimension()) ? Vector3d(1.,1.,0.) : Vector3d(1.,1.,1.); + typename BoundingBoxMesh::BoundingBoxType domain(-0.5*extents, 0.5*extents); + return domain; + } + double get_mesh_size() const { return 1./6.; } + Sphere mySphere{"test sphere", Vector3d::ZERO, 0.35}; + std::unique_ptr mySphereGeometry; +}; + +typedef SphereDecompositionFixture CDMeshSphereTestsBboxMesh2D; +TEST_F(CDMeshSphereTestsBboxMesh2D, Sphere_SnapMesh) +{ + const bool doSnap = true; + test_build_good_mesh(doSnap, get_interface_geometry(), get_domain(), get_mesh_size()); //test_build_good_mesh(doSnap, get_interface_geometry(), get_domain(), get_mesh_size(), "2DSnapSphere.e"); +} + +TEST_F(CDMeshSphereTestsBboxMesh2D, Sphere_CutMesh) +{ + const bool doSnap = false; + test_build_good_mesh(doSnap, get_interface_geometry(), get_domain(), get_mesh_size()); //test_build_good_mesh(doSnap, get_interface_geometry(), get_domain(), get_mesh_size(), "2DCutSphere.e"); +} + +typedef SphereDecompositionFixture CDMeshSphereTestsBboxMesh3D; +TEST_F(CDMeshSphereTestsBboxMesh3D, Sphere_SnapMesh) +{ + const bool doSnap = true; + test_build_good_mesh(doSnap, get_interface_geometry(), get_domain(), get_mesh_size()); //test_build_good_mesh(doSnap, get_interface_geometry(), get_domain(), get_mesh_size(), "3DSnapSphere.e"); +} + +TEST_F(CDMeshSphereTestsBboxMesh3D, Sphere_CutMesh) +{ + const bool doSnap = false; + test_build_good_mesh(doSnap, get_interface_geometry(), get_domain(), get_mesh_size()); //test_build_good_mesh(doSnap, get_interface_geometry(), get_domain(), get_mesh_size(), "3DCutSphere.e"); +} + +template +class CubeDecompositionFixture : public AnalyticDecompositionFixture +{ +public: + CubeDecompositionFixture() + { + const double x = 0.25; + const std::array cubeVerts = + {{ + {-x,-x,-x}, {+x,-x,-x}, {+x,+x,-x}, {-x,+x,-x}, + {-x,-x,+x}, {+x,-x,+x}, {+x,+x,+x}, {-x,+x,+x} + }}; + + const std::array,12> facetsVerts + {{ + {{0,4,7}}, {{0,7,3}}, + {{1,2,6}}, {{1,6,5}}, + {{0,1,5}}, {{0,5,4}}, + {{2,3,7}}, {{2,7,6}}, + {{0,3,2}}, {{0,2,1}}, + {{4,5,6}}, {{4,6,7}} + }}; + for (auto && facetVerts : facetsVerts) + { + std::unique_ptr facet = std::make_unique( cubeVerts[facetVerts[0]], cubeVerts[facetVerts[1]], cubeVerts[facetVerts[2]] ); + myCube.add( std::move(facet) ); + } + + myCubeGeometry.reset(new AnalyticSurfaceInterfaceGeometry(myCube, AuxMetaData::get(this->fixture.meta_data()).active_part(), this->cdfemSupport, Phase_Support::get(this->fixture.meta_data()))); + } +protected: + const InterfaceGeometry & get_interface_geometry() const { return *myCubeGeometry; } + typename BoundingBoxMesh::BoundingBoxType get_domain() const + { + const Vector3d extents = (2 == this->fixture.meta_data().spatial_dimension()) ? Vector3d(1.,1.,0.) : Vector3d(1.,1.,1.); + typename BoundingBoxMesh::BoundingBoxType domain(-0.5*extents, 0.5*extents); + return domain; + } + double get_mesh_size() const { return 1./6.; } + Faceted_Surface myCube{"test cube"}; + std::unique_ptr myCubeGeometry; +}; + +typedef CubeDecompositionFixture CDMeshCubeTestsBboxMesh2D; +TEST_F(CDMeshCubeTestsBboxMesh2D, Cube_SnapMesh) +{ + const bool doSnap = true; + test_build_good_mesh(doSnap, get_interface_geometry(), get_domain(), get_mesh_size()); //test_build_good_mesh(doSnap, get_interface_geometry(), get_domain(), get_mesh_size(), "2DSnapCube.e"); +} + +typedef CubeDecompositionFixture CDMeshCubeTestsBboxMesh3D; +TEST_F(CDMeshCubeTestsBboxMesh3D, Sphere_SnapMesh) +{ + const bool doSnap = true; + test_build_good_mesh(doSnap, get_interface_geometry(), get_domain(), get_mesh_size()); //test_build_good_mesh(doSnap, get_interface_geometry(), get_domain(), get_mesh_size(), "3DSnapCube.e"); +} + +} // namespace krino + diff --git a/packages/krino/krino/unit_tests/Akri_Unit_CDFEM_Parent_Edge.cpp b/packages/krino/krino/unit_tests/Akri_Unit_CDFEM_Parent_Edge.cpp new file mode 100644 index 000000000000..5b912bda3b65 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_CDFEM_Parent_Edge.cpp @@ -0,0 +1,782 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include + +#include + +namespace krino +{ + +TEST(CDFEM_Parent_Edge_One_LS, Two_Nodes_No_Snapping) +{ + const InterfaceID iface(0,0); + std::vector > nodes_isovar(2); + nodes_isovar[0].resize(1); + nodes_isovar[1].resize(1); + nodes_isovar[0][0] = 1; + nodes_isovar[1][0] = 1; + + CDFEM_Parent_Edge edge(nodes_isovar); + EXPECT_FALSE(edge.have_any_crossings()); + EXPECT_EQ(1, edge.get_crossing_sign(iface)); + + nodes_isovar[0][0] = -1; + nodes_isovar[1][0] = -1; + edge.find_crossings(nodes_isovar); + EXPECT_FALSE(edge.have_any_crossings()); + EXPECT_EQ(-1, edge.get_crossing_sign(iface)); + + nodes_isovar[0][0] = -1; + nodes_isovar[1][0] = 2; + edge.find_crossings(nodes_isovar); + EXPECT_TRUE(edge.have_any_crossings()); + EXPECT_TRUE(edge.have_crossing(iface)); + EXPECT_DOUBLE_EQ(1./3., edge.get_crossing_position(iface)); + EXPECT_EQ(1, edge.get_crossing_sign(iface)); +} + +TEST(CDFEM_Parent_Edge_One_LS, Three_Nodes_No_Snapping) +{ + std::vector > nodes_isovar(3); + nodes_isovar[0].resize(1); + nodes_isovar[1].resize(1); + nodes_isovar[2].resize(1); + nodes_isovar[0][0] = 1; + nodes_isovar[1][0] = 1; + nodes_isovar[2][0] = 1; + const InterfaceID iface(0,0); + + CDFEM_Parent_Edge edge({0.0,0.5,1.0}, nodes_isovar); + EXPECT_FALSE(edge.have_any_crossings()); + EXPECT_EQ(1, edge.get_crossing_sign(iface)); + + nodes_isovar[0][0] = -1; + nodes_isovar[1][0] = -1; + nodes_isovar[2][0] = -1; + edge.find_crossings(nodes_isovar); + EXPECT_FALSE(edge.have_any_crossings()); + EXPECT_EQ(-1, edge.get_crossing_sign(iface)); + + nodes_isovar[0][0] = -1; + nodes_isovar[1][0] = 1; + nodes_isovar[2][0] = -1; + edge.find_crossings(nodes_isovar); + EXPECT_FALSE(edge.have_any_crossings()); + EXPECT_EQ(-1, edge.get_crossing_sign(iface)); + + nodes_isovar[0][0] = 1; + nodes_isovar[1][0] = -1; + nodes_isovar[2][0] = 1; + edge.find_crossings(nodes_isovar); + EXPECT_FALSE(edge.have_any_crossings()); + EXPECT_EQ(1, edge.get_crossing_sign(iface)); + + nodes_isovar[0][0] = -1; + nodes_isovar[1][0] = 2; + nodes_isovar[2][0] = 4; + edge.find_crossings(nodes_isovar); + EXPECT_TRUE(edge.have_any_crossings()); + const InterfaceID interface_id(0,0); + EXPECT_TRUE(edge.have_crossing(interface_id)); + EXPECT_DOUBLE_EQ(1./6., edge.get_crossing_position(interface_id)); + EXPECT_EQ(1, edge.get_crossing_sign(iface)); + + nodes_isovar[0][0] = -1; + nodes_isovar[1][0] = -1; + nodes_isovar[2][0] = 1; + edge.find_crossings(nodes_isovar); + EXPECT_TRUE(edge.have_any_crossings()); + EXPECT_TRUE(edge.have_crossing(interface_id)); + EXPECT_DOUBLE_EQ(3./4., edge.get_crossing_position(interface_id)); + EXPECT_EQ(1, edge.get_crossing_sign(iface)); +} + +TEST(CDFEM_Parent_Edge_Two_LS, Two_Nodes_No_Snapping) +{ + Phase_Support::set_one_levelset_per_phase(true); + + const InterfaceID iface(0,1); + + std::vector > nodes_isovar(2); + nodes_isovar[0].resize(2); + nodes_isovar[1].resize(2); + + // crossing_sign convention, iface.first phase corresponds to (-) for one LS=one interface case + // iface.first lower everywhere + { + nodes_isovar[0][0] = 0; + nodes_isovar[0][1] = 1; + nodes_isovar[1][0] = 0; + nodes_isovar[1][1] = 1; + CDFEM_Parent_Edge edge(nodes_isovar); + EXPECT_FALSE(edge.have_any_crossings()); + EXPECT_EQ(0, edge.get_uncrossed_phase()); + } + + // iface.second lower everywhere + { + nodes_isovar[0][0] = 2; + nodes_isovar[0][1] = 1; + nodes_isovar[1][0] = 2; + nodes_isovar[1][1] = 1; + CDFEM_Parent_Edge edge(nodes_isovar); + EXPECT_FALSE(edge.have_any_crossings()); + EXPECT_EQ(1, edge.get_uncrossed_phase()); + } + + // first lower at node 0, second lower at node 1 + { + nodes_isovar[0][0] = 0.5; + nodes_isovar[0][1] = 1; + nodes_isovar[1][0] = 1.5; + nodes_isovar[1][1] = 1; + CDFEM_Parent_Edge edge(nodes_isovar); + EXPECT_TRUE(edge.have_any_crossings()); + EXPECT_TRUE(edge.have_crossing(iface)); + EXPECT_DOUBLE_EQ(0.5, edge.get_crossing_position(iface)); + EXPECT_EQ(1, edge.get_crossing_sign(iface)); + } + + // second lower at node 0, first lower at node 1 + { + nodes_isovar[0][0] = 1.5; + nodes_isovar[0][1] = 1; + nodes_isovar[1][0] = 0.5; + nodes_isovar[1][1] = 1; + CDFEM_Parent_Edge edge(nodes_isovar); + EXPECT_TRUE(edge.have_any_crossings()); + EXPECT_TRUE(edge.have_crossing(iface)); + EXPECT_DOUBLE_EQ(0.5, edge.get_crossing_position(iface)); + EXPECT_EQ(-1, edge.get_crossing_sign(iface)); + } + + // Equal at node 0, first lower at node 1 + { + nodes_isovar[0][0] = 0.; + nodes_isovar[0][1] = 0.; + nodes_isovar[1][0] = 0; + nodes_isovar[1][1] = 1.; + CDFEM_Parent_Edge edge(nodes_isovar); + EXPECT_TRUE(edge.have_any_crossings()); + EXPECT_TRUE(edge.have_crossing(iface)); + EXPECT_DOUBLE_EQ(0., edge.get_crossing_position(iface)); + EXPECT_EQ(-1, edge.get_crossing_sign(iface)); + } + + // Equal at node 0, second lower at node 1 + { + nodes_isovar[0][0] = 0.; + nodes_isovar[0][1] = 0.; + nodes_isovar[1][0] = 0; + nodes_isovar[1][1] = -1.; + CDFEM_Parent_Edge edge(nodes_isovar); + EXPECT_FALSE(edge.have_any_crossings()); + EXPECT_FALSE(edge.have_crossing(iface)); + } + // Equal at node 0, first lower at node 1 + { + nodes_isovar[0][0] = 0.; + nodes_isovar[0][1] = 1.; + nodes_isovar[1][0] = 0; + nodes_isovar[1][1] = 0.; + CDFEM_Parent_Edge edge(nodes_isovar); + EXPECT_TRUE(edge.have_any_crossings()); + EXPECT_TRUE(edge.have_crossing(iface)); + EXPECT_DOUBLE_EQ(1., edge.get_crossing_position(iface)); + EXPECT_EQ(1, edge.get_crossing_sign(iface)); + } + + // Equal at node 1, second lower at node 0 + { + nodes_isovar[0][0] = 0.; + nodes_isovar[0][1] = -1.; + nodes_isovar[1][0] = 0.; + nodes_isovar[1][1] = 0.; + CDFEM_Parent_Edge edge(nodes_isovar); + EXPECT_FALSE(edge.have_any_crossings()); + EXPECT_FALSE(edge.have_crossing(iface)); + } + + // (0, +delta) at node 0, (0, -) at node 1 + { + nodes_isovar[0][0] = 0.; + nodes_isovar[0][1] = 1.e-16; + nodes_isovar[1][0] = 0.; + nodes_isovar[1][1] = -1.; + CDFEM_Parent_Edge edge(nodes_isovar); + EXPECT_TRUE(edge.have_any_crossings()); + EXPECT_TRUE(edge.have_crossing(iface)); + EXPECT_NEAR(0., edge.get_crossing_position(iface), 1.e-15); + EXPECT_EQ(1, edge.get_crossing_sign(iface)); + } + + // (0, -) at node 0, (0, +delta) at node 1 + { + nodes_isovar[0][0] = 0.; + nodes_isovar[0][1] = -1.; + nodes_isovar[1][0] = 0.; + nodes_isovar[1][1] = 1.e-16; + CDFEM_Parent_Edge edge(nodes_isovar); + EXPECT_TRUE(edge.have_any_crossings()); + EXPECT_TRUE(edge.have_crossing(iface)); + EXPECT_NEAR(1., edge.get_crossing_position(iface), 1.e-15); + EXPECT_EQ(-1, edge.get_crossing_sign(iface)); + } +} + +TEST(CDFEM_Parent_Edge_Three_LS, Two_Nodes_No_Snapping) +{ + Phase_Support::set_one_levelset_per_phase(true); + + const InterfaceID iface01(0,1); + const InterfaceID iface02(0,2); + const InterfaceID iface12(1,2); + + std::vector > nodes_isovar(2); + nodes_isovar[0].resize(3); + nodes_isovar[1].resize(3); + + { + nodes_isovar[0][0] = -0.01288; + nodes_isovar[0][1] = 0.021996; + nodes_isovar[0][2] = 0.01; + nodes_isovar[1][0] = 0.038335; + nodes_isovar[1][1] = 0.037250; + nodes_isovar[1][2] = 0.01; + CDFEM_Parent_Edge edge(nodes_isovar); + EXPECT_TRUE(edge.have_any_crossings()); + EXPECT_FALSE(edge.have_crossing(iface01)); + EXPECT_FALSE(edge.have_crossing(iface12)); + EXPECT_TRUE(edge.have_crossing(iface02)); + const double crossing_pos = (nodes_isovar[0][0] - nodes_isovar[0][2]) / + (nodes_isovar[0][0] - nodes_isovar[1][0] + nodes_isovar[1][2] - nodes_isovar[0][2]); + EXPECT_DOUBLE_EQ(crossing_pos, edge.get_crossing_position(iface02)); + EXPECT_EQ(1, edge.get_crossing_sign(iface02)); + } + + { + nodes_isovar[0][0] = 0.038335; + nodes_isovar[0][1] = 0.037250; + nodes_isovar[0][2] = 0.01; + nodes_isovar[1][0] = -0.01288; + nodes_isovar[1][1] = 0.021996; + nodes_isovar[1][2] = 0.01; + CDFEM_Parent_Edge edge(nodes_isovar); + EXPECT_TRUE(edge.have_any_crossings()); + EXPECT_FALSE(edge.have_crossing(iface01)); + EXPECT_FALSE(edge.have_crossing(iface12)); + EXPECT_TRUE(edge.have_crossing(iface02)); + const double crossing_pos = (nodes_isovar[0][0] - nodes_isovar[0][2]) / + (nodes_isovar[0][0] - nodes_isovar[1][0] + nodes_isovar[1][2] - nodes_isovar[0][2]); + EXPECT_DOUBLE_EQ(crossing_pos, edge.get_crossing_position(iface02)); + EXPECT_EQ(-1, edge.get_crossing_sign(iface02)); + } +} + +TEST(CDFEM_Parent_Edge_Three_LS, Three_Nodes_No_Snapping) +{ + Phase_Support::set_one_levelset_per_phase(true); + + const InterfaceID iface01(0,1); + const InterfaceID iface02(0,2); + const InterfaceID iface12(1,2); + + std::vector > nodes_isovar(3); + nodes_isovar[0].resize(3); + nodes_isovar[1].resize(3); + nodes_isovar[2].resize(3); + + { + nodes_isovar[0][0] = 0.2; + nodes_isovar[0][1] = 0.0025391738062501383; + nodes_isovar[0][2] = 0.01; + nodes_isovar[1][0] = 0.19; + nodes_isovar[1][1] = -0.00048874386459929649; + nodes_isovar[1][2] = 0.01; + nodes_isovar[2][0] = 0.17; + nodes_isovar[2][1] = -0.0052539980592431739; + nodes_isovar[2][2] = 0.01; + CDFEM_Parent_Edge edge({0.0,0.5,1.0}, nodes_isovar); + EXPECT_FALSE(edge.have_any_crossings()); + EXPECT_FALSE(edge.have_crossing(iface01)); + EXPECT_FALSE(edge.have_crossing(iface12)); + EXPECT_FALSE(edge.have_crossing(iface02)); + } + + // Check that uncrossed phase is correct for 3 node problem with 2 crossings on the edge. + { + nodes_isovar[0][0] = 0.015; + nodes_isovar[0][1] = 1.; + nodes_isovar[0][2] = 0.01; + nodes_isovar[1][0] = 0.0095; + nodes_isovar[1][1] = 1.; + nodes_isovar[1][2] = 0.01; + nodes_isovar[2][0] = 0.015; + nodes_isovar[2][1] = 1.; + nodes_isovar[2][2] = 0.01; + CDFEM_Parent_Edge edge({0.0,0.5,1.0}, nodes_isovar); + EXPECT_FALSE(edge.have_any_crossings()); + EXPECT_FALSE(edge.have_crossing(iface01)); + EXPECT_FALSE(edge.have_crossing(iface12)); + EXPECT_FALSE(edge.have_crossing(iface02)); + EXPECT_EQ(2, edge.get_uncrossed_phase()); + } + + // Test edge that goes from phase 1-0-2 in the piecewise approximation, but there is no 0-2 in the linear version, + // so result is just 1-2. + { + nodes_isovar[0][0] = 0.72535; + nodes_isovar[0][1] = -0.844886; + nodes_isovar[0][2] = 0.10576; + nodes_isovar[1][0] = -0.58386; + nodes_isovar[1][1] = -0.931365; + nodes_isovar[1][2] = 0.7754522; + nodes_isovar[2][0] = -0.28731; + nodes_isovar[2][1] = 0.711750; + nodes_isovar[2][2] = -0.5794; + CDFEM_Parent_Edge edge({0.0,0.5,1.0}, nodes_isovar); + EXPECT_TRUE(edge.have_any_crossings()); + EXPECT_FALSE(edge.have_crossing(iface01)); + EXPECT_TRUE(edge.have_crossing(iface12)); + EXPECT_FALSE(edge.have_crossing(iface02)); + } + + // Test edge that goes from phase 1-0-2 in the piecewise approximation, with different locations than those + // given by a simple linear approximation. + { + nodes_isovar[0][0] = 0.25; + nodes_isovar[0][1] = 1.0; + nodes_isovar[0][2] = 0.0; + nodes_isovar[1][0] = 0.25; + nodes_isovar[1][1] = 1.0; + nodes_isovar[1][2] = 0.0; + nodes_isovar[2][0] = 0.25; + nodes_isovar[2][1] = 0.0; + nodes_isovar[2][2] = 1.0; + CDFEM_Parent_Edge edge({0.0,0.5,1.0}, nodes_isovar); + EXPECT_TRUE(edge.have_any_crossings()); + EXPECT_TRUE(edge.have_crossing(iface01)); + EXPECT_FALSE(edge.have_crossing(iface12)); + EXPECT_TRUE(edge.have_crossing(iface02)); + EXPECT_DOUBLE_EQ(0.625, edge.get_crossing_position(iface02)); + EXPECT_DOUBLE_EQ(0.875, edge.get_crossing_position(iface01)); + } + +} + +TEST(CDFEM_Parent_Edge_Two_LS, Two_Crossings_Same_Edge) +{ + Phase_Support::set_one_levelset_per_phase(true); + + const InterfaceID iface01(0,1); + + std::vector > nodes_isovar(3); + nodes_isovar[0].resize(2); + nodes_isovar[1].resize(2); + nodes_isovar[2].resize(2); + + // Interface (0,1) has 2 crossings, one between each parent and the mid node. + // We will treat this as an uncrossed edge regardless of snapping. + nodes_isovar[0][0] = 0.02; + nodes_isovar[0][1] = 0.01; + nodes_isovar[1][0] = 0.; + nodes_isovar[1][1] = 0.01; + nodes_isovar[2][0] = 0.02; + nodes_isovar[2][1] = 0.01; + + // No snapping + { + CDFEM_Parent_Edge edge({0.0,0.5,1.0}, nodes_isovar); + EXPECT_FALSE(edge.have_any_crossings()); + EXPECT_FALSE(edge.have_crossing(iface01)); + } +} + +void expect_all_edge_segments_have_finite_or_zero_length(const CDFEM_Parent_Edge & edge, const double snapTol) +{ + std::vector crossingLocations; + for (auto && crossing : edge.get_crossings()) + { + crossingLocations.push_back(crossing.second); + } + std::sort(crossingLocations.begin(), crossingLocations.end()); + double previousCrossingLocation = 0.0; + for (auto && crossingLocation : crossingLocations) + { + const double intervalSize = crossingLocation- previousCrossingLocation; + EXPECT_TRUE(intervalSize == 0.0 || intervalSize >= snapTol) << "Found infinitesmal interval " << intervalSize << " on edge " << edge; + previousCrossingLocation = crossingLocation; + } + const double lastIntervalSize = 1.0 - previousCrossingLocation; + EXPECT_TRUE(lastIntervalSize == 0.0 || lastIntervalSize >= snapTol) << "Found infinitesmal interval " << lastIntervalSize << " on edge " << edge; +} + +TEST(CDFEM_Parent_Edge_Two_LS, LSPerPhaseInfinitesimalSnapTo0) +{ + Phase_Support::set_one_levelset_per_phase(true); + + const std::vector > nodesIsovar = {{-CDFEM_Parent_Edge::MinSize(),0.0},{1.0,0.0}}; + CDFEM_Parent_Edge edge(nodesIsovar); + EXPECT_TRUE(edge.have_any_crossings()); + + expect_all_edge_segments_have_finite_or_zero_length(edge, CDFEM_Parent_Edge::MinSize()); +} + +TEST(CDFEM_Parent_Edge_Two_LS, LSPerPhaseInfinitesimalSnapTo1) +{ + Phase_Support::set_one_levelset_per_phase(true); + + const std::vector > nodesIsovar = {{1.0,0.0},{-CDFEM_Parent_Edge::MinSize(),0.0}}; + CDFEM_Parent_Edge edge(nodesIsovar); + EXPECT_TRUE(edge.have_any_crossings()); + + expect_all_edge_segments_have_finite_or_zero_length(edge, CDFEM_Parent_Edge::MinSize()); +} + +void expect_all_fake_crossings_are_really_fake(const CDFEM_Parent_Edge & edge) +{ + EXPECT_TRUE(edge.all_fake_crossings_are_really_fake()); +} + +TEST(CDFEM_Parent_Edge_Three_LS, LSPerPhaseInfinitesimalSnapInMiddle) +{ + Phase_Support::set_one_levelset_per_phase(true); + + const std::vector > nodesIsovar = {{-1.0,1.+CDFEM_Parent_Edge::MinSize(),0.0},{1.+CDFEM_Parent_Edge::MinSize(),-1.0,0.0}}; + CDFEM_Parent_Edge edge(nodesIsovar); + EXPECT_EQ(1u,edge.get_crossings().size()); + + expect_all_edge_segments_have_finite_or_zero_length(edge, CDFEM_Parent_Edge::MinSize()); + expect_all_fake_crossings_are_really_fake(edge); +} + +TEST(CDFEM_Parent_Edge_Three_LS, LSPerPhaseTieInMiddle) +{ + Phase_Support::set_one_levelset_per_phase(true); + + const std::vector > nodesIsovar = {{0.,-1.,1.},{0.,1.,-1.}}; + CDFEM_Parent_Edge edge(nodesIsovar); + EXPECT_EQ(1u,edge.get_crossings().size()); + + expect_all_edge_segments_have_finite_or_zero_length(edge, CDFEM_Parent_Edge::MinSize()); + expect_all_fake_crossings_are_really_fake(edge); +} + +TEST(CDFEM_Parent_Edge_Three_LS, UnderflowAtEndStillResultsInCorrectFakeCrossing) +{ + Phase_Support::set_one_levelset_per_phase(true); + + const std::vector > nodesIsovar = {{ -0.6, -0.6, 0.6 },{ -6e-16, -5.9e-16, -7e-16 }}; + CDFEM_Parent_Edge edge(nodesIsovar); + + expect_all_edge_segments_have_finite_or_zero_length(edge, CDFEM_Parent_Edge::MinSize()); + expect_all_fake_crossings_are_really_fake(edge); +} + +TEST(CDFEM_Parent_Edge_Three_LS, WithScaling_UnderflowAtEndStillResultsInCorrectFakeCrossing) +{ + Phase_Support::set_one_levelset_per_phase(true); + + const std::vector > nodesIsovar = {{ -0.6e8, -0.6e8, 0.6e8 },{ -6e-8, -5.9e-8, -7e-8 }}; + CDFEM_Parent_Edge edge(nodesIsovar); + + expect_all_edge_segments_have_finite_or_zero_length(edge, CDFEM_Parent_Edge::MinSize()); + expect_all_fake_crossings_are_really_fake(edge); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +TEST(CDFEM_Parent_Edge_Two_LS, SnapTo0) +{ + const double snapTol = 0.1; + Phase_Support::set_one_levelset_per_phase(true); + + const std::vector > nodesIsovar = {{-0.01,0.0},{1.0,0.0}}; + CDFEM_Parent_Edge edge(nodesIsovar); + EXPECT_TRUE(edge.have_any_crossings()); + + edge.collapse_small_segments_while_preserving_topology(snapTol); + expect_all_edge_segments_have_finite_or_zero_length(edge, snapTol); +} + +TEST(CDFEM_Parent_Edge_Two_LS, SnapTo1) +{ + const double snapTol = 0.1; + Phase_Support::set_one_levelset_per_phase(true); + + const std::vector > nodesIsovar = {{1.0,0.0},{-0.01,0.0}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.collapse_small_segments_while_preserving_topology(snapTol); + expect_all_edge_segments_have_finite_or_zero_length(edge, snapTol); +} + +TEST(CDFEM_Parent_Edge_Three_LS, SnapInMiddle) +{ + const double snapTol = 0.1; + Phase_Support::set_one_levelset_per_phase(true); + + const std::vector > nodesIsovar = {{-1.0,1.1,0.0},{1.1,-1.0,0.0}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.collapse_small_segments_while_preserving_topology(snapTol); + expect_all_edge_segments_have_finite_or_zero_length(edge, snapTol); +} + +TEST(CDFEM_Parent_Edge_Three_LS, OneSnapInMiddleMakesOtherSnapUnnecessary) +{ + const double snapTol = 0.1; + Phase_Support::set_one_levelset_per_phase(true); + std::vector locs = {0.4,0.44,0.53}; + const double phi0At0 = -1.0; + const double phi0At1 = (0.-(1.-locs[0])*phi0At0)/locs[0]; + const double phi1At1 = -0.1; + const double phi1At0 = (0.-locs[1]*phi1At1)/(1.-locs[1]); + const double phi1AtLocs2 = (1.-locs[2])*phi1At0 + locs[2]*phi1At1; + const double phi2At1 = -1.0; + const double phi2At0 = (phi1AtLocs2-locs[2]*phi2At1)/(1.-locs[2]); + + const std::vector > nodesIsovar = {{phi0At0, phi1At0, phi2At0, 0.0}, {phi0At1, phi1At1, phi2At1, 0.0}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.collapse_small_segments_while_preserving_topology(snapTol); + expect_all_edge_segments_have_finite_or_zero_length(edge, snapTol); + + const std::vector > oppositeNodesIsovar = {{phi0At1, phi1At1, phi2At1, 0.0}, {phi0At0, phi1At0, phi2At0, 0.0}}; + CDFEM_Parent_Edge oppositeEdge(oppositeNodesIsovar); + + oppositeEdge.collapse_small_segments_while_preserving_topology(snapTol); + expect_all_edge_segments_have_finite_or_zero_length(oppositeEdge, snapTol); + + for (auto && crossing : edge.get_crossings()) + EXPECT_DOUBLE_EQ(crossing.second, 1.-oppositeEdge.get_crossing_position(crossing.first)) << "Snapping opposite edges give different results " << edge << " compared to " << oppositeEdge; +} + +void expect_crossing_at_position_with_sign(const CDFEM_Parent_Edge & edge, const InterfaceID & iface, const double pos, const int sign) +{ + EXPECT_TRUE(edge.have_crossing(iface)); + EXPECT_EQ(pos, edge.get_crossing_position(iface)); + EXPECT_EQ(sign, edge.get_crossing_sign(iface)); +} + +void expect_crossing_at_position_with_sign(const CDFEM_Parent_Edge & edge, const int ls, const double pos, const int sign) +{ + const InterfaceID iface(ls,ls); + EXPECT_TRUE(edge.have_crossing(iface)); + EXPECT_EQ(pos, edge.get_crossing_position(iface)); + EXPECT_EQ(sign, edge.get_crossing_sign(iface)); +} + +void expect_fake_crossing_at_position_with_sign(const CDFEM_Parent_Edge & edge, const InterfaceID & iface, const double pos, const int sign) +{ + const auto result = edge.get_crossing_position_and_sign(iface); + EXPECT_LE(0, std::get<0>(result)) << "Did not find expected fake crossing " << iface << " on edge " << edge; + EXPECT_EQ(pos, std::get<0>(result)); + EXPECT_EQ(sign, std::get<1>(result)); + EXPECT_TRUE(std::get<2>(result)) << "Found real crossing when fake one was expected for " << iface << " on edge " << edge; +} + +TEST(CDFEM_Parent_Edge_Snapping_One_LS, nodeDomainsAt0MovesNegCrossingTo0) +{ + Phase_Support::set_one_levelset_per_phase(false); + const std::vector > nodesIsovar = {{1}, {-1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({0}, {}); + + expect_crossing_at_position_with_sign(edge, 0, 0., -1); +} + +TEST(CDFEM_Parent_Edge_Snapping_One_LS, nodeDomainsAt0MovesPosCrossingTo0) +{ + Phase_Support::set_one_levelset_per_phase(false); + const std::vector > nodesIsovar = {{-1}, {1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({0}, {}); + + expect_crossing_at_position_with_sign(edge, 0, 0., 1); +} + +TEST(CDFEM_Parent_Edge_Snapping_One_LS, nodeDomainsAt1MovesNegCrossingTo1) +{ + Phase_Support::set_one_levelset_per_phase(false); + const std::vector > nodesIsovar = {{1}, {-1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({}, {0}); + + expect_crossing_at_position_with_sign(edge, 0, 1., -1); +} + +TEST(CDFEM_Parent_Edge_Snapping_One_LS, nodeDomainsAt1MovesPosCrossingTo1) +{ + Phase_Support::set_one_levelset_per_phase(false); + const std::vector > nodesIsovar = {{-1}, {1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({}, {0}); + + expect_crossing_at_position_with_sign(edge, 0, 1., 1); +} + +TEST(CDFEM_Parent_Edge_Snapping_One_LS, nodeDomainsAtBothEndsMovesNegCrossingToClosestAt0) +{ + Phase_Support::set_one_levelset_per_phase(false); + const std::vector > nodesIsovar = {{0.9}, {-1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({0}, {0}); + + expect_crossing_at_position_with_sign(edge, 0, 0., -1); +} + +TEST(CDFEM_Parent_Edge_Snapping_One_LS, nodeDomainsAtBothEndsMovesNegCrossingToClosestAt1) +{ + Phase_Support::set_one_levelset_per_phase(false); + const std::vector > nodesIsovar = {{1}, {-0.9}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({0}, {0}); + + expect_crossing_at_position_with_sign(edge, 0, 1., -1); +} + +TEST(CDFEM_Parent_Edge_Snapping_One_LS, nodeDomainsAtBothEndsMovesPosCrossingToClosestAt0) +{ + Phase_Support::set_one_levelset_per_phase(false); + const std::vector > nodesIsovar = {{-0.9}, {1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({0}, {0}); + + expect_crossing_at_position_with_sign(edge, 0, 0., 1); +} + +TEST(CDFEM_Parent_Edge_Snapping_Two_LS_Per_Phase, nodeDomainsAt0MovesNegCrossingTo0) +{ + Phase_Support::set_one_levelset_per_phase(true); + const std::vector > nodesIsovar = {{1,-1}, {-1,1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({0,1}, {}); + + expect_crossing_at_position_with_sign(edge, InterfaceID(0,1), 0., -1); +} + +TEST(CDFEM_Parent_Edge_Snapping_Two_LS_Per_Phase, nodeDomainsAt0MovesPosCrossingTo0) +{ + Phase_Support::set_one_levelset_per_phase(true); + const std::vector > nodesIsovar = {{-1,1}, {1,-1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({0,1}, {}); + + expect_crossing_at_position_with_sign(edge, InterfaceID(0,1), 0., 1); +} + +TEST(CDFEM_Parent_Edge_Snapping_Two_LS_Per_Phase, nodeDomainsAt1MovesNegCrossingTo1) +{ + Phase_Support::set_one_levelset_per_phase(true); + const std::vector > nodesIsovar = {{1,-1}, {-1,1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({}, {0,1}); + + expect_crossing_at_position_with_sign(edge, InterfaceID(0,1), 1., -1); +} + +TEST(CDFEM_Parent_Edge_Snapping_Two_LS_Per_Phase, nodeDomainsAt1MovesPosCrossingTo1) +{ + Phase_Support::set_one_levelset_per_phase(true); + const std::vector > nodesIsovar = {{-1,1}, {1,-1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({}, {0,1}); + + expect_crossing_at_position_with_sign(edge, InterfaceID(0,1), 1., 1); +} + +TEST(CDFEM_Parent_Edge_Snapping_Two_LS_Per_Phase, nodeDomainsAtBothEndsMovesNegCrossingToClosestAt0) +{ + Phase_Support::set_one_levelset_per_phase(true); + const std::vector > nodesIsovar = {{0.9,-0.9}, {-1,1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({0,1}, {0,1}); + + expect_crossing_at_position_with_sign(edge, InterfaceID(0,1), 0., -1); +} + +TEST(CDFEM_Parent_Edge_Snapping_Two_LS_Per_Phase, nodeDomainsAtBothEndsMovesNegCrossingToClosestAt1) +{ + Phase_Support::set_one_levelset_per_phase(true); + const std::vector > nodesIsovar = {{1,-1}, {-0.9,0.9}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({0,1}, {0,1}); + + expect_crossing_at_position_with_sign(edge, InterfaceID(0,1), 1., -1); +} + +TEST(CDFEM_Parent_Edge_Snapping_Two_LS_Per_Phase, nodeDomainsAtBothEndsMovesPosCrossingToClosestAt0) +{ + Phase_Support::set_one_levelset_per_phase(true); + const std::vector > nodesIsovar = {{-0.9,0.9}, {1,-1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({0,1}, {0,1}); + + expect_crossing_at_position_with_sign(edge, InterfaceID(0,1), 0., 1); +} + +TEST(CDFEM_Parent_Edge_Snapping_Three_LS_Per_Phase, nodeDomainsMoveAllCrossingsTo0) +{ + Phase_Support::set_one_levelset_per_phase(true); + const std::vector > nodesIsovar = {{-1,0,2}, {2,0,-1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({0,1,2}, {}); + + expect_fake_crossing_at_position_with_sign(edge, InterfaceID(0,1), 0., 1); + expect_fake_crossing_at_position_with_sign(edge, InterfaceID(1,2), 0., 1); + expect_crossing_at_position_with_sign(edge, InterfaceID(0,2), 0., 1); +} + +TEST(CDFEM_Parent_Edge_Snapping_Three_LS_Per_Phase, nodeDomainsWithMiddlePhase2MoveAllCrossingsTo0) +{ + Phase_Support::set_one_levelset_per_phase(true); + const std::vector > nodesIsovar = {{-1,2,0}, {2,-1,0}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({0,1,2}, {}); + + expect_fake_crossing_at_position_with_sign(edge, InterfaceID(0,2), 0., 1); + expect_fake_crossing_at_position_with_sign(edge, InterfaceID(1,2), 0., -1); + expect_crossing_at_position_with_sign(edge, InterfaceID(0,1), 0., 1); +} + + +TEST(CDFEM_Parent_Edge_Snapping_Three_LS_Per_Phase, nodeDomainsCenterPhaseMoveAllCrossingsTo0) +{ + Phase_Support::set_one_levelset_per_phase(true); + const std::vector > nodesIsovar = {{-1,0,2}, {2,0,-1}}; + CDFEM_Parent_Edge edge(nodesIsovar); + + edge.adjust_crossing_locations_based_on_node_captured_domains({0,2}, {}); + + expect_fake_crossing_at_position_with_sign(edge, InterfaceID(0,1), 0., 1); + expect_crossing_at_position_with_sign(edge, InterfaceID(0,2), 0., 1); +} + +} // namespace krino diff --git a/packages/krino/krino/unit_tests/Akri_Unit_CDMesh.cpp b/packages/krino/krino/unit_tests/Akri_Unit_CDMesh.cpp new file mode 100644 index 000000000000..efff1945a32a --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_CDMesh.cpp @@ -0,0 +1,3092 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace krino { + +class LevelSet; + +namespace { + template +void build_one_tet4_mesh(DECOMP_FIXTURE & fixture, + stk::mesh::Part & elem_part, + const int parallel_rank, const int parallel_size) +{ + stk::mesh::BulkData & mesh = fixture.fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + ASSERT_TRUE(parallel_size == 1 || parallel_size == 2); + ThrowRequire(elem_part.topology() == stk::topology::TETRAHEDRON_4); + ASSERT_EQ(stk::topology::ELEMENT_RANK, elem_part.primary_entity_rank()); + + mesh.modification_begin(); + { + stk::mesh::PartVector elem_parts(2); + elem_parts[1] = &aux_meta.active_part(); + + std::vector elem_nodes = {1, 2, 3, 4}; + + stk::mesh::Entity element; + if(parallel_rank == 0) + { + elem_parts[0] = &elem_part; + element = fixture.create_element(elem_parts, 1, elem_nodes); + } + } + mesh.modification_end(); + + if(parallel_rank == 0) + { + EXPECT_EQ(1u, stk::mesh::count_selected_entities(meta.universal_part(), mesh.buckets(stk::topology::ELEMENT_RANK))); + + const auto node1 = mesh.get_entity(stk::topology::NODE_RANK, 1); + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + const auto node3 = mesh.get_entity(stk::topology::NODE_RANK, 3); + const auto node4 = mesh.get_entity(stk::topology::NODE_RANK, 4); + + ASSERT_TRUE(mesh.is_valid(node1)); + ASSERT_TRUE(mesh.is_valid(node2)); + ASSERT_TRUE(mesh.is_valid(node3)); + ASSERT_TRUE(mesh.is_valid(node4)); + + double * node1_coords = field_data(fixture.coord_field, node1); + double * node2_coords = field_data(fixture.coord_field, node2); + double * node3_coords = field_data(fixture.coord_field, node3); + double * node4_coords = field_data(fixture.coord_field, node4); + + node1_coords[0] = 1.; + node1_coords[1] = 1.; + node1_coords[2] = 1.; + + node2_coords[0] = -1.; + node2_coords[1] = 1.; + node2_coords[2] = -1.; + + node3_coords[0] = 1.; + node3_coords[1] = -1.; + node3_coords[2] = -1.; + + node4_coords[0] = -1.; + node4_coords[1] = -1.; + node4_coords[2] = 1.; + } +} + +template +void build_two_tet4_mesh_np2(DECOMP_FIXTURE & fixture, + stk::mesh::Part & elem1_part, stk::mesh::Part & elem2_part, stk::mesh::Part & surface_part, + const int parallel_rank, const int parallel_size, const bool add_side = true, + const bool build_all_on_P0 = false) +{ + stk::mesh::BulkData & mesh = fixture.fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + ASSERT_TRUE(parallel_size == 1 || parallel_size == 2); + ThrowRequire(elem1_part.topology() == stk::topology::TETRAHEDRON_4); + ASSERT_EQ(meta.side_rank(), surface_part.primary_entity_rank()); + ASSERT_EQ(stk::topology::ELEMENT_RANK, elem1_part.primary_entity_rank()); + ASSERT_EQ(stk::topology::ELEMENT_RANK, elem2_part.primary_entity_rank()); + + stk::mesh::Entity sideEntity; + mesh.modification_begin(); + { + stk::mesh::PartVector elem_parts(2); + elem_parts[1] = &aux_meta.active_part(); + + std::vector elem1_nodes = {1, 3, 2, 4}; + std::vector elem2_nodes = {1, 2, 3, 5}; + + stk::mesh::Entity element; + if(parallel_rank == 0) + { + elem_parts[0] = &elem1_part; + element = fixture.create_element(elem_parts, 1, elem1_nodes); + } + if((parallel_rank == 1 && !build_all_on_P0) || (parallel_rank == 0 && build_all_on_P0) || + parallel_size == 1) + { + elem_parts[0] = &elem2_part; + element = fixture.create_element(elem_parts, 2, elem2_nodes); + } + + if(parallel_size > 1 && !build_all_on_P0) + { + const int opp_rank = parallel_rank == 0 ? 1 : 0; + for(auto i=1; i <= 3; ++i) + { + const auto node = mesh.get_entity(stk::topology::NODE_RANK, i); + mesh.add_node_sharing(node, opp_rank); + } + } + + if(add_side && (parallel_rank == 0 || !build_all_on_P0)) + { + sideEntity = mesh.declare_solo_side(7, {&surface_part}); + for(auto i=1; i <= 3; ++i) + { + auto side_node = mesh.get_entity(stk::topology::NODE_RANK, i); + mesh.declare_relation(sideEntity, side_node, i-1); + } + attach_entity_to_elements(mesh, sideEntity); + } + } + mesh.modification_end(); + + if(parallel_rank == 0 || !build_all_on_P0) + { + EXPECT_EQ(2u, stk::mesh::count_selected_entities(meta.universal_part(), mesh.buckets(stk::topology::ELEMENT_RANK))); + if(add_side) + { + EXPECT_EQ(1u, stk::mesh::count_selected_entities(meta.universal_part(), mesh.buckets(meta.side_rank()))); + EXPECT_EQ(1u, stk::mesh::count_selected_entities(surface_part, mesh.buckets(meta.side_rank()))); + + EXPECT_EQ(2u, mesh.num_connectivity(sideEntity, stk::topology::ELEMENT_RANK)); + } + + const auto node1 = mesh.get_entity(stk::topology::NODE_RANK, 1); + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + const auto node3 = mesh.get_entity(stk::topology::NODE_RANK, 3); + const auto node4 = mesh.get_entity(stk::topology::NODE_RANK, 4); + const auto node5 = mesh.get_entity(stk::topology::NODE_RANK, 5); + + ASSERT_TRUE(mesh.is_valid(node1)); + ASSERT_TRUE(mesh.is_valid(node2)); + ASSERT_TRUE(mesh.is_valid(node3)); + ASSERT_TRUE(mesh.is_valid(node4)); + ASSERT_TRUE(mesh.is_valid(node5)); + + double * node1_coords = field_data(fixture.coord_field, node1); + double * node2_coords = field_data(fixture.coord_field, node2); + double * node3_coords = field_data(fixture.coord_field, node3); + double * node4_coords = field_data(fixture.coord_field, node4); + double * node5_coords = field_data(fixture.coord_field, node5); + + node1_coords[0] = 0.; + node1_coords[1] = 0.; + node1_coords[2] = 0.; + + node2_coords[0] = 0.; + node2_coords[1] = 0.; + node2_coords[2] = 1.; + + node3_coords[0] = 0.; + node3_coords[1] = 1.; + node3_coords[2] = 0.; + + node4_coords[0] = -1.; + node4_coords[1] = 0.; + node4_coords[2] = 0.; + + node5_coords[0] = 1.; + node5_coords[1] = 0.; + node5_coords[2] = 0.; + } +} + +template +void build_two_tri3_mesh_np2(DECOMP_FIXTURE & fixture, + stk::mesh::Part & elem1_part, stk::mesh::Part & elem2_part, stk::mesh::Part & surface_part, + const int parallel_rank, const int parallel_size, const bool add_side = true, + const bool build_all_on_P0 = false) +{ + stk::mesh::BulkData & mesh = fixture.fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + ASSERT_TRUE(parallel_size == 1 || parallel_size == 2); + ThrowRequire(elem1_part.topology() == stk::topology::TRIANGLE_3_2D); + ASSERT_EQ(meta.side_rank(), surface_part.primary_entity_rank()); + ASSERT_EQ(stk::topology::ELEMENT_RANK, elem1_part.primary_entity_rank()); + ASSERT_EQ(stk::topology::ELEMENT_RANK, elem2_part.primary_entity_rank()); + + stk::mesh::Entity sideEntity; + mesh.modification_begin(); + { + stk::mesh::PartVector elem_parts(2); + elem_parts[1] = &aux_meta.active_part(); + + std::vector elem1_nodes = {2, 1, 3}; + std::vector elem2_nodes = {1, 2, 4}; + + stk::mesh::Entity element; + if(parallel_rank == 0) + { + elem_parts[0] = &elem1_part; + element = fixture.create_element(elem_parts, 1, elem1_nodes); + } + if((parallel_rank == 1 && !build_all_on_P0) || (parallel_rank == 0 && build_all_on_P0) || + parallel_size == 1) + { + elem_parts[0] = &elem2_part; + element = fixture.create_element(elem_parts, 2, elem2_nodes); + } + + if(parallel_size > 1 && !build_all_on_P0) + { + const int opp_rank = parallel_rank == 0 ? 1 : 0; + for(auto i=1; i <= 2; ++i) + { + const auto node = mesh.get_entity(stk::topology::NODE_RANK, i); + mesh.add_node_sharing(node, opp_rank); + } + } + + if(add_side && (parallel_rank == 0 || !build_all_on_P0)) + { + sideEntity = mesh.declare_solo_side(7, {&surface_part}); + for(auto i=1; i <= 2; ++i) + { + auto side_node = mesh.get_entity(stk::topology::NODE_RANK, i); + mesh.declare_relation(sideEntity, side_node, i-1); + } + attach_entity_to_elements(mesh, sideEntity); + } + } + mesh.modification_end(); + + if(parallel_rank == 0 || !build_all_on_P0) + { + EXPECT_EQ(2u, stk::mesh::count_selected_entities(meta.universal_part(), mesh.buckets(stk::topology::ELEMENT_RANK))); + if(add_side) + { + EXPECT_EQ(1u, stk::mesh::count_selected_entities(meta.universal_part(), mesh.buckets(meta.side_rank()))); + EXPECT_EQ(1u, stk::mesh::count_selected_entities(surface_part, mesh.buckets(meta.side_rank()))); + + EXPECT_EQ(2u, mesh.num_connectivity(sideEntity, stk::topology::ELEMENT_RANK)); + } + + const auto node1 = mesh.get_entity(stk::topology::NODE_RANK, 1); + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + const auto node3 = mesh.get_entity(stk::topology::NODE_RANK, 3); + const auto node4 = mesh.get_entity(stk::topology::NODE_RANK, 4); + + ASSERT_TRUE(mesh.is_valid(node1)); + ASSERT_TRUE(mesh.is_valid(node2)); + ASSERT_TRUE(mesh.is_valid(node3)); + ASSERT_TRUE(mesh.is_valid(node4)); + + double * node1_coords = field_data(fixture.coord_field, node1); + double * node2_coords = field_data(fixture.coord_field, node2); + double * node3_coords = field_data(fixture.coord_field, node3); + double * node4_coords = field_data(fixture.coord_field, node4); + + node1_coords[0] = 0.; + node1_coords[1] = 0.; + + node2_coords[0] = 0.; + node2_coords[1] = 1.; + + node3_coords[0] = 1.; + node3_coords[1] = 0.; + + node4_coords[0] = -1.; + node4_coords[1] = 0.; + } +} +} + +struct SingleLSPolicy +{ + void setup_ls_field(const bool is_death, stk::mesh::MetaData & meta, CDFEM_Support & cdfem_support) + { + AuxMetaData & aux_meta = AuxMetaData::get(meta); + Phase_Support::set_one_levelset_per_phase(false); + ls_isovar = aux_meta.declare_field("LS", FieldType::REAL, stk::topology::NODE_RANK, 1u); + cdfem_support.add_interpolation_field(ls_isovar); + + LevelSet * ls_ptr = nullptr; + ls_field = std::make_shared("LS", LevelSet_Identifier(0), ls_isovar, 0., ls_ptr); + if(is_death) + { + death_spec = std::unique_ptr(new CDFEM_Inequality_Spec("death_spec")); + } + cdfem_support.add_ls_field(*ls_field, death_spec.get()); + } + + void register_ls_on_blocks(const stk::mesh::PartVector & blocks, stk::mesh::MetaData & meta, + Block_Surface_Connectivity & block_surface_info, const bool register_fields) + { + AuxMetaData & aux_meta = AuxMetaData::get(meta); + PhaseTag p, n; + const LevelSet_Identifier id0(0); + p.add(id0, 1); + n.add(id0, -1); + PhaseVec named_phases{{"A", p}, {"B", n}}; + + if(death_spec) + { + death_spec->set_phases(p, n); + } + + Phase_Support & phase_support = Phase_Support::get(meta); + phase_support.set_input_block_surface_connectivity(block_surface_info); + LevelSet * ls_ptr = nullptr; + phase_support.register_blocks_for_level_set(ls_ptr, blocks); + phase_support.decompose_blocks(blocks, named_phases, LS_Name_Generator()); + + for(auto && b : blocks) + { + auto conformal_A = phase_support.find_conformal_io_part(*b, p); + auto conformal_B = phase_support.find_conformal_io_part(*b, n); + ThrowRequire(conformal_A != nullptr); + ThrowRequire(conformal_B != nullptr); + if(register_fields) + { + aux_meta.register_field("LS", FieldType::REAL, stk::topology::NODE_RANK, 1u, 1u, *b); + aux_meta.register_field("LS", FieldType::REAL, stk::topology::NODE_RANK, 1u, 1u, *conformal_A); + aux_meta.register_field("LS", FieldType::REAL, stk::topology::NODE_RANK, 1u, 1u, *conformal_B); + } + } + } + + FieldRef ls_isovar; + std::shared_ptr ls_field; + std::unique_ptr death_spec; +}; + +template +struct LSPerPhasePolicy +{ + void setup_ls_field(const bool is_death, stk::mesh::MetaData & meta, CDFEM_Support & cdfem_support) + { + AuxMetaData & aux_meta = AuxMetaData::get(meta); + Phase_Support::set_one_levelset_per_phase(true); + ThrowRequire(!is_death); + for(unsigned i=0; i < NUM_LS; ++i) + { + const std::string isovar_name = "LS" + std::to_string(i); + FieldRef ls_isovar = aux_meta.declare_field(isovar_name, FieldType::REAL, stk::topology::NODE_RANK, 1u); + ls_isovars.push_back(ls_isovar); + cdfem_support.add_interpolation_field(ls_isovar); + + LevelSet * ls_ptr = nullptr; + auto ls_field = std::make_shared(isovar_name, LevelSet_Identifier(i), ls_isovar, 0., ls_ptr); + ls_fields.push_back(ls_field); + cdfem_support.add_ls_field(*ls_field, nullptr); + } + } + + void register_ls_on_blocks(const stk::mesh::PartVector & blocks, stk::mesh::MetaData & meta, + Block_Surface_Connectivity & block_surface_info, const bool register_fields) + { + AuxMetaData & aux_meta = AuxMetaData::get(meta); + PhaseVec named_phases; + for(unsigned ls=0; ls < NUM_LS; ++ls) + { + PhaseTag tag; + tag.add(LevelSet_Identifier(ls), -1); + named_phases.push_back(NamedPhase("P" + std::to_string(ls), tag)); + } + + Phase_Support & phase_support = Phase_Support::get(meta); + phase_support.set_input_block_surface_connectivity(block_surface_info); + LevelSet * ls_ptr = nullptr; + phase_support.register_blocks_for_level_set(ls_ptr, blocks); + phase_support.decompose_blocks(blocks, named_phases, LS_Name_Generator()); + + for(auto && b : blocks) + { + for( unsigned ls=0; ls < NUM_LS; ++ls ) + { + auto conformal_part = phase_support.find_conformal_io_part(*b, named_phases[ls].tag()); + ThrowRequire(conformal_part != nullptr); + if(register_fields) + { + // Need to register every LS on every conformal part + for( unsigned ls2=0; ls2 < NUM_LS; ++ls2 ) + { + aux_meta.register_field(ls_isovars[ls2].name(), FieldType::REAL, stk::topology::NODE_RANK, 1u, 1u, *conformal_part); + } + aux_meta.register_field(ls_isovars[ls].name(), FieldType::REAL, stk::topology::NODE_RANK, 1u, 1u, *b); + } + } + } + } + + std::vector ls_isovars; + std::vector > ls_fields; +}; + +template +struct LSPerInterfacePolicy +{ + void setup_ls_field(const bool is_death, stk::mesh::MetaData & meta, CDFEM_Support & cdfem_support) + { + AuxMetaData & aux_meta = AuxMetaData::get(meta); + Phase_Support::set_one_levelset_per_phase(false); + ThrowRequire(!is_death); + for(unsigned i=0; i < NUM_LS; ++i) + { + const std::string isovar_name = "LS" + std::to_string(i); + FieldRef ls_isovar = aux_meta.declare_field(isovar_name, FieldType::REAL, stk::topology::NODE_RANK, 1u); + ls_isovars.push_back(ls_isovar); + cdfem_support.add_interpolation_field(ls_isovar); + + LevelSet * ls_ptr = nullptr; + auto ls_field = std::make_shared(isovar_name, LevelSet_Identifier(i), ls_isovar, 0., ls_ptr); + ls_fields.push_back(ls_field); + cdfem_support.add_ls_field(*ls_field, nullptr); + } + } + + void register_ls_on_blocks(const stk::mesh::PartVector & blocks, stk::mesh::MetaData & meta, + Block_Surface_Connectivity & block_surface_info, const bool register_fields) + { + AuxMetaData & aux_meta = AuxMetaData::get(meta); + PhaseVec named_phases; + const unsigned numPhases = 1<>ls)%2 == 0; + const int lsSign = lsIsNeg ? -1 : 1; + tag.add(LevelSet_Identifier(ls), lsSign); + phaseName += (lsIsNeg ? "-" : "+"); + } + named_phases.push_back(NamedPhase(phaseName, tag)); + } + + Phase_Support & phase_support = Phase_Support::get(meta); + phase_support.set_input_block_surface_connectivity(block_surface_info); + LevelSet * ls_ptr = nullptr; + phase_support.register_blocks_for_level_set(ls_ptr, blocks); + phase_support.decompose_blocks(blocks, named_phases, LS_Name_Generator()); + + if(register_fields) + { + for(auto && b : blocks) + { + for( unsigned ls=0; ls < NUM_LS; ++ls ) + { + aux_meta.register_field(ls_isovars[ls].name(), FieldType::REAL, stk::topology::NODE_RANK, 1u, 1u, *b); + + for (unsigned phase=0; phase ls_isovars; + std::vector > ls_fields; +}; + +template +class CompleteDecompositionFixture : public ::testing::Test +{ +public: + CompleteDecompositionFixture() + : fixture(), cdfemSupport(CDFEM_Support::get(fixture.meta_data())) + { + AuxMetaData & aux_meta = AuxMetaData::get(fixture.meta_data()); + auto & vec_type = fixture.meta_data().spatial_dimension() == 2 ? FieldType::VECTOR_2D : FieldType::VECTOR_3D; + coord_field = aux_meta.register_field("coordinates", vec_type, stk::topology::NODE_RANK, 1u, 1u, fixture.meta_data().universal_part()); + cdfemSupport.set_coords_field(coord_field); + cdfemSupport.add_interpolation_field(coord_field); + cdfemSupport.register_parent_node_ids_field(); + + cdfemSupport.set_prolongation_model(INTERPOLATION); + } + + void setup_ls_field(const bool is_death = false) + { + ls_policy.setup_ls_field(is_death, fixture.meta_data(), cdfemSupport); + } + + void register_ls_on_blocks(const stk::mesh::PartVector & blocks, const bool register_fields = true) + { + ls_policy.register_ls_on_blocks(blocks, fixture.meta_data(), block_surface_info, register_fields); + } + + stk::mesh::Part & declare_input_block(const std::string & name, const stk::topology topo) + { + auto & block_part = fixture.meta_data().declare_part_with_topology(name, topo); + stk::io::put_io_part_attribute(block_part); + return block_part; + } + + stk::mesh::Part & declare_input_surface(const std::string & name, const stk::topology topo, const std::set & touching_blocks) + { + auto & surface_part = fixture.meta_data().declare_part_with_topology(name, topo); + stk::io::put_io_part_attribute(surface_part); + + block_surface_info.add_surface(surface_part.mesh_meta_data_ordinal(), touching_blocks); + return surface_part; + } + + stk::mesh::PartVector declare_input_surfaces_touching_block(const unsigned numSurfaces, const stk::mesh::Part & touchingBlock) + { + const stk::topology topo = touchingBlock.topology().side_topology(); + stk::mesh::PartVector surfaces; + for (unsigned i=0; iget_active_part(), cdfemSupport, Phase_Support::get(fixture.meta_data())); + if (cdfemSupport.get_cdfem_edge_degeneracy_handling() == SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE) + nodesToSnappedDomains = snap_as_much_as_possible_while_maintaining_quality(krino_mesh->stk_bulk(), krino_mesh->get_active_part(), cdfemSupport.get_interpolation_fields(), interfaceGeometry, cdfemSupport.get_global_ids_are_parallel_consistent()); + interfaceGeometry.prepare_to_process_elements(krino_mesh->stk_bulk(), nodesToSnappedDomains); + + if(!krino_mesh->my_old_mesh) + { + krino_mesh->my_old_mesh = std::make_shared(fixture.bulk_data(), std::shared_ptr()); + krino_mesh->my_old_mesh->generate_nonconformal_elements(); + } + + krino_mesh->generate_nonconformal_elements(); + if (cdfemSupport.get_cdfem_edge_degeneracy_handling() == SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE) + krino_mesh->snap_nearby_intersections_to_nodes(interfaceGeometry, nodesToSnappedDomains); + krino_mesh->set_phase_of_uncut_elements(interfaceGeometry); + krino_mesh->triangulate(interfaceGeometry); + krino_mesh->my_old_mesh->stash_field_data(-1, *krino_mesh); + + + krino_mesh->decompose(); + krino_mesh->modify_mesh(); + krino_mesh->prolongation(); + + if(krinolog.shouldPrint(LOG_DEBUG)) + { + krino_mesh->debug_output(); + } + } + + void debug_output() + { + krino_mesh->debug_output(); + } + + void commit() + { + krino_mesh = std::make_shared(fixture.bulk_data(), std::shared_ptr()); + } + + stk::mesh::Entity create_element(stk::mesh::PartVector & elem_parts, stk::mesh::EntityId elem_id, + std::vector elem_nodes) + { + auto elem = stk::mesh::declare_element( fixture.bulk_data(), elem_parts, elem_id, elem_nodes ); + { + const stk::mesh::Entity * const nodes = fixture.bulk_data().begin_nodes(elem); + for(unsigned i=0; i < elem_nodes.size(); ++i) + { + EXPECT_EQ(elem_nodes[i], fixture.bulk_data().identifier(nodes[i])); + if (!fixture.bulk_data().bucket(nodes[i]).member(cdfemSupport.get_active_part())) + fixture.bulk_data().change_entity_parts(nodes[i], stk::mesh::ConstPartVector{&cdfemSupport.get_active_part()}, {}); + } + } + return elem; + } + + void run_rebalance_with(const std::string& decomp_method) + { + /* This is a 2 processor test to confirm that we can rebalance a mesh with CDFEM cut elements, + * and then successfully cut the mesh again. We create an initial mesh with 2 tets both owned + * by P0, do a decomposition, rebalance so that 1 parent element should end up on each of P0 and + * P1, and then do a second decomposition. + */ + + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + if (parallel_size != 2) return; + + cdfemSupport.set_cdfem_edge_tol(0.1); + cdfemSupport.set_simplex_generation_method(CUT_QUADS_BY_GLOBAL_IDENTIFIER); + + setup_ls_field(); + + const stk::topology tet4 = stk::topology::TETRAHEDRON_4; + auto & block1_part = declare_input_block("block_1", tet4); + auto & block2_part = declare_input_block("block_2", tet4); + auto & surface_part = declare_input_surface("surface_1", tet4.side_topology(), {block1_part.mesh_meta_data_ordinal(), block2_part.mesh_meta_data_ordinal()}); + + register_ls_on_blocks({&block1_part, &block2_part}); + + FieldRef elem_weight_field = + aux_meta.register_field("element_weight", FieldType::REAL, stk::topology::ELEMENT_RANK, + 1u, 1, fixture.meta_data().universal_part()); + + commit(); + + const bool build_all_on_P0 = true; + build_two_tet4_mesh_np2(*this, block1_part, block2_part, surface_part, parallel_rank, + parallel_size, true, build_all_on_P0); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + if(parallel_rank == 0) + { + auto & node_buckets = mesh.buckets(stk::topology::NODE_RANK); + for(auto && b_ptr : node_buckets) + { + for(auto && node : *b_ptr) + { + const double * coords = field_data(coord_field, node); + double * ls_data = field_data(ls_policy.ls_isovar, node); + if(ls_data) *ls_data = coords[1]-0.5; + } + } + } + stk::mesh::communicate_field_data(mesh, {&ls_policy.ls_isovar.field(), &coord_field.field()}); + + ASSERT_NO_THROW(decompose_mesh()); + + if(parallel_rank == 1) + { + EXPECT_EQ(0u, stk::mesh::get_num_entities(mesh)); + } + + const stk::mesh::Selector parent_selector = krino_mesh->get_parent_part(); + if(parallel_rank == 0) + { + auto & elem_buckets = mesh.buckets(stk::topology::ELEMENT_RANK); + for(auto && b_ptr : elem_buckets) + { + const bool is_parent = parent_selector(*b_ptr); + for(auto && elem : *b_ptr) + { + double * weight = field_data(elem_weight_field, elem); + *weight = is_parent ? 1. : 0.; + } + } + } + + rebalance_utils::rebalance_mesh(mesh, + krino_mesh.get(), + elem_weight_field.name(), + coord_field.name(), + {fixture.meta_data().universal_part()}, + decomp_method); + + // Both procs should now own 1 parent element and 4 children + EXPECT_EQ(1u, stk::mesh::count_selected_entities( + fixture.meta_data().locally_owned_part() & parent_selector, + mesh.buckets(stk::topology::ELEMENT_RANK))); + EXPECT_EQ(4u, stk::mesh::count_selected_entities( + fixture.meta_data().locally_owned_part() & krino_mesh->get_child_part(), + mesh.buckets(stk::topology::ELEMENT_RANK))); + + krino_mesh = std::make_shared(mesh, krino_mesh); + + auto & node_buckets = mesh.buckets(stk::topology::NODE_RANK); + for(auto && b_ptr : node_buckets) + { + for(auto && node : *b_ptr) + { + const double * coords = field_data(coord_field, node); + double * ls_data = field_data(ls_policy.ls_isovar, node); + if(ls_data) *ls_data = coords[2]-0.5; + } + } + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + EXPECT_TRUE(check_induced_parts(mesh)); + EXPECT_TRUE(check_face_and_edge_ownership(mesh)); + EXPECT_TRUE(check_face_and_edge_relations(mesh)); + EXPECT_TRUE(check_shared_entity_nodes(mesh)); + EXPECT_TRUE(krino_mesh->check_element_side_parts()); + } + + MESH_FIXTURE fixture; + FieldRef coord_field; + CDFEM_Support & cdfemSupport; + std::shared_ptr krino_mesh; + Block_Surface_Connectivity block_surface_info; + LS_FIELD_POLICY ls_policy; + LogRedirecter log; +}; + +namespace { +template +void build_two_tri3_mesh_on_one_or_two_procs(CompleteDecompositionFixture & fixture, + stk::mesh::Part & block_part, const int parallel_rank) +{ + stk::mesh::BulkData & mesh = fixture.fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.fixture.meta_data(); + const int parallel_size = mesh.parallel_size(); + mesh.modification_begin(); + { + /* + * 4---3 + * |\ 2| + * | \ | + * |1 \| + * 1---2 + */ + stk::mesh::PartVector elem_parts; + elem_parts.push_back(&block_part); + elem_parts.push_back(&fixture.cdfemSupport.get_active_part()); + + std::vector elem1_nodes = {1, 2, 4}; + std::vector elem2_nodes = {2, 3, 4}; + + if(parallel_rank == 0) fixture.create_element(elem_parts, 1, elem1_nodes); + if(parallel_rank == 1 || parallel_size == 1) fixture.create_element(elem_parts, 2, elem2_nodes); + + if(parallel_size > 1) + { + const int opp_rank = parallel_rank == 0 ? 1 : 0; + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + const auto node4 = mesh.get_entity(stk::topology::NODE_RANK, 4); + mesh.add_node_sharing(node2, opp_rank); + mesh.add_node_sharing(node4, opp_rank); + } + } + mesh.modification_end(); + + EXPECT_EQ(2u, stk::mesh::count_selected_entities(meta.universal_part(), mesh.buckets(stk::topology::ELEMENT_RANK))); + + const auto node1 = mesh.get_entity(stk::topology::NODE_RANK, 1); + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + const auto node3 = mesh.get_entity(stk::topology::NODE_RANK, 3); + const auto node4 = mesh.get_entity(stk::topology::NODE_RANK, 4); + + ASSERT_TRUE(mesh.is_valid(node1)); + ASSERT_TRUE(mesh.is_valid(node2)); + ASSERT_TRUE(mesh.is_valid(node3)); + ASSERT_TRUE(mesh.is_valid(node4)); + + double * node1_coords = field_data(fixture.coord_field, node1); + double * node2_coords = field_data(fixture.coord_field, node2); + double * node3_coords = field_data(fixture.coord_field, node3); + double * node4_coords = field_data(fixture.coord_field, node4); + + node1_coords[0] = 0.; + node1_coords[1] = 0.; + + node2_coords[0] = 1.; + node2_coords[1] = 0.; + + node3_coords[0] = 1.; + node3_coords[1] = 1.; + + node4_coords[0] = 0.; + node4_coords[1] = 1.; +} + +template +void build_two_tri3_mesh_on_one_or_two_procs_with_sides(CompleteDecompositionFixture & fixture, + stk::mesh::Part & blockPart, const stk::mesh::PartVector & sideParts, const int parallel_rank) +{ + build_two_tri3_mesh_on_one_or_two_procs(fixture, blockPart, parallel_rank); + + ThrowRequire(sideParts.size() == 4); + stk::mesh::BulkData & mesh = fixture.fixture.bulk_data(); + const int parallel_size = mesh.parallel_size(); + + mesh.modification_begin(); + if(parallel_rank == 0) + { + const auto element1 = mesh.get_entity(stk::topology::ELEMENT_RANK,1); + mesh.declare_element_side(element1, 0, stk::mesh::PartVector{sideParts[0]}); + mesh.declare_element_side(element1, 2, stk::mesh::PartVector{sideParts[3]}); + } + if(parallel_rank == 1 || parallel_size == 1) + { + const auto element2 = mesh.get_entity(stk::topology::ELEMENT_RANK,2); + mesh.declare_element_side(element2, 0, stk::mesh::PartVector{sideParts[1]}); + mesh.declare_element_side(element2, 1, stk::mesh::PartVector{sideParts[2]}); + } + mesh.modification_end(); +} + +void check_two_tri3_snapped_mesh_np2(CompleteDecompositionFixture & fixture, const int parallel_rank) +{ + stk::mesh::BulkData & mesh = fixture.fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + std::vector entities; + + // Should be no new nodes because of snapped interface, 3 nodes owned by P0, 1 by P1 + mesh.get_entities(stk::topology::NODE_RANK, meta.universal_part(), entities); + EXPECT_EQ(4u, entities.size()); + mesh.get_entities(stk::topology::NODE_RANK, aux_meta.active_part(), entities); + EXPECT_EQ(4u, entities.size()); + mesh.get_entities(stk::topology::NODE_RANK, aux_meta.active_part() & meta.locally_owned_part(), entities); + if(parallel_rank == 0) + { + EXPECT_EQ(3u, entities.size()); + } + else + { + EXPECT_EQ(1u, entities.size()); + } + + // Should be 1 interface edge + mesh.get_entities(stk::topology::EDGE_RANK, aux_meta.active_part() & + aux_meta.get_part("surface_block_1_A_B") & aux_meta.get_part("surface_block_1_B_A"), entities); + EXPECT_EQ(1u, entities.size()); + + // Should be 2 coincident subelements, no parents + mesh.get_entities(stk::topology::ELEMENT_RANK, meta.universal_part(), entities); + EXPECT_EQ(2u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.active_part(), entities); + EXPECT_EQ(2u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, !aux_meta.active_part(), entities); + EXPECT_EQ(0u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_A"), entities); + EXPECT_EQ(1u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_B"), entities); + EXPECT_EQ(1u, entities.size()); +} + +} + +typedef CompleteDecompositionFixture CDMeshTests2D; +TEST_F(CDMeshTests2D, IsovariableNotDefinedOnDecomposedBlock) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + + if (parallel_size > 1) return; + + setup_ls_field(); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}, false); + + commit(); + + EXPECT_ANY_THROW(krino_mesh->check_isovariable_field_existence_on_decomposed_blocks(true)); +} + +TEST_F(CDMeshTests2D, IsovariableNotDefinedOnBlock1) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + + if (parallel_size > 1) return; + + setup_ls_field(); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}, false); + + auto & meta = fixture.meta_data(); + auto & aux_meta = AuxMetaData::get(meta); + + auto * A_part = meta.get_part("block_1_A"); + ASSERT_TRUE(A_part != nullptr); + auto * B_part = meta.get_part("block_1_B"); + ASSERT_TRUE(B_part != nullptr); + + // Catch the case where the field exists on both conformal parts, but not + // the initial un-decomposed part so we can't do the initial decomposition. + aux_meta.register_field("LS", FieldType::REAL, stk::topology::NODE_RANK, + 1u, 1u, *A_part); + aux_meta.register_field("LS", FieldType::REAL, stk::topology::NODE_RANK, + 1u, 1u, *B_part); + + commit(); + + EXPECT_ANY_THROW(krino_mesh->check_isovariable_field_existence_on_decomposed_blocks(true)); +} + +TEST_F(CDMeshTests2D, IsovariableOnlyOnBlock1SteadyState) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + + if (parallel_size > 1) return; + + setup_ls_field(); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}, false); + + auto & meta = fixture.meta_data(); + auto & aux_meta = AuxMetaData::get(meta); + + aux_meta.register_field("LS", FieldType::REAL, stk::topology::NODE_RANK, + 1u, 1u, block_part); + + commit(); + + EXPECT_NO_THROW(krino_mesh->check_isovariable_field_existence_on_decomposed_blocks(false)); +} + +TEST_F(CDMeshTests2D, DeathIsovariableNotDefinedOnDecomposedBlock) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + + if (parallel_size > 1) return; + + const bool is_death = true; + setup_ls_field(is_death); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}, false); + + commit(); + + EXPECT_ANY_THROW(krino_mesh->check_isovariable_field_existence_on_decomposed_blocks(true)); +} + +TEST_F(CDMeshTests2D, DeathIsovariableNotDefinedOnDeadBlock) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + + if (parallel_size > 1) return; + + const bool is_death = true; + setup_ls_field(is_death); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}, false); + + auto & meta = fixture.meta_data(); + auto & aux_meta = AuxMetaData::get(meta); + auto * alive_part = meta.get_part("block_1_A"); + ASSERT_TRUE(alive_part != nullptr); + + aux_meta.register_field("LS", FieldType::REAL, stk::topology::NODE_RANK, + 1u, 1u, block_part); + aux_meta.register_field("LS", FieldType::REAL, stk::topology::NODE_RANK, + 1u, 1u, *alive_part); + + commit(); + + EXPECT_NO_THROW(krino_mesh->check_isovariable_field_existence_on_decomposed_blocks(true)); +} + +typedef CompleteDecompositionFixture CDMeshTests2D; +TEST_F(CDMeshTests2D, Single_Tri3_Decomposition) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + + if (parallel_size > 1) return; + + setup_ls_field(); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}); + + commit(); + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + mesh.modification_begin(); + { + stk::mesh::PartVector elem_parts; + elem_parts.push_back(&block_part); + elem_parts.push_back(&aux_meta.active_part()); + std::vector node_ids = {1, 2, 3}; + + create_element(elem_parts, 1, node_ids); + } + mesh.modification_end(); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + const auto node1 = mesh.get_entity(stk::topology::NODE_RANK, 1); + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + const auto node3 = mesh.get_entity(stk::topology::NODE_RANK, 3); + + double * node1_coords = field_data(coord_field, node1); + double * node2_coords = field_data(coord_field, node2); + double * node3_coords = field_data(coord_field, node3); + + node1_coords[0] = 0.; + node1_coords[1] = 0.; + + node2_coords[0] = 1.; + node2_coords[1] = 0.; + + node3_coords[0] = 0.; + node3_coords[1] = 1.; + + *field_data(ls_policy.ls_isovar, node1) = -1; + *field_data(ls_policy.ls_isovar, node2) = 1; + *field_data(ls_policy.ls_isovar, node3) = -1; + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + std::vector entities; + + // Should have added 2 nodes at the cutting locations + mesh.get_entities(stk::topology::NODE_RANK, meta.universal_part(), entities); + EXPECT_EQ(5u, entities.size()); + mesh.get_entities(stk::topology::NODE_RANK, aux_meta.active_part(), entities); + EXPECT_EQ(5u, entities.size()); + + // Should be 1 interface edge + mesh.get_entities(stk::topology::EDGE_RANK, aux_meta.active_part() & + aux_meta.get_part("surface_block_1_A_B") & aux_meta.get_part("surface_block_1_B_A"), entities); + EXPECT_EQ(1u, entities.size()); + + // Should be 3 conformal elements plus the parent element + mesh.get_entities(stk::topology::ELEMENT_RANK, meta.universal_part(), entities); + EXPECT_EQ(4u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.active_part(), entities); + EXPECT_EQ(3u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, !aux_meta.active_part(), entities); + EXPECT_EQ(1u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_A"), entities); + EXPECT_EQ(1u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_B"), entities); + EXPECT_EQ(2u, entities.size()); +} + +TEST_F(CDMeshTests2D, Two_Tri3_Snapped_Interface_Decomposition_NP2) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size != 2) return; + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + setup_ls_field(); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}); + + commit(); + + build_two_tri3_mesh_on_one_or_two_procs(*this, block_part, parallel_rank); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + { + const auto node1 = mesh.get_entity(stk::topology::NODE_RANK, 1); + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + const auto node3 = mesh.get_entity(stk::topology::NODE_RANK, 3); + const auto node4 = mesh.get_entity(stk::topology::NODE_RANK, 4); + + *field_data(ls_policy.ls_isovar, node1) = -1; + *field_data(ls_policy.ls_isovar, node2) = 0; + *field_data(ls_policy.ls_isovar, node3) = 1; + *field_data(ls_policy.ls_isovar, node4) = 0; + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + check_two_tri3_snapped_mesh_np2(*this, parallel_rank); + + std::vector entities; + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_A") & meta.locally_owned_part(), entities); + if(parallel_rank == 0) + { + EXPECT_TRUE(entities.empty()); + } + else + { + EXPECT_EQ(1u, entities.size()); + } + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_B") & meta.locally_owned_part(), entities); + if(parallel_rank == 0) + { + EXPECT_EQ(1u, entities.size()); + } + else + { + EXPECT_TRUE(entities.empty()); + } + + krino_mesh = std::make_shared(mesh, krino_mesh); + } + + // Swap the A and B elements + { + const auto node1 = mesh.get_entity(stk::topology::NODE_RANK, 1); + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + const auto node3 = mesh.get_entity(stk::topology::NODE_RANK, 3); + const auto node4 = mesh.get_entity(stk::topology::NODE_RANK, 4); + + *field_data(ls_policy.ls_isovar, node1) = 1; + *field_data(ls_policy.ls_isovar, node2) = 0; + *field_data(ls_policy.ls_isovar, node3) = -1; + *field_data(ls_policy.ls_isovar, node4) = 0; + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + check_two_tri3_snapped_mesh_np2(*this, parallel_rank); + + std::vector entities; + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_A") & meta.locally_owned_part(), entities); + if(parallel_rank == 0) + { + EXPECT_EQ(1u, entities.size()); + } + else + { + EXPECT_TRUE(entities.empty()); + } + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_B") & meta.locally_owned_part(), entities); + if(parallel_rank == 0) + { + EXPECT_TRUE(entities.empty()); + } + else + { + EXPECT_EQ(1u, entities.size()); + } + } +} + +TEST_F(CDMeshTests2D, Two_Tri3_Death_Snapped_Interface_Decomposition_NP2) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size != 2) return; + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + setup_ls_field(true); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}); + + commit(); + + build_two_tri3_mesh_on_one_or_two_procs(*this, block_part, parallel_rank); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + { + const auto node1 = mesh.get_entity(stk::topology::NODE_RANK, 1); + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + const auto node3 = mesh.get_entity(stk::topology::NODE_RANK, 3); + const auto node4 = mesh.get_entity(stk::topology::NODE_RANK, 4); + + *field_data(ls_policy.ls_isovar, node1) = -1; + *field_data(ls_policy.ls_isovar, node2) = 0; + *field_data(ls_policy.ls_isovar, node3) = 1; + *field_data(ls_policy.ls_isovar, node4) = 0; + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + check_two_tri3_snapped_mesh_np2(*this, parallel_rank); + + std::vector entities; + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_A") & meta.locally_owned_part(), entities); + if(parallel_rank == 0) + { + EXPECT_TRUE(entities.empty()); + } + else + { + EXPECT_EQ(1u, entities.size()); + } + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_B") & meta.locally_owned_part(), entities); + if(parallel_rank == 0) + { + EXPECT_EQ(1u, entities.size()); + } + else + { + EXPECT_TRUE(entities.empty()); + } + } +} + +TEST_F(CDMeshTests2D, Two_Tri3_Death_Snapped_Interface_Decomposition_NP2_Opposite_Ownership) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size != 2) return; + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + setup_ls_field(true); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}); + + commit(); + + build_two_tri3_mesh_on_one_or_two_procs(*this, block_part, parallel_rank); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + { + const auto node1 = mesh.get_entity(stk::topology::NODE_RANK, 1); + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + const auto node3 = mesh.get_entity(stk::topology::NODE_RANK, 3); + const auto node4 = mesh.get_entity(stk::topology::NODE_RANK, 4); + + *field_data(ls_policy.ls_isovar, node1) = 1; + *field_data(ls_policy.ls_isovar, node2) = 0; + *field_data(ls_policy.ls_isovar, node3) = -1; + *field_data(ls_policy.ls_isovar, node4) = 0; + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + check_two_tri3_snapped_mesh_np2(*this, parallel_rank); + + std::vector entities; + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_A") & meta.locally_owned_part(), entities); + if(parallel_rank == 0) + { + EXPECT_EQ(1u, entities.size()); + } + else + { + EXPECT_TRUE(entities.empty()); + } + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_B") & meta.locally_owned_part(), entities); + if(parallel_rank == 0) + { + EXPECT_TRUE(entities.empty()); + } + else + { + EXPECT_EQ(1u, entities.size()); + } + } +} + +TEST_F(CDMeshTests2D, Two_Tri3_Periodic) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size > 2) return; + + cdfemSupport.set_cdfem_edge_tol(0.1); + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + AuxMetaData & aux_meta = AuxMetaData::get(fixture.meta_data()); + + setup_ls_field(); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}); + + commit(); + + build_two_tri3_mesh_on_one_or_two_procs(*this, block_part, parallel_rank); + + stk::mesh::create_exposed_block_boundary_sides(mesh, fixture.meta_data().universal_part(), {&aux_meta.exposed_boundary_part()}); + + { + const auto node1 = mesh.get_entity(stk::topology::NODE_RANK, 1); + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + const auto node3 = mesh.get_entity(stk::topology::NODE_RANK, 3); + const auto node4 = mesh.get_entity(stk::topology::NODE_RANK, 4); + + *field_data(ls_policy.ls_isovar, node1) = -0.99; + *field_data(ls_policy.ls_isovar, node2) = -0.8; + *field_data(ls_policy.ls_isovar, node3) = 0.2; + *field_data(ls_policy.ls_isovar, node4) = 0.01; + + krino_mesh->add_periodic_node_pair(node3, node4); + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + // Periodic constraint should cause interface to snap to both 3 and 4 so no elements + // get cut. + std::vector entities; + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_A"), entities); + EXPECT_EQ(0u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_B"), entities); + EXPECT_EQ(2u, entities.size()); + } +} + +TEST_F(CDMeshTests2D, Two_Tri3_PeriodicParallelNonSharedNode) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size == 1 || (parallel_size % 2) != 0) return; + + cdfemSupport.set_cdfem_edge_tol(0.1); + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + setup_ls_field(); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}); + + commit(); + + const stk::mesh::EntityId starting_id = 1 + 3*parallel_rank; + mesh.modification_begin(); + { + stk::mesh::PartVector elem_parts; + elem_parts.push_back(&block_part); + elem_parts.push_back(&cdfemSupport.get_active_part()); + + std::vector elem_nodes = {starting_id, starting_id + 1, starting_id + 2}; + + create_element(elem_parts, parallel_rank + 1, elem_nodes); + } + mesh.modification_end(); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + EXPECT_EQ(1u, stk::mesh::count_selected_entities(meta.universal_part(), mesh.buckets(stk::topology::ELEMENT_RANK))); + + const auto node1 = mesh.get_entity(stk::topology::NODE_RANK, starting_id); + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, starting_id + 1); + const auto node3 = mesh.get_entity(stk::topology::NODE_RANK, starting_id + 2); + + ASSERT_TRUE(mesh.is_valid(node1)); + ASSERT_TRUE(mesh.is_valid(node2)); + ASSERT_TRUE(mesh.is_valid(node3)); + + double * node1_coords = field_data(coord_field, node1); + double * node2_coords = field_data(coord_field, node2); + double * node3_coords = field_data(coord_field, node3); + + const double x0 = 1.1 * parallel_rank; + node1_coords[0] = x0; + node1_coords[1] = 0.; + + node2_coords[0] = x0 + 1.; + node2_coords[1] = 0.; + + node3_coords[0] = x0 + 1.; + node3_coords[1] = 1.; + + // Going to add "periodic" constraint between node1 on P0 and P1, so ghost those nodes appropriately + mesh.modification_begin(); + auto & ghosting = mesh.create_ghosting("test_ghosting"); + mesh.modification_end(); + stk::mesh::EntityProcVec add_send; + const int mod = parallel_rank % 2; + if(mod == 0) + { + add_send.push_back(std::make_pair(node1, parallel_rank + 1)); + } + else + { + add_send.push_back(std::make_pair(node1, parallel_rank - 1)); + } + mesh.modification_begin(); + mesh.change_ghosting(ghosting, add_send); + mesh.modification_end(); + + ASSERT_EQ(4u, + stk::mesh::count_selected_entities(meta.universal_part(), mesh.buckets(stk::topology::NODE_RANK))); + { + // Procs with mod == 0 will setup isovars so that the 1-2 edge has a crossing within the snap tolerance + // of node 1. Procs with mod == 1 will have the crossing outside the snap tolerance + *field_data(ls_policy.ls_isovar, node1) = (mod == 0) ? -0.01 : -1.; + *field_data(ls_policy.ls_isovar, node2) = 1.; + *field_data(ls_policy.ls_isovar, node3) = 1.; + + auto other_node1 = mesh.get_entity(stk::topology::NODE_RANK, + (mod == 0) ? starting_id + 3 : starting_id - 3); + krino_mesh->add_periodic_node_pair(node1, other_node1); + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + // Periodic constraint should cause interface to snap to node1 so the element is entirely in the + // A phase. + std::vector entities; + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_A"), entities); + EXPECT_EQ(1u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_B"), entities); + EXPECT_EQ(0u, entities.size()); + } +} + +void set_level_set(const stk::mesh::BulkData & mesh, FieldRef lsField, const std::vector & nodeIds, const std::vector & nodeLS) +{ + ThrowRequire(nodeIds.size() == nodeLS.size()); + for (size_t i=0; i(lsField, node) = nodeLS[i]; + } +} + +TEST_F(CDMeshTests2D, Two_Tri3_Check_Compatibility_When_Snapping) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size > 2) return; + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + AuxMetaData & aux_meta = AuxMetaData::get(fixture.meta_data()); + + setup_ls_field(); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}); + + commit(); + + build_two_tri3_mesh_on_one_or_two_procs(*this, block_part, parallel_rank); + + stk::mesh::create_exposed_block_boundary_sides(mesh, fixture.meta_data().universal_part(), {&aux_meta.exposed_boundary_part()}); + + set_level_set(mesh, ls_policy.ls_isovar, {1,2,3,4} , {-1., -1., 1.e20, 1.}); + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + std::vector entities; + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_A"), entities); + EXPECT_EQ(2u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_B"), entities); + EXPECT_EQ(1u, entities.size()); + + //fixture.write_results("Two_Tri3_Check_Compatibility_When_Snapping.e"); +} + +void set_level_sets(const stk::mesh::BulkData & mesh, std::vector & lsFields, const std::vector & nodeIds, const std::vector> & nodeLS) +{ + ThrowRequire(nodeIds.size() == nodeLS.size()); + const size_t numFields = lsFields.size(); + for (size_t i=0; i(lsFields[j], node) = nodeLS[i][j]; + } +} + +typedef CompleteDecompositionFixture > CDMeshTests2DLSPerPhase; +TEST_F(CDMeshTests2DLSPerPhase, Two_Tri3_Check_Compatibility_When_Snapping) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size > 2) return; + + cdfemSupport.set_cdfem_edge_degeneracy_handling(SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE); + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + AuxMetaData & aux_meta = AuxMetaData::get(fixture.meta_data()); + + aux_meta.set_assert_32bit_flag(); + aux_meta.clear_force_64bit_flag(); + + setup_ls_field(); + + auto & blockPart = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&blockPart}); + const auto & sideParts = declare_input_surfaces_touching_block(4, blockPart); + + commit(); + + build_two_tri3_mesh_on_one_or_two_procs_with_sides(*this, blockPart, sideParts, parallel_rank); + + stk::mesh::create_exposed_block_boundary_sides(mesh, fixture.meta_data().universal_part(), {&aux_meta.exposed_boundary_part()}); + + const double eps = 1.e-13; + set_level_sets(mesh, ls_policy.ls_isovars, {1,2,3,4} , {{-1.,1.,1.+eps}, {-1.,1.,1.+eps}, {1.e2,1.,-1.}, {1.,-1.,-1.+eps}}); + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + //std::cout << log.get_log() << std::endl; + + std::vector entities; + mesh.get_entities(stk::topology::NODE_RANK, fixture.meta_data().universal_part(), entities); + EXPECT_EQ(7u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_P0"), entities); + EXPECT_EQ(3u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_P1"), entities); + EXPECT_EQ(1u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_P2"), entities); + EXPECT_EQ(2u, entities.size()); + + //fixture.write_results("Two_Tri3_Check_Compatibility_When_Snapping_LSPerPhase.e"); +} + +typedef CompleteDecompositionFixture CDMeshTests3D; +TEST_F(CDMeshTests3D, Write_Results_No_Side) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size > 2) return; + + const stk::topology tet4 = stk::topology::TETRAHEDRON_4; + auto & block1_part = declare_input_block("block_1", tet4); + auto & block2_part = declare_input_block("block_2", tet4); + auto & surface_part = declare_input_surface("surface_1", tet4.side_topology(), {block1_part.mesh_meta_data_ordinal(), block2_part.mesh_meta_data_ordinal()}); + + commit(); + + build_two_tet4_mesh_np2(*this, block1_part, block2_part, surface_part, parallel_rank, parallel_size, false); + + fixture.write_results("Write_Results_No_Side.e"); +} + +TEST_F(CDMeshTests3D, Write_Results_With_Side) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size > 2) return; + + const stk::topology tet4 = stk::topology::TETRAHEDRON_4; + auto & block1_part = declare_input_block("block_1", tet4); + auto & block2_part = declare_input_block("block_2", tet4); + auto & surface_part = declare_input_surface("surface_1", tet4.side_topology(), {block1_part.mesh_meta_data_ordinal(), block2_part.mesh_meta_data_ordinal()}); + + commit(); + + build_two_tet4_mesh_np2(*this, block1_part, block2_part, surface_part, parallel_rank, parallel_size); + + fixture.write_results("Write_Results_With_Side.e"); +} + +namespace +{ +void randomize_ls_field(const stk::mesh::BulkData & mesh, + const FieldRef & field, + std::mt19937 & mt, + std::uniform_real_distribution & dist) +{ + auto & node_buckets = mesh.buckets(stk::topology::NODE_RANK); + for(auto && b_ptr : node_buckets) + { + for(auto && node : *b_ptr) + { + double * ls_data = field_data(field, node); + if(ls_data) *ls_data = dist(mt); + } + } +} +void set_ls_field_on_part(const stk::mesh::BulkData & mesh, + const stk::mesh::Part & part, + const FieldRef & ls_field, + const double ls_value) +{ + auto & node_buckets = mesh.get_buckets(stk::topology::NODE_RANK, part); + for(auto && b_ptr : node_buckets) + { + for(auto && node : *b_ptr) + { + double * ls_data = field_data(ls_field, node); + if(ls_data) *ls_data = ls_value; + } + } +} + +} + +TEST_F(CDMeshTests3D, Random_TwoTet4_InternalSideset) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + + if (parallel_size > 2) return; + + // Use a large snap tolerance to make snapping more common since it is a frequent source + // of parallel bugs + cdfemSupport.set_cdfem_edge_tol(0.1); + cdfemSupport.set_simplex_generation_method(CUT_QUADS_BY_GLOBAL_IDENTIFIER); + + setup_ls_field(); + + const stk::topology tet4 = stk::topology::TETRAHEDRON_4; + auto & block1_part = declare_input_block("block_1", tet4); + auto & block2_part = declare_input_block("block_2", tet4); + auto & surface_part = declare_input_surface("surface_1", tet4.side_topology(), {block1_part.mesh_meta_data_ordinal(), block2_part.mesh_meta_data_ordinal()}); + + register_ls_on_blocks({&block1_part, &block2_part}); + + commit(); + + build_two_tet4_mesh_np2(*this, block1_part, block2_part, surface_part, parallel_rank, parallel_size); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution dist(-1., 1.); +#ifdef NDEBUG + const int num_cases = 5000; +#else + const int num_cases = 1000; +#endif + for(int i=0; i < num_cases; ++i) + { + if (i%1000 == 0) std::cout << "Testing random configuration " << i << std::endl; + + randomize_ls_field(mesh, ls_policy.ls_isovar, mt, dist); + stk::mesh::communicate_field_data(mesh, {&ls_policy.ls_isovar.field(), &coord_field.field()}); + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + EXPECT_TRUE(check_induced_parts(mesh)); + EXPECT_TRUE(check_face_and_edge_ownership(mesh)); + EXPECT_TRUE(check_face_and_edge_relations(mesh)); + EXPECT_TRUE(check_shared_entity_nodes(mesh)); + EXPECT_TRUE(krino_mesh->check_element_side_parts()); + + if(HasNonfatalFailure()) + { + std::cout << "Failure on iteration " << i << std::endl; + krino_mesh->debug_output(); + std::cout << log.get_log() << std::endl; + std::ostringstream fname; + fname << "Random_TwoTet4_InternalSideset_iter_" << i << ".e"; + fixture.write_results(fname.str()); + ASSERT_TRUE(false); + } + + krino_mesh = std::make_shared(mesh, krino_mesh); + } +} + +typedef CompleteDecompositionFixture CDMeshTestsBboxMesh2D; +TEST_F(CDMeshTestsBboxMesh2D, Random_SnapMesh) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + if (parallel_size > 2) return; + + cdfemSupport.set_cdfem_edge_degeneracy_handling(SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE); + const double approxMinRelativeSize = 0.25; + + setup_ls_field(); + + auto & block1_part = aux_meta.get_part("block_1"); + + register_ls_on_blocks({&block1_part}); + + typename BoundingBoxMesh::BoundingBoxType domain(Vector3d::ZERO, Vector3d(1.,1.,0.)); + const double mesh_size = 1./3.; + fixture.set_domain(domain, mesh_size); + fixture.populate_mesh(); + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part(),&aux_meta.active_part()}); + + double overallMinEdgeLength = std::numeric_limits::max(); + double overallMaxEdgeLength = -std::numeric_limits::max(); + double overallMinVolume = std::numeric_limits::max(); + double overallMaxVolume = -std::numeric_limits::max(); + const double expectedMinLength = mesh_size*approxMinRelativeSize; + const double expectedMinVol = std::pow(mesh_size*approxMinRelativeSize, 2.) / 2.; + + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution dist(-1., 1.); +#ifdef NDEBUG + const int num_cases = 5000; +#else + const int num_cases = 1000; +#endif + for(int i=0; i < num_cases; ++i) + { + if (i%1000 == 0) std::cout << "Testing random configuration " << i << std::endl; + + MeshClone::stash_or_restore_mesh(mesh, 0); // restore original uncut mesh + commit(); // new krino_mesh each time + + randomize_ls_field(mesh, ls_policy.ls_isovar, mt, dist); + set_ls_field_on_part(mesh, aux_meta.exposed_boundary_part(), ls_policy.ls_isovar, 1.); + stk::mesh::communicate_field_data(mesh, {&ls_policy.ls_isovar.field(), &coord_field.field()}); + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + EXPECT_TRUE(check_induced_parts(mesh)); + EXPECT_TRUE(check_face_and_edge_ownership(mesh)); + EXPECT_TRUE(check_face_and_edge_relations(mesh)); + EXPECT_TRUE(check_shared_entity_nodes(mesh)); + EXPECT_TRUE(krino_mesh->check_element_side_parts()); + + double minEdgeLength, maxEdgeLength, minVolume, maxVolume; + compute_element_quality(mesh, minEdgeLength, maxEdgeLength, minVolume, maxVolume); + overallMinEdgeLength = std::min(overallMinEdgeLength, minEdgeLength); + overallMaxEdgeLength = std::max(overallMaxEdgeLength, maxEdgeLength); + overallMinVolume = std::min(overallMinVolume, minVolume); + overallMaxVolume = std::max(overallMaxVolume, maxVolume); + + bool failedQuality = false; + if (minVolume < 0.5*expectedMinVol) + { + failedQuality = true; + std::cout << "Failed quality requirements: minEdgeLength=" << minEdgeLength + << ", maxEdgeLength=" << maxEdgeLength + << ", minVolume=" << minVolume + << ", maxVolume=" << maxVolume << std::endl; + } + + if(HasNonfatalFailure() || failedQuality) + { + std::cout << "Failure on iteration " << i << std::endl; + krino_mesh->debug_output(); + std::cout << log.get_log() << std::endl; + std::ostringstream fname; + fname << "Random_SnapTri3_iter_" << i << ".e"; + SimpleStkFixture::write_results(fname.str(), mesh); + ASSERT_TRUE(false); + } + } + std::cout << "Expected quality: minEdgeLength~=" << expectedMinLength << ", minVolume~=" << expectedMinVol << std::endl; + std::cout << "Quality results: minEdgeLength=" << overallMinEdgeLength + << ", maxEdgeLength=" << overallMaxEdgeLength + << ", minVolume=" << overallMinVolume + << ", maxVolume=" << overallMaxVolume << std::endl; +} + +typedef CompleteDecompositionFixture> CDMeshTestsBboxMesh2DLSPerPhase; +TEST_F(CDMeshTestsBboxMesh2DLSPerPhase, Random_SnapMesh) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + if (parallel_size > 2) return; + + cdfemSupport.set_cdfem_edge_degeneracy_handling(SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE); + const double approxMinRelativeSize = 0.25; + + setup_ls_field(); + + auto & block1_part = aux_meta.get_part("block_1"); + + register_ls_on_blocks({&block1_part}); + + typename BoundingBoxMesh::BoundingBoxType domain(Vector3d::ZERO, Vector3d(1.,1.,0.)); + const double mesh_size = 1./3.; + fixture.set_domain(domain, mesh_size); + fixture.populate_mesh(); + fixture.create_domain_sides(); + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part(),&aux_meta.active_part()}); + + const auto & ls_isovars = ls_policy.ls_isovars; + std::vector sync_fields = {&coord_field.field()}; + for(auto && isovar : ls_isovars) sync_fields.push_back(&isovar.field()); + + double overallMinEdgeLength = std::numeric_limits::max(); + double overallMaxEdgeLength = -std::numeric_limits::max(); + double overallMinVolume = std::numeric_limits::max(); + double overallMaxVolume = -std::numeric_limits::max(); + const double expectedMinLength = mesh_size*approxMinRelativeSize; + const double expectedMinVol = std::pow(mesh_size*approxMinRelativeSize, 2.) / 2.; + + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution dist(-1., 1.); +#ifdef NDEBUG + const int num_cases = 5000; +#else + const int num_cases = 1000; +#endif + for(int i=0; i < num_cases; ++i) + { + if (i%1000 == 0) std::cout << "Testing random configuration " << i << std::endl; + + MeshClone::stash_or_restore_mesh(mesh, 0); // restore original uncut mesh + commit(); // new krino_mesh each time + + for(auto && isovar : ls_isovars) + { + randomize_ls_field(mesh, isovar, mt, dist); + } + stk::mesh::communicate_field_data(mesh, sync_fields); + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << log.get_log() << std::endl; + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + EXPECT_TRUE(check_induced_parts(mesh)); + EXPECT_TRUE(check_face_and_edge_ownership(mesh)); + EXPECT_TRUE(check_face_and_edge_relations(mesh)); + EXPECT_TRUE(check_shared_entity_nodes(mesh)); + EXPECT_TRUE(krino_mesh->check_element_side_parts()); + + double minEdgeLength, maxEdgeLength, minVolume, maxVolume; + compute_element_quality(mesh, minEdgeLength, maxEdgeLength, minVolume, maxVolume); + overallMinEdgeLength = std::min(overallMinEdgeLength, minEdgeLength); + overallMaxEdgeLength = std::max(overallMaxEdgeLength, maxEdgeLength); + overallMinVolume = std::min(overallMinVolume, minVolume); + overallMaxVolume = std::max(overallMaxVolume, maxVolume); + + if(HasNonfatalFailure()) + { + std::cout << "Failure on iteration " << i << std::endl; + krino_mesh->debug_output(); + std::cout << log.get_log() << std::endl; + std::ostringstream fname; + fname << "Random_SnapTri3_iter_" << i << ".e"; + SimpleStkFixture::write_results(fname.str(), mesh); + ASSERT_TRUE(false); + } + } + std::cout << "Expected quality: minEdgeLength~=" << expectedMinLength << ", minVolume~=" << expectedMinVol << std::endl; + std::cout << "Quality results: minEdgeLength=" << overallMinEdgeLength + << ", maxEdgeLength=" << overallMaxEdgeLength + << ", minVolume=" << overallMinVolume + << ", maxVolume=" << overallMaxVolume << std::endl; +} + +typedef CompleteDecompositionFixture CDMeshTestsBboxMesh3D; +TEST_F(CDMeshTestsBboxMesh3D, Random_SnapMesh) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + if (parallel_size > 2) return; + + cdfemSupport.set_cdfem_edge_degeneracy_handling(SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE); + const double approxMinRelativeSize = 0.25; + + setup_ls_field(); + + auto & block1_part = aux_meta.get_part("block_1"); + + register_ls_on_blocks({&block1_part}); + + typename BoundingBoxMesh::BoundingBoxType domain(Vector3d::ZERO, Vector3d(1.,1.,1.)); + const double mesh_size = 1./3.; + fixture.set_domain(domain, mesh_size); + fixture.populate_mesh(); + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part(),&aux_meta.active_part()}); + + double overallMinEdgeLength = std::numeric_limits::max(); + double overallMaxEdgeLength = -std::numeric_limits::max(); + double overallMinVolume = std::numeric_limits::max(); + double overallMaxVolume = -std::numeric_limits::max(); + const double expectedMinLength = mesh_size*approxMinRelativeSize; + const double expectedMinVol = std::pow(mesh_size*approxMinRelativeSize, 3.) / 6.; + + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution dist(-1., 1.); +#ifdef NDEBUG + const int num_cases = 1000; +#else + const int num_cases = 250; +#endif + for(int i=0; i < num_cases; ++i) + { + if (i%250 == 0) std::cout << "Testing random configuration " << i << std::endl; + + MeshClone::stash_or_restore_mesh(mesh, 0); // restore original uncut mesh + commit(); // new krino_mesh each time + + randomize_ls_field(mesh, ls_policy.ls_isovar, mt, dist); + set_ls_field_on_part(mesh, aux_meta.exposed_boundary_part(), ls_policy.ls_isovar, 1.); + stk::mesh::communicate_field_data(mesh, {&ls_policy.ls_isovar.field(), &coord_field.field()}); + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + EXPECT_TRUE(check_induced_parts(mesh)); + EXPECT_TRUE(check_face_and_edge_ownership(mesh)); + EXPECT_TRUE(check_face_and_edge_relations(mesh)); + EXPECT_TRUE(check_shared_entity_nodes(mesh)); + EXPECT_TRUE(krino_mesh->check_element_side_parts()); + + double minEdgeLength, maxEdgeLength, minVolume, maxVolume; + compute_element_quality(mesh, minEdgeLength, maxEdgeLength, minVolume, maxVolume); + overallMinEdgeLength = std::min(overallMinEdgeLength, minEdgeLength); + overallMaxEdgeLength = std::max(overallMaxEdgeLength, maxEdgeLength); + overallMinVolume = std::min(overallMinVolume, minVolume); + overallMaxVolume = std::max(overallMaxVolume, maxVolume); + + bool failedQuality = false; + if (minVolume < 0.5*expectedMinVol) + { + failedQuality = true; + std::cout << "Failed quality requirements: minEdgeLength=" << minEdgeLength + << ", maxEdgeLength=" << maxEdgeLength + << ", minVolume=" << minVolume + << ", maxVolume=" << maxVolume << std::endl; + } + + if(HasNonfatalFailure() || failedQuality) + { + std::cout << "Failure on iteration " << i << std::endl; + krino_mesh->debug_output(); + std::cout << log.get_log() << std::endl; + std::ostringstream fname; + fname << "Random_SnapTet4_iter_" << i << ".e"; + SimpleStkFixture::write_results(fname.str(), mesh); + ASSERT_TRUE(false); + } + } + std::cout << "Expected quality: minEdgeLength~=" << expectedMinLength << ", minVolume~=" << expectedMinVol << std::endl; + std::cout << "Actual quality results: minEdgeLength=" << overallMinEdgeLength + << ", maxEdgeLength=" << overallMaxEdgeLength + << ", minVolume=" << overallMinVolume + << ", maxVolume=" << overallMaxVolume << std::endl; +} + +typedef CompleteDecompositionFixture CDMeshTestsBboxMesh3DBCC; +TEST_F(CDMeshTestsBboxMesh3DBCC, Random_SnapMesh) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + if (parallel_size > 2) return; + + cdfemSupport.set_cdfem_edge_degeneracy_handling(SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE); + const double approxMinRelativeSize = 0.25; + + setup_ls_field(); + + auto & block1_part = aux_meta.get_part("block_1"); + + register_ls_on_blocks({&block1_part}); + + typename BoundingBoxMesh::BoundingBoxType domain(Vector3d::ZERO, Vector3d(1.,1.,1.)); + const double mesh_size = 1./3.; + fixture.set_domain(domain, mesh_size); + fixture.set_mesh_structure_type(BCC_BOUNDING_BOX_MESH); + fixture.populate_mesh(); + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part(),&aux_meta.active_part()}); + + const double BCCsize = std::sqrt(3.)/2.*mesh_size; + double overallMinEdgeLength = std::numeric_limits::max(); + double overallMaxEdgeLength = -std::numeric_limits::max(); + double overallMinVolume = std::numeric_limits::max(); + double overallMaxVolume = -std::numeric_limits::max(); + const double expectedMinLength = BCCsize*approxMinRelativeSize; + const double expectedMinVol = std::pow(BCCsize*approxMinRelativeSize, 3.) / (6.*std::sqrt(2.)); + + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution dist(-1., 1.); +#ifdef NDEBUG + const int num_cases = 1000; +#else + const int num_cases = 250; +#endif + for(int i=0; i < num_cases; ++i) + { + if (i%250 == 0) std::cout << "Testing random configuration " << i << std::endl; + + MeshClone::stash_or_restore_mesh(mesh, 0); // restore original uncut mesh + commit(); // new krino_mesh each time + + randomize_ls_field(mesh, ls_policy.ls_isovar, mt, dist); + set_ls_field_on_part(mesh, aux_meta.exposed_boundary_part(), ls_policy.ls_isovar, 1.); + stk::mesh::communicate_field_data(mesh, {&ls_policy.ls_isovar.field(), &coord_field.field()}); + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + EXPECT_TRUE(check_induced_parts(mesh)); + EXPECT_TRUE(check_face_and_edge_ownership(mesh)); + EXPECT_TRUE(check_face_and_edge_relations(mesh)); + EXPECT_TRUE(check_shared_entity_nodes(mesh)); + EXPECT_TRUE(krino_mesh->check_element_side_parts()); + + double minEdgeLength, maxEdgeLength, minVolume, maxVolume; + compute_element_quality(mesh, minEdgeLength, maxEdgeLength, minVolume, maxVolume); + overallMinEdgeLength = std::min(overallMinEdgeLength, minEdgeLength); + overallMaxEdgeLength = std::max(overallMaxEdgeLength, maxEdgeLength); + overallMinVolume = std::min(overallMinVolume, minVolume); + overallMaxVolume = std::max(overallMaxVolume, maxVolume); + + bool failedQuality = false; + if (minVolume < 0.5*expectedMinVol) + { + failedQuality = true; + std::cout << "Failed quality requirements: minEdgeLength=" << minEdgeLength + << ", maxEdgeLength=" << maxEdgeLength + << ", minVolume=" << minVolume + << ", maxVolume=" << maxVolume << std::endl; + } + + if(HasNonfatalFailure() || failedQuality) + { + std::cout << "Failure on iteration " << i << std::endl; + krino_mesh->debug_output(); + std::cout << log.get_log() << std::endl; + std::ostringstream fname; + fname << "Random_SnapTet4_iter_" << i << ".e"; + SimpleStkFixture::write_results(fname.str(), mesh); + ASSERT_TRUE(false); + } + } + std::cout << "Expected quality: minEdgeLength~=" << expectedMinLength << ", minVolume~=" << expectedMinVol << std::endl; + std::cout << "Actual quality results: minEdgeLength=" << overallMinEdgeLength + << ", maxEdgeLength=" << overallMaxEdgeLength + << ", minVolume=" << overallMinVolume + << ", maxVolume=" << overallMaxVolume << std::endl; +} + +TEST_F(CDMeshTests2DLSPerPhase, Tri3_3LS_SnappedTriplePoint) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + + if (parallel_size > 1) return; + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + cdfemSupport.set_cdfem_edge_tol(0.15); + + setup_ls_field(); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}); + + commit(); + + mesh.modification_begin(); + { + stk::mesh::PartVector elem_parts; + elem_parts.push_back(&block_part); + elem_parts.push_back(&aux_meta.active_part()); + std::vector node_ids = {1, 2, 3}; + + create_element(elem_parts, 1, node_ids); + } + mesh.modification_end(); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + const auto node1 = mesh.get_entity(stk::topology::NODE_RANK, 1); + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + const auto node3 = mesh.get_entity(stk::topology::NODE_RANK, 3); + + double * node1_coords = field_data(coord_field, node1); + double * node2_coords = field_data(coord_field, node2); + double * node3_coords = field_data(coord_field, node3); + + node1_coords[0] = 0.; + node1_coords[1] = 0.; + + node2_coords[0] = 1.; + node2_coords[1] = 0.; + + node3_coords[0] = 0.; + node3_coords[1] = 1.; + + const auto & ls_isovars = ls_policy.ls_isovars; + *field_data(ls_isovars[0], node1) = 0.; + *field_data(ls_isovars[0], node2) = 0.; + *field_data(ls_isovars[0], node3) = 0.; + + *field_data(ls_isovars[1], node1) = 0.1; + *field_data(ls_isovars[1], node2) = -0.2; + *field_data(ls_isovars[1], node3) = -0.01; + + *field_data(ls_isovars[2], node1) = 0.2; + *field_data(ls_isovars[2], node2) = -0.25; + *field_data(ls_isovars[2], node3) = -0.005; + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + std::vector entities; + + // This assumes that the element is cut first by the (0,1) interface, + // then the (0, 2) virtual interface, and then the (1, 2) interface. + // THere are other valid decompositions if the element is cut in a different order. + // Should have added 3 nodes at the cutting locations + mesh.get_entities(stk::topology::NODE_RANK, meta.universal_part(), entities); + EXPECT_EQ(5u, entities.size()); + mesh.get_entities(stk::topology::NODE_RANK, aux_meta.active_part(), entities); + EXPECT_EQ(5u, entities.size()); + + mesh.get_entities(stk::topology::EDGE_RANK, aux_meta.active_part() & + aux_meta.get_part("surface_block_1_P0_P1") & aux_meta.get_part("surface_block_1_P1_P0"), entities); + EXPECT_EQ(1u, entities.size()); + mesh.get_entities(stk::topology::EDGE_RANK, aux_meta.active_part() & + aux_meta.get_part("surface_block_1_P1_P2") & aux_meta.get_part("surface_block_1_P2_P1"), entities); + EXPECT_EQ(1u, entities.size()); + mesh.get_entities(stk::topology::EDGE_RANK, aux_meta.active_part() & + aux_meta.get_part("surface_block_1_P0_P2") & aux_meta.get_part("surface_block_1_P2_P0"), entities); + EXPECT_EQ(0u, entities.size()); + + // Should be 3 conformal elements plus the parent element + mesh.get_entities(stk::topology::ELEMENT_RANK, meta.universal_part(), entities); + EXPECT_EQ(4u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.active_part(), entities); + EXPECT_EQ(3u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, !aux_meta.active_part(), entities); + EXPECT_EQ(1u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_P0"), entities); + EXPECT_EQ(1u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_P1"), entities); + EXPECT_EQ(1u, entities.size()); + mesh.get_entities(stk::topology::ELEMENT_RANK, aux_meta.get_part("block_1_P2"), entities); + EXPECT_EQ(1u, entities.size()); +} + +TEST_F(CDMeshTests2DLSPerPhase, Tri3_3LS_TriplePointDebug) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + + if (parallel_size > 1) return; + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + cdfemSupport.set_cdfem_edge_tol(0.01); + + setup_ls_field(); + + auto & block_part = declare_input_block("block_1", stk::topology::TRIANGLE_3_2D); + register_ls_on_blocks({&block_part}); + + commit(); + + mesh.modification_begin(); + { + stk::mesh::PartVector elem_parts; + elem_parts.push_back(&block_part); + elem_parts.push_back(&aux_meta.active_part()); + std::vector node_ids = {1, 2, 3}; + + create_element(elem_parts, 1, node_ids); + } + mesh.modification_end(); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + const auto node1 = mesh.get_entity(stk::topology::NODE_RANK, 1); + const auto node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + const auto node3 = mesh.get_entity(stk::topology::NODE_RANK, 3); + + double * node1_coords = field_data(coord_field, node1); + double * node2_coords = field_data(coord_field, node2); + double * node3_coords = field_data(coord_field, node3); + + node1_coords[0] = 0.; + node1_coords[1] = 0.; + + node2_coords[0] = 1.; + node2_coords[1] = 0.; + + node3_coords[0] = 0.; + node3_coords[1] = 1.; + + const auto & ls_isovars = ls_policy.ls_isovars; + *field_data(ls_isovars[0], node1) = -0.2; + *field_data(ls_isovars[1], node1) = 0.1; + *field_data(ls_isovars[2], node1) = 0.6; + + *field_data(ls_isovars[0], node2) = 0.7; + *field_data(ls_isovars[1], node2) = -0.5; + *field_data(ls_isovars[2], node2) = 0.4; + + *field_data(ls_isovars[0], node3) = 0.1; + *field_data(ls_isovars[1], node3) = 0.3; + *field_data(ls_isovars[2], node3) = -0.5; + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + // Test output, but remove output unless actually debugging. + fixture.write_results("debug_2d.e"); + std::remove("debug_2d.e"); +} + +typedef CompleteDecompositionFixture > CDMeshTests2DLSPerPhase; +TEST_F(CDMeshTests2DLSPerPhase, Random_TwoTri3_InternalSideset_Snap) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size > 2) return; + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + cdfemSupport.set_cdfem_edge_degeneracy_handling(SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE); + + setup_ls_field(); + + const stk::topology tri3 = stk::topology::TRIANGLE_3_2D; + auto & block1_part = declare_input_block("block_1", tri3); + auto & block2_part = declare_input_block("block_2", tri3); + auto & surface_part = declare_input_surface("surface_1", tri3.side_topology(), {block1_part.mesh_meta_data_ordinal(), block2_part.mesh_meta_data_ordinal()}); + + register_ls_on_blocks({&block1_part, &block2_part}); + + commit(); + + build_two_tri3_mesh_np2(*this, block1_part, block2_part, surface_part, parallel_rank, parallel_size); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + const auto & ls_isovars = ls_policy.ls_isovars; + std::vector sync_fields = {&coord_field.field()}; + for(auto && isovar : ls_isovars) sync_fields.push_back(&isovar.field()); + + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution dist(-1., 1.); +#ifdef NDEBUG + const int num_cases = 5000; +#else + const int num_cases = 1000; +#endif + for(int i=0; i < num_cases; ++i) + { + if (i%1000 == 0) std::cout << "Testing random configuration " << i << std::endl; + + MeshClone::stash_or_restore_mesh(mesh, 0); // restore original uncut mesh + commit(); // new krino_mesh each time + + for(auto && isovar : ls_isovars) + { + randomize_ls_field(mesh, isovar, mt, dist); + } + stk::mesh::communicate_field_data(mesh, sync_fields); + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << log.get_log() << std::endl; + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + EXPECT_TRUE(check_induced_parts(mesh)); + EXPECT_TRUE(check_face_and_edge_ownership(mesh)); + EXPECT_TRUE(check_face_and_edge_relations(mesh)); + EXPECT_TRUE(check_shared_entity_nodes(mesh)); + EXPECT_TRUE(krino_mesh->check_element_side_parts()); + + if (false) + { + std::ostringstream fname; + fname << "Random_TwoTri3_InternalSideset_Snap_iter_" << i << ".e"; + fixture.write_results(fname.str()); + } + + if(HasNonfatalFailure()) + { + std::cout << "Failure on iteration " << i << std::endl; + std::ostringstream fname; + fname << "Random_TwoTri3_InternalSideset_Snap_iter_" << i << ".e"; + fixture.write_results(fname.str()); + ASSERT_TRUE(false); + } + + krino_mesh = std::make_shared(mesh, krino_mesh); + } +} + +typedef CompleteDecompositionFixture > CDMeshTests3DLSPerInterface; +TEST_F(CDMeshTests3DLSPerInterface, Random_OneTet4) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size > 2) return; + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + aux_meta.set_assert_32bit_flag(); + aux_meta.clear_force_64bit_flag(); + + // Use a large snap tolerance to make snapping more common since it is a frequent source + // of parallel bugs + cdfemSupport.set_cdfem_edge_tol(0.1); + cdfemSupport.set_simplex_generation_method(CUT_QUADS_BY_GLOBAL_IDENTIFIER); + + setup_ls_field(); + + const stk::topology tet4 = stk::topology::TETRAHEDRON_4; + auto & block1_part = declare_input_block("block_1", tet4); + + register_ls_on_blocks({&block1_part}); + + commit(); + + build_one_tet4_mesh(*this, block1_part, parallel_rank, parallel_size); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + const auto & ls_isovars = ls_policy.ls_isovars; + std::vector sync_fields = {&coord_field.field()}; + for(auto && isovar : ls_isovars) sync_fields.push_back(&isovar.field()); + + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution dist(-1., 1.); +#ifdef NDEBUG + const int num_cases = 5000; +#else + const int num_cases = 1000; +#endif + for(int i=0; i < num_cases; ++i) + { + if (i%1000 == 0) std::cout << "Testing random configuration " << i << std::endl; + + MeshClone::stash_or_restore_mesh(mesh, 0); // restore original uncut mesh + commit(); // new krino_mesh each time + + for(auto && isovar : ls_isovars) + { + randomize_ls_field(mesh, isovar, mt, dist); + } + stk::mesh::communicate_field_data(mesh, sync_fields); + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << log.get_log() << std::endl; + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + EXPECT_TRUE(check_induced_parts(mesh)); + EXPECT_TRUE(check_face_and_edge_ownership(mesh)); + EXPECT_TRUE(check_face_and_edge_relations(mesh)); + EXPECT_TRUE(check_shared_entity_nodes(mesh)); + EXPECT_TRUE(krino_mesh->check_element_side_parts()); + + if(HasNonfatalFailure()) + { + std::cout << log.get_log() << std::endl; + std::cout << "Failure on iteration " << i << std::endl; + std::ostringstream fname; + fname << "Random_TwoTet4_InternalSideset_iter_" << i << ".e"; + fixture.write_results(fname.str()); + ASSERT_TRUE(false); + } + + krino_mesh = std::make_shared(mesh, krino_mesh); + } +} + +typedef CompleteDecompositionFixture > CDMeshTests3DLSPerInterface; +TEST_F(CDMeshTests3DLSPerInterface, OneTet4_CutBasedOnNearestEdgeCut) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size > 1) return; + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + aux_meta.set_assert_32bit_flag(); + aux_meta.clear_force_64bit_flag(); + + // Use a large snap tolerance to make snapping more common since it is a frequent source + // of parallel bugs + cdfemSupport.set_cdfem_edge_tol(0.001); + cdfemSupport.set_simplex_generation_method(CUT_QUADS_BY_NEAREST_EDGE_CUT); + + setup_ls_field(); + + const stk::topology tet4 = stk::topology::TETRAHEDRON_4; + auto & block1_part = declare_input_block("block_1", tet4); + + register_ls_on_blocks({&block1_part}); + + commit(); + + build_one_tet4_mesh(*this, block1_part, parallel_rank, parallel_size); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + const std::array nodes = {{ mesh.get_entity(stk::topology::NODE_RANK, 1), mesh.get_entity(stk::topology::NODE_RANK, 2), mesh.get_entity(stk::topology::NODE_RANK, 3), mesh.get_entity(stk::topology::NODE_RANK, 4) }}; + const auto & ls_isovars = ls_policy.ls_isovars; + *field_data(ls_isovars[0], nodes[0]) = -1; + *field_data(ls_isovars[0], nodes[1]) = 1.; + *field_data(ls_isovars[0], nodes[2]) = 2.; + *field_data(ls_isovars[0], nodes[3]) = 3.; + + commit(); + decompose_mesh(); + + + EXPECT_TRUE(check_induced_parts(mesh)); + EXPECT_TRUE(check_face_and_edge_ownership(mesh)); + EXPECT_TRUE(check_face_and_edge_relations(mesh)); + EXPECT_TRUE(check_shared_entity_nodes(mesh)); + EXPECT_TRUE(krino_mesh->check_element_side_parts()); + + EXPECT_EQ(1u+1u, mesh.num_elements(nodes[0])); + EXPECT_EQ(1u+1u, mesh.num_elements(nodes[1])); + EXPECT_EQ(2u+1u, mesh.num_elements(nodes[2])); + EXPECT_EQ(3u+1u, mesh.num_elements(nodes[3])); + + // regression test + const ScaledJacobianQualityMetric qualityMetric; + const double quality = determine_quality(mesh, krino_mesh->get_active_part(), qualityMetric); + const double goldQuality = 0.21; + EXPECT_GT(quality, goldQuality); +} + +typedef CompleteDecompositionFixture > CDMeshTests3DLSPerPhase; +TEST_F(CDMeshTests3DLSPerPhase, Random_TwoTet4_InternalSideset) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size > 2) return; + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + // Use a large snap tolerance to make snapping more common since it is a frequent source + // of parallel bugs + cdfemSupport.set_cdfem_edge_tol(0.1); + cdfemSupport.set_simplex_generation_method(CUT_QUADS_BY_GLOBAL_IDENTIFIER); + + setup_ls_field(); + + const stk::topology tet4 = stk::topology::TETRAHEDRON_4; + auto & block1_part = declare_input_block("block_1", tet4); + auto & block2_part = declare_input_block("block_2", tet4); + auto & surface_part = declare_input_surface("surface_1", tet4.side_topology(), {block1_part.mesh_meta_data_ordinal(), block2_part.mesh_meta_data_ordinal()}); + + register_ls_on_blocks({&block1_part, &block2_part}); + + commit(); + + build_two_tet4_mesh_np2(*this, block1_part, block2_part, surface_part, parallel_rank, parallel_size); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + const auto & ls_isovars = ls_policy.ls_isovars; + std::vector sync_fields = {&coord_field.field()}; + for(auto && isovar : ls_isovars) sync_fields.push_back(&isovar.field()); + + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution dist(-1., 1.); +#ifdef NDEBUG + const int num_cases = 5000; +#else + const int num_cases = 1000; +#endif + for(int i=0; i < num_cases; ++i) + { + if (i%1000 == 0) std::cout << "Testing random configuration " << i << std::endl; + + for(auto && isovar : ls_isovars) + { + randomize_ls_field(mesh, isovar, mt, dist); + } + stk::mesh::communicate_field_data(mesh, sync_fields); + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << log.get_log() << std::endl; + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + EXPECT_TRUE(check_induced_parts(mesh)); + EXPECT_TRUE(check_face_and_edge_ownership(mesh)); + EXPECT_TRUE(check_face_and_edge_relations(mesh)); + EXPECT_TRUE(check_shared_entity_nodes(mesh)); + EXPECT_TRUE(krino_mesh->check_element_side_parts()); + + if(HasNonfatalFailure()) + { + std::cout << "Failure on iteration " << i << std::endl; + std::ostringstream fname; + fname << "Random_TwoTet4_InternalSideset_iter_" << i << ".e"; + fixture.write_results(fname.str()); + ASSERT_TRUE(false); + } + + krino_mesh = std::make_shared(mesh, krino_mesh); + } +} + +TEST_F(CDMeshTests3DLSPerPhase, Random_TwoTet4_InternalSideset_Snap) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size > 2) return; + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + cdfemSupport.set_cdfem_edge_degeneracy_handling(SNAP_TO_INTERFACE_WHEN_QUALITY_ALLOWS_THEN_SNAP_TO_NODE); + + setup_ls_field(); + + const stk::topology tet4 = stk::topology::TETRAHEDRON_4; + auto & block1_part = declare_input_block("block_1", tet4); + auto & block2_part = declare_input_block("block_2", tet4); + auto & surface_part = declare_input_surface("surface_1", tet4.side_topology(), {block1_part.mesh_meta_data_ordinal(), block2_part.mesh_meta_data_ordinal()}); + + register_ls_on_blocks({&block1_part, &block2_part}); + + commit(); + + build_two_tet4_mesh_np2(*this, block1_part, block2_part, surface_part, parallel_rank, parallel_size); + + stk::mesh::create_exposed_block_boundary_sides(mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + const auto & ls_isovars = ls_policy.ls_isovars; + std::vector sync_fields = {&coord_field.field()}; + for(auto && isovar : ls_isovars) sync_fields.push_back(&isovar.field()); + + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution dist(-1., 1.); +#ifdef NDEBUG + const int num_cases = 5000; +#else + const int num_cases = 1000; +#endif + for(int i=0; i < num_cases; ++i) + { + if (i%1000 == 0) std::cout << "Testing random configuration " << i << std::endl; + + MeshClone::stash_or_restore_mesh(mesh, 0); // restore original uncut mesh + commit(); // new krino_mesh each time + + for(auto && isovar : ls_isovars) + { + randomize_ls_field(mesh, isovar, mt, dist); + } + stk::mesh::communicate_field_data(mesh, sync_fields); + + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << log.get_log() << std::endl; + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + EXPECT_TRUE(check_induced_parts(mesh)); + EXPECT_TRUE(check_face_and_edge_ownership(mesh)); + EXPECT_TRUE(check_face_and_edge_relations(mesh)); + EXPECT_TRUE(check_shared_entity_nodes(mesh)); + EXPECT_TRUE(krino_mesh->check_element_side_parts()); + + if(HasNonfatalFailure()) + { + std::cout << "Failure on iteration " << i << std::endl; + std::ostringstream fname; + fname << "Random_TwoTet4_InternalSideset_iter_" << i << ".e"; + fixture.write_results(fname.str()); + ASSERT_TRUE(false); + } + + krino_mesh = std::make_shared(mesh, krino_mesh); + } +} + +TEST_F(CDMeshTests3DLSPerPhase, RestoreAfterFailedStep) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + + if (parallel_size > 2) return; + + stk::mesh::BulkData & mesh = fixture.bulk_data(); + stk::mesh::MetaData & meta = fixture.meta_data(); + AuxMetaData & aux_meta = AuxMetaData::get(meta); + + // Use a large snap tolerance to make snapping more common since it is a frequent source + // of parallel bugs + cdfemSupport.set_cdfem_edge_tol(0.1); + cdfemSupport.set_simplex_generation_method(CUT_QUADS_BY_GLOBAL_IDENTIFIER); + + setup_ls_field(); + + const stk::topology tet4 = stk::topology::TETRAHEDRON_4; + auto & block1_part = declare_input_block("block_1", tet4); + auto & block2_part = declare_input_block("block_2", tet4); + auto & surface_part = declare_input_surface("surface_1", + tet4.side_topology(), + {block1_part.mesh_meta_data_ordinal(), block2_part.mesh_meta_data_ordinal()}); + + register_ls_on_blocks({&block1_part, &block2_part}); + + commit(); + + build_two_tet4_mesh_np2( + *this, block1_part, block2_part, surface_part, parallel_rank, parallel_size); + + stk::mesh::create_exposed_block_boundary_sides( + mesh, meta.universal_part(), {&aux_meta.exposed_boundary_part()}); + + const auto & ls_isovars = ls_policy.ls_isovars; + std::vector sync_fields = {&coord_field.field()}; + for (auto && isovar : ls_isovars) + sync_fields.push_back(&isovar.field()); + + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution dist(-1., 1.); + for (int i = 0; i < 100; ++i) + { + if (i % 1000 == 0) std::cout << "Testing random configuration " << i << std::endl; + + // We want to test restoring the mesh after failed time steps for a variety of + // random decompositions: + // 1) Do a random decomposition and stash the mesh to act like a successful time + // step. + // 2) Do a second random decomposition to simulate the changes of a time step that + // will fail. + // 3) Restore the mesh to the stashed mesh, and rebuild the CDMesh, confirm that + // does not fail. + + auto randomize_all_fields = [&]() + { + for (auto && isovar : ls_isovars) + { + randomize_ls_field(mesh, isovar, mt, dist); + } + stk::mesh::communicate_field_data(mesh, sync_fields); + }; + + auto run_decomp = [&]() + { + try + { + decompose_mesh(); + } + catch (const std::exception & exception) + { + std::cout << log.get_log() << std::endl; + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + }; + // Step 1. + randomize_all_fields(); + run_decomp(); + krino::MeshClone::stash_or_restore_mesh(mesh, i); + krino_mesh = std::make_shared(mesh, krino_mesh); + + // Step 2. + randomize_all_fields(); + run_decomp(); + krino::MeshClone::stash_or_restore_mesh(mesh, i); + + // Step 3. + ASSERT_NO_THROW(krino_mesh->rebuild_after_rebalance()); + krino_mesh = std::make_shared(mesh, krino_mesh); + } +} + +TEST_F(CDMeshTests3D, Rebalance_with_rcb) +{ + run_rebalance_with("rcb"); +} + +TEST_F(CDMeshTests3D, Rebalance_with_parmetis) +{ + run_rebalance_with("parmetis"); +} + +typedef CompleteDecompositionFixture NonconformalAdaptivityTest; +TEST_F(NonconformalAdaptivityTest, InternalSidePositivePermutationNonOwnedElement) +{ + /* This tests that percept can correctly handle an internal side where the element + * with the same owning proc as the side has a negative permutation and the second + * connected element (with a different owning processor) has a positive permutation. + * In serial it just tests use of the adaptivity interface. + */ + + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + stk::mesh::BulkData & mesh = fixture.bulk_data(); + + if (parallel_size > 2) return; + + cdfemSupport.set_cdfem_edge_tol(0.1); + cdfemSupport.set_simplex_generation_method(CUT_QUADS_BY_GLOBAL_IDENTIFIER); + + setup_ls_field(); + + const stk::topology tet4 = stk::topology::TETRAHEDRON_4; + auto & block1_part = declare_input_block("block_1", tet4); + auto & block2_part = declare_input_block("block_2", tet4); + auto & surface_part = declare_input_surface("surface_1", tet4.side_topology(), {block1_part.mesh_meta_data_ordinal(), block2_part.mesh_meta_data_ordinal()}); + + register_ls_on_blocks({&block1_part, &block2_part}); + + auto & aux_meta = AuxMetaData::get(fixture.meta_data()); + FieldRef marker_field = + aux_meta.register_field("refine_marker", FieldType::INTEGER, stk::topology::ELEMENT_RANK, + 1u, 1, fixture.meta_data().universal_part()); + + auto & meta = fixture.meta_data(); + auto & active_part = aux_meta.active_part(); + stk::diag::TimerSet enabledTimerSet(0); + stk::diag::Timer root_timer = createRootTimer("test", enabledTimerSet); + HAdapt::setup(meta, active_part, root_timer); + + commit(); + + const bool build_all_on_P0 = false; + build_two_tet4_mesh_np2(*this, block1_part, block2_part, surface_part, parallel_rank, + parallel_size, true, build_all_on_P0); + + auto elem_1 = mesh.get_entity(stk::topology::ELEMENT_RANK, 1u); + int & elem_1_marker = *field_data(marker_field, elem_1); + elem_1_marker = 1; + + if(parallel_size == 2) + { + stk::mesh::EntityProcVec changes; + if(parallel_rank == 0) + { + changes.push_back(std::make_pair(mesh.get_entity(stk::topology::FACE_RANK, 7), 1)); + } + mesh.change_entity_owner(changes); + } + + EXPECT_NO_THROW(HAdapt::do_adaptive_refinement(meta, marker_field.name())); +} + +typedef CompleteDecompositionFixture MeshCloneTest; +TEST_F(MeshCloneTest, FaceOwnershipAndPartChangeBetweenClones) +{ + /* + * This test regression tests a bug where a shared face both changed parts and + * parallel owners between calls to MeshClone::stash_or_restore_mesh(), leading to a + * throw when copying field data to the clone mesh because the parts of the face were not + * updated on the clone. + */ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + const int parallel_rank = stk::parallel_machine_rank(pm); + stk::mesh::BulkData & mesh = fixture.bulk_data(); + + if (parallel_size != 2) return; + + const stk::topology tet4 = stk::topology::TETRAHEDRON_4; + auto & block1_part = declare_input_block("block_1", tet4); + auto & block2_part = declare_input_block("block_2", tet4); + auto & surface_1_part = declare_input_surface("surface_1", tet4.side_topology(), {block1_part.mesh_meta_data_ordinal(), block2_part.mesh_meta_data_ordinal()}); + auto & surface_2_part = declare_input_surface("surface_2", tet4.side_topology(), {block1_part.mesh_meta_data_ordinal(), block2_part.mesh_meta_data_ordinal()}); + + auto & aux_meta = AuxMetaData::get(fixture.meta_data()); + aux_meta.register_field("side_field", FieldType::REAL, stk::topology::FACE_RANK, + 1u, 1u, surface_1_part); + + commit(); + + build_two_tet4_mesh_np2(*this, block1_part, block2_part, surface_1_part, parallel_rank, parallel_size); + + // Stash the initial mesh. + ASSERT_NO_THROW(MeshClone::stash_or_restore_mesh(mesh, 0)); + + // Change the parallel owner of the shared face, then change its surface part from surface_1 to + // surface_2. + const auto side_1 = mesh.get_entity(stk::topology::FACE_RANK, 7); + const auto current_owner = mesh.parallel_owner_rank(side_1); + stk::mesh::EntityProcVec owner_changes; + if(parallel_rank == current_owner) + { + owner_changes.emplace_back(side_1, (current_owner + 1) % 2); + } + mesh.change_entity_owner(owner_changes); + ASSERT_TRUE(mesh.is_valid(side_1)); + const auto new_owner = mesh.parallel_owner_rank(side_1); + ASSERT_NE(new_owner, current_owner); + + mesh.modification_begin(); + if(new_owner == parallel_rank) + { + mesh.change_entity_parts(side_1, stk::mesh::ConstPartVector{&surface_2_part}, stk::mesh::ConstPartVector{&surface_1_part}); + } + mesh.modification_end(); + + // Confirm that updating the stash mesh succeeds + try + { + MeshClone::stash_or_restore_mesh(mesh, 1); + } + catch (const std::exception & exception) + { + std::cout << "Decomposing mesh failed with exception:\n"; + std::cout << exception.what() << "\n"; + ASSERT_TRUE(false); + } + + // Change the shared face part back to surface_1, then restore from the stash and + // confirm that the parts of the face are correct. + mesh.modification_begin(); + if(new_owner == parallel_rank) + { + mesh.change_entity_parts(side_1, stk::mesh::ConstPartVector{&surface_1_part}, stk::mesh::ConstPartVector{&surface_2_part}); + } + mesh.modification_end(); + ASSERT_TRUE(mesh.bucket(side_1).member(surface_1_part)); + MeshClone::stash_or_restore_mesh(mesh, 1); + const auto side_1_new = mesh.get_entity(stk::topology::FACE_RANK, 7); + EXPECT_TRUE(mesh.bucket(side_1_new).member(surface_2_part)); + EXPECT_FALSE(mesh.bucket(side_1_new).member(surface_1_part)); +} + +} diff --git a/packages/krino/krino/unit_tests/Akri_Unit_ContourElement.cpp b/packages/krino/krino/unit_tests/Akri_Unit_ContourElement.cpp new file mode 100644 index 000000000000..372f6cdbb820 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_ContourElement.cpp @@ -0,0 +1,83 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include + +#include + +namespace krino { + +TEST(SingleElementFixture, LS_Element_Tet4) +{ + stk::topology tet4 = stk::topology::TETRAHEDRON_4; + SingleElementFixture test_fixture(tet4); + + test_fixture.generate_mesh(); + stk::mesh::MetaData & meta = test_fixture.stk_fixture.meta_data(); + stk::mesh::BulkData & bulk = test_fixture.stk_fixture.bulk_data(); + + const unsigned ndim = meta.spatial_dimension(); + const stk::mesh::FieldBase & coords_field = test_fixture.coord_field.field(); + const stk::mesh::FieldBase & scalar_field = test_fixture.scalar_field.field(); + + stk::mesh::Entity elem = test_fixture.my_elem; + const stk::mesh::Entity* elem_nodes = bulk.begin_nodes(elem); + const unsigned npe = bulk.num_nodes(elem); + + // Set coordinates + const double coords[4][3] = {{0.,0.,0.},{1.,0.,0.},{0.,1.,0.},{0.,0.,4.}}; + for (unsigned node=0; node(stk::mesh::field_data(coords_field, elem_nodes[node])); + for (unsigned dim=0; dim(stk::mesh::field_data(scalar_field, elem_nodes[node])); + *scalar_data = isovar[node]; + } + + // + // Create facets on 0 isosurface + // + + const double isoval = 0.0; + krino::ContourElement ls_elem( bulk, elem, coords_field, scalar_field, isoval ); + const double length_scale = 1.0; // Used for snapping facets to vertices of element when distance is small compared to length_scale + ls_elem.compute_subelement_decomposition(length_scale); + + Faceted_Surface faceted_surface("tmp"); + ls_elem.build_subelement_facets(faceted_surface); + const auto & facets = faceted_surface.get_facets(); + + ASSERT_EQ(1u, facets.size()); + + Facet & facet = *facets[0]; + + EXPECT_EQ(2.0, facet.facet_vertex(0)[2]); + EXPECT_EQ(2.0, facet.facet_vertex(1)[2]); + EXPECT_EQ(2.0, facet.facet_vertex(2)[2]); + + const Vector3d normal = facet.facet_normal(); + const double area = facet.facet_area(); + + EXPECT_EQ(0.0, normal[0]); + EXPECT_EQ(0.0, normal[1]); + EXPECT_EQ(1.0, normal[2]); + + EXPECT_EQ(0.125, area); +} + +} diff --git a/packages/krino/krino/unit_tests/Akri_Unit_Element.cpp b/packages/krino/krino/unit_tests/Akri_Unit_Element.cpp new file mode 100644 index 000000000000..d799a4402563 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_Element.cpp @@ -0,0 +1,718 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino { + +template +class Mesh_Element_Fixture : public ::testing::Test +{ +public: + Mesh_Element_Fixture() : + elem_fixture(static_cast(TOPO)), + krino_mesh(elem_fixture.stk_fixture.bulk_data(), std::shared_ptr()), + interfaceGeometry(krino_mesh.get_active_part(),krino_mesh.get_cdfem_support(), krino_mesh.get_phase_support()) + { + elem_fixture.generate_mesh(); + check_entity_counts(); + Phase_Support::get(stk_meta()).add_decomposed_part(stk_meta().universal_part()); + Phase_Support::set_one_levelset_per_phase(false); + const NodeToCapturedDomainsMap nodesToCapturedDomains; + interfaceGeometry.prepare_to_process_elements(krino_mesh.stk_bulk(), nodesToCapturedDomains); + } + virtual ~Mesh_Element_Fixture() {}; + void check_entity_counts() + { + std::vector entities; + stk::mesh::get_entities(stk_bulk(), stk::topology::ELEMENT_RANK, entities); + ASSERT_EQ(1u, entities.size()); + stk::mesh::get_entities(stk_bulk(), stk::topology::NODE_RANK, entities); + ASSERT_EQ(elem_fixture.my_topology.num_nodes(), entities.size()); + } + stk::mesh::BulkData & stk_bulk() { return elem_fixture.stk_fixture.bulk_data(); } + stk::mesh::MetaData & stk_meta() { return elem_fixture.stk_fixture.meta_data(); } + stk::mesh::Entity elem() { return elem_fixture.my_elem; } + + unsigned node_ordinal(const stk::mesh::Entity node) + { + const stk::mesh::Entity * const elem_nodes = stk_bulk().begin_nodes(elem()); + const unsigned num_nodes = stk_bulk().num_nodes(elem()); + const stk::mesh::Entity * found = std::find(elem_nodes, elem_nodes+num_nodes, node); + return found - elem_nodes; + } + + void generate_nonconformal_elements() { + krino_mesh.generate_nonconformal_elements(); + } + void triangulate() + { + Mesh_Element & meshElem = get_mesh_element(); + meshElem.create_cutter(krino_mesh, interfaceGeometry); // update cutter with for edges that now have crossings found + get_mesh_element().create_cutter(krino_mesh, interfaceGeometry); + krino_mesh.triangulate(interfaceGeometry); + } + + LevelSetElementCutter & get_cutter() + { + Mesh_Element & meshElem = get_mesh_element(); + meshElem.create_cutter(krino_mesh, interfaceGeometry); + LevelSetElementCutter * cutter = dynamic_cast(meshElem.get_cutter()); + ThrowRequire(cutter); + return *cutter; + } + + void find_edge_crossings(const std::vector & node_LS_values) + { + std::vector > nodesIsovar(2); + LevelSetElementCutter & cutter = get_cutter(); + for (unsigned iEdge=0; iEdge > & node_LS_values) + { + std::vector > nodesIsovar(2); + LevelSetElementCutter & cutter = get_cutter(); + for (unsigned iEdge=0; iEdge & node_LS_values) + { + generate_nonconformal_elements(); + find_edge_crossings(node_LS_values); + triangulate(); + } + + void generate_mesh_element_and_cutter(const std::vector > & node_LS_values) + { + generate_nonconformal_elements(); + find_edge_crossings(node_LS_values); + triangulate(); + } + + Mesh_Element & get_mesh_element() { + return *krino_mesh.elements.front(); + } + + std::array get_edge_nodes(const unsigned edgeOrdinal) + { + const auto & nodes = get_mesh_element().get_nodes(); + const unsigned * edge_node_ordinals = get_edge_node_ordinals(elem_fixture.my_topology, edgeOrdinal); + return {{nodes[edge_node_ordinals[0]], nodes[edge_node_ordinals[1]]}}; + } + + Segment3d get_edge_segment(const unsigned edge_ord) + { + const auto & edgeNodes = get_edge_nodes(edge_ord); + return Segment3d(get_mesh_element().get_node_parametric_coords(edgeNodes[0]), + get_mesh_element().get_node_parametric_coords(edgeNodes[1])); + } + + SingleElementFixture elem_fixture; + CDMesh krino_mesh; + LevelSetInterfaceGeometry interfaceGeometry; +}; + +typedef Mesh_Element_Fixture Mesh_Element_Tri3; +TEST_F(Mesh_Element_Tri3, generate) +{ + generate_nonconformal_elements(); + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_EQ(elem(), mesh_elem.entity()); + const NodeVec & mesh_nodes = mesh_elem.get_nodes(); + ASSERT_EQ(3u, mesh_nodes.size()); + const stk::mesh::Entity * const elem_nodes = stk_bulk().begin_nodes(elem()); + for(unsigned i=0; i < mesh_nodes.size(); ++i) + { + EXPECT_EQ(mesh_nodes[i]->entity(), elem_nodes[i]); + } +} + +typedef Mesh_Element_Tri3 Mesh_Element_Tri3_One_LS; +TEST_F(Mesh_Element_Tri3_One_LS, All_Nodes_Positive) +{ + const InterfaceID iface(0,0); + krino_mesh.add_interface_id(iface); + + const std::vector node_LS_values(3, 1.); + + generate_mesh_element_and_cutter(node_LS_values); + krino_mesh.determine_node_signs(iface); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_FALSE(mesh_elem.have_interface()); + + for(unsigned i=0; i < mesh_elem.topology().num_edges(); ++i) + { + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(InterfaceID(0,0), get_edge_segment(i))); + } +} + +TEST_F(Mesh_Element_Tri3_One_LS, All_Nodes_Negative) +{ + const InterfaceID iface(0,0); + krino_mesh.add_interface_id(iface); + + const std::vector node_LS_values(3, -1.); + + generate_mesh_element_and_cutter(node_LS_values); + krino_mesh.determine_node_signs(iface); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_FALSE(mesh_elem.have_interface()); + + for(unsigned i=0; i < mesh_elem.topology().num_edges(); ++i) + { + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(InterfaceID(0,0), get_edge_segment(i))); + } +} + +TEST_F(Mesh_Element_Tri3_One_LS, Node0_Pos_Node1_2_Neg) +{ + const InterfaceID iface(0,0); + krino_mesh.add_interface_id(iface); + + std::vector node_LS_values(3); + node_LS_values[0] = 1.; + node_LS_values[1] = -0.5; + node_LS_values[2] = -0.5; + + generate_mesh_element_and_cutter(node_LS_values); + krino_mesh.determine_node_signs(iface); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_TRUE(mesh_elem.have_interface()); + const NodeVec & mesh_nodes = mesh_elem.get_nodes(); + EXPECT_EQ(3u, mesh_nodes.size()); + EXPECT_EQ(+1, mesh_nodes[0]->get_node_sign()); + EXPECT_EQ(-1, mesh_nodes[1]->get_node_sign()); + EXPECT_EQ(-1, mesh_nodes[2]->get_node_sign()); + + // NOTE: Edge 0 is from nodes 0->1, Edge 1 is 1->2, Edge 2 is 2->0 + EXPECT_DOUBLE_EQ(2./3., mesh_elem.interface_crossing_position(iface, get_edge_segment(0))); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface, get_edge_segment(1))); + EXPECT_DOUBLE_EQ(1./3., mesh_elem.interface_crossing_position(iface, get_edge_segment(2))); +} + +TEST_F(Mesh_Element_Tri3_One_LS, Node0_Snapped_Node1_2_Pos) +{ + const InterfaceID iface(0,0); + krino_mesh.add_interface_id(iface); + + std::vector node_LS_values(3); + node_LS_values[0] = 0; + node_LS_values[1] = 1.; + node_LS_values[2] = 1.; + + generate_mesh_element_and_cutter(node_LS_values); + krino_mesh.determine_node_signs(iface); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_FALSE(mesh_elem.have_interface()); + + // NOTE: Edge 0 is from nodes 0->1, Edge 1 is 1->2, Edge 2 is 2->0 + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface, get_edge_segment(0))); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface, get_edge_segment(1))); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface, get_edge_segment(2))); +} + +TEST_F(Mesh_Element_Tri3_One_LS, Node0_Snapped_Node1_2_Neg) +{ + const InterfaceID iface(0,0); + krino_mesh.add_interface_id(iface); + + std::vector node_LS_values(3); + node_LS_values[0] = 0; + node_LS_values[1] = -1.; + node_LS_values[2] = -1.; + + generate_mesh_element_and_cutter(node_LS_values); + krino_mesh.determine_node_signs(iface); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_TRUE(mesh_elem.have_interface()); + const NodeVec & mesh_nodes = mesh_elem.get_nodes(); + EXPECT_EQ(3u, mesh_nodes.size()); + EXPECT_EQ( 0, mesh_nodes[0]->get_node_sign()); + EXPECT_EQ(-1, mesh_nodes[1]->get_node_sign()); + EXPECT_EQ(-1, mesh_nodes[2]->get_node_sign()); + + // NOTE: Edge 0 is from nodes 0->1, Edge 1 is 1->2, Edge 2 is 2->0 + // NOTE: Because we snap-away from nodes, crossing will be epsilon away from nodes. + EXPECT_NEAR(0., mesh_elem.interface_crossing_position(iface, get_edge_segment(0)), std::numeric_limits::epsilon()); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface, get_edge_segment(1))); + EXPECT_NEAR(1., mesh_elem.interface_crossing_position(iface, get_edge_segment(2)), std::numeric_limits::epsilon()); +} + +TEST_F(Mesh_Element_Tri3_One_LS, Node0_Snapped_Node1_Pos_Node_2_Neg) +{ + const InterfaceID iface(0,0); + krino_mesh.add_interface_id(iface); + + std::vector node_LS_values(3); + node_LS_values[0] = 0; + // Crossing position 0.608351703529745 for edge 1 + // gives a test case that fails to intersect exactly with node 0 + node_LS_values[1] = 1.; + node_LS_values[2] = -(1/0.60835170352974499152765019971412 - 1.); + + generate_mesh_element_and_cutter(node_LS_values); + krino_mesh.determine_node_signs(iface); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_TRUE(mesh_elem.have_interface()); + const NodeVec & mesh_nodes = mesh_elem.get_nodes(); + EXPECT_EQ(3u, mesh_nodes.size()); + EXPECT_EQ( 0, mesh_nodes[0]->get_node_sign()); + EXPECT_EQ(+1, mesh_nodes[1]->get_node_sign()); + EXPECT_EQ(-1, mesh_nodes[2]->get_node_sign()); + + // NOTE: Edge 0 is from nodes 0->1, Edge 1 is 1->2, Edge 2 is 2->0 + // NOTE: Because we snap-away from nodes, crossing will be epsilon away from nodes. + EXPECT_DOUBLE_EQ(node_LS_values[1]/(node_LS_values[1] - node_LS_values[2]), mesh_elem.interface_crossing_position(iface, get_edge_segment(1))); + EXPECT_NEAR(1., mesh_elem.interface_crossing_position(iface, get_edge_segment(2)), std::numeric_limits::epsilon()); +} + +TEST_F(Mesh_Element_Tri3_One_LS, Node0_1_Snapped_Node2_Pos) +{ + const InterfaceID iface(0,0); + krino_mesh.add_interface_id(iface); + + std::vector node_LS_values(3); + node_LS_values[0] = 0; + node_LS_values[1] = 0; + node_LS_values[2] = 1.; + + generate_mesh_element_and_cutter(node_LS_values); + krino_mesh.determine_node_signs(iface); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_FALSE(mesh_elem.have_interface()); + + // NOTE: Edge 0 is from nodes 0->1, Edge 1 is 1->2, Edge 2 is 2->0 + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface, get_edge_segment(0))); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface, get_edge_segment(1))); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface, get_edge_segment(2))); +} + +TEST_F(Mesh_Element_Tri3_One_LS, Node0_1_Snapped_Node2_Neg) +{ + const InterfaceID iface(0,0); + krino_mesh.add_interface_id(iface); + + std::vector node_LS_values(3); + node_LS_values[0] = 0; + node_LS_values[1] = 0; + node_LS_values[2] = -1.; + + generate_mesh_element_and_cutter(node_LS_values); + krino_mesh.determine_node_signs(iface); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_TRUE(mesh_elem.have_interface()); + const NodeVec & mesh_nodes = mesh_elem.get_nodes(); + EXPECT_EQ(3u, mesh_nodes.size()); + EXPECT_EQ( 0, mesh_nodes[0]->get_node_sign()); + EXPECT_EQ( 0, mesh_nodes[1]->get_node_sign()); + EXPECT_EQ(-1, mesh_nodes[2]->get_node_sign()); + + // NOTE: Edge 0 is from nodes 0->1, Edge 1 is 1->2, Edge 2 is 2->0 + // TODO: What should intersections be for snapped edge? + // I don't think we should ever be looking for the interface crossing position of it + // NOTE: Because we snap-away from nodes, crossing will be epsilon away from nodes. + EXPECT_NEAR(0., mesh_elem.interface_crossing_position(iface, get_edge_segment(1)), std::numeric_limits::epsilon()); + EXPECT_NEAR(1., mesh_elem.interface_crossing_position(iface, get_edge_segment(2)), std::numeric_limits::epsilon()); +} + +typedef Mesh_Element_Tri3 Mesh_Element_Tri3_Three_LS; +TEST_F(Mesh_Element_Tri3_Three_LS, All_Phase2_Unsnapped) +{ + const LS_Field ls1("LS1", LevelSet_Identifier(1)); + const LS_Field ls2("LS2", LevelSet_Identifier(2)); + const LS_Field ls3("LS3", LevelSet_Identifier(3)); + CDFEM_Support & cdfem_supp = CDFEM_Support::get(elem_fixture.stk_fixture.meta_data()); + cdfem_supp.add_ls_field(ls1); + cdfem_supp.add_ls_field(ls2); + cdfem_supp.add_ls_field(ls3); + const InterfaceID iface01(0,1); + const InterfaceID iface02(0,2); + const InterfaceID iface12(1,2); + krino_mesh.add_interface_id(iface01); + krino_mesh.add_interface_id(iface02); + krino_mesh.add_interface_id(iface12); + Phase_Support::set_one_levelset_per_phase(true); + + std::vector > node_LS_values(3); + node_LS_values[0].resize(3); + node_LS_values[1].resize(3); + node_LS_values[2].resize(3); + // LS 0 + node_LS_values[0][0] = 1; + node_LS_values[1][0] = 1; + node_LS_values[2][0] = 1; + // LS 1 + node_LS_values[0][1] = 1; + node_LS_values[1][1] = 1; + node_LS_values[2][1] = 1; + // LS 2 + node_LS_values[0][2] = -1; + node_LS_values[1][2] = -1; + node_LS_values[2][2] = -1; + + generate_mesh_element_and_cutter(node_LS_values); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_FALSE(mesh_elem.have_interface()); +} + +TEST_F(Mesh_Element_Tri3_Three_LS, One_Interface) +{ + const LS_Field ls1("LS1", LevelSet_Identifier(1)); + const LS_Field ls2("LS2", LevelSet_Identifier(2)); + const LS_Field ls3("LS3", LevelSet_Identifier(3)); + CDFEM_Support & cdfem_supp = CDFEM_Support::get(elem_fixture.stk_fixture.meta_data()); + cdfem_supp.add_ls_field(ls1); + cdfem_supp.add_ls_field(ls2); + cdfem_supp.add_ls_field(ls3); + const InterfaceID iface01(0,1); + const InterfaceID iface02(0,2); + const InterfaceID iface12(1,2); + krino_mesh.add_interface_id(iface01); + krino_mesh.add_interface_id(iface02); + krino_mesh.add_interface_id(iface12); + Phase_Support::set_one_levelset_per_phase(true); + + std::vector > node_LS_values(3); + node_LS_values[0].resize(3); + node_LS_values[1].resize(3); + node_LS_values[2].resize(3); + // LS 0 + node_LS_values[0][0] = 0.038335; + node_LS_values[1][0] = 0.014437; + node_LS_values[2][0] = -0.01288; + // LS 1 + node_LS_values[0][1] = 0.037250; + node_LS_values[1][1] = 0.070753; + node_LS_values[2][1] = 0.021996; + // LS 2 + node_LS_values[0][2] = 0.01; + node_LS_values[1][2] = 0.01; + node_LS_values[2][2] = 0.01; + + generate_mesh_element_and_cutter(node_LS_values); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_TRUE(mesh_elem.have_interface()); + const NodeVec & mesh_nodes = mesh_elem.get_nodes(); + EXPECT_EQ(3u, mesh_nodes.size()); + + const Vector3d node0_coords = mesh_nodes[0]->owner_coords(&mesh_elem); + const Vector3d node1_coords = mesh_nodes[1]->owner_coords(&mesh_elem); + const Vector3d node2_coords = mesh_nodes[2]->owner_coords(&mesh_elem); + const Segment3d edge0(node0_coords, node1_coords); + const Segment3d edge1(node1_coords, node2_coords); + const Segment3d edge2(node2_coords, node0_coords); + + krino_mesh.determine_node_signs(iface01); + + if (mesh_elem.have_interface(iface01)) + { + EXPECT_EQ(+1, mesh_nodes[0]->get_node_sign()); + EXPECT_EQ(+1, mesh_nodes[1]->get_node_sign()); + EXPECT_EQ(+1, mesh_nodes[2]->get_node_sign()); + } + + krino_mesh.determine_node_signs(iface02); + + EXPECT_TRUE(mesh_elem.have_interface(iface02)); + EXPECT_EQ(+1, mesh_nodes[0]->get_node_sign()); + EXPECT_EQ(+1, mesh_nodes[1]->get_node_sign()); + EXPECT_EQ(-1, mesh_nodes[2]->get_node_sign()); + + krino_mesh.determine_node_signs(iface12); + + if (mesh_elem.have_interface(iface12)) + { + EXPECT_EQ(+1, mesh_nodes[0]->get_node_sign()); + EXPECT_EQ(+1, mesh_nodes[1]->get_node_sign()); + EXPECT_EQ(+1, mesh_nodes[2]->get_node_sign()); + } + + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface01, edge0)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface01, edge1)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface01, edge2)); + + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface12, edge0)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface12, edge1)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface12, edge2)); + + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface02, edge0)); +} + +TEST_F(Mesh_Element_Tri3_Three_LS, Handle_Hanging_Child) +{ + const LS_Field ls1("LS1", LevelSet_Identifier(1)); + const LS_Field ls2("LS2", LevelSet_Identifier(2)); + const LS_Field ls3("LS3", LevelSet_Identifier(3)); + CDFEM_Support & cdfem_supp = CDFEM_Support::get(elem_fixture.stk_fixture.meta_data()); + cdfem_supp.add_ls_field(ls1); + cdfem_supp.add_ls_field(ls2); + cdfem_supp.add_ls_field(ls3); + const InterfaceID iface01(0,1); + const InterfaceID iface02(0,2); + const InterfaceID iface12(1,2); + krino_mesh.add_interface_id(iface01); + krino_mesh.add_interface_id(iface02); + krino_mesh.add_interface_id(iface12); + Phase_Support::set_one_levelset_per_phase(true); + + std::vector > node_LS_values(3); + node_LS_values[0].resize(3); + node_LS_values[1].resize(3); + node_LS_values[2].resize(3); + // LS 0 + node_LS_values[0][0] = 0.04; + node_LS_values[1][0] = 0.015; + node_LS_values[2][0] = 0.025; + // LS 1 + node_LS_values[0][1] = 0.; + node_LS_values[1][1] = 0.02; + node_LS_values[2][1] = 0.02; + // LS 2 + node_LS_values[0][2] = 0.01; + node_LS_values[1][2] = 0.01; + node_LS_values[2][2] = 0.01; + + generate_mesh_element_and_cutter(node_LS_values); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_TRUE(mesh_elem.have_interface()); + const NodeVec & mesh_nodes = mesh_elem.get_nodes(); + EXPECT_EQ(3u, mesh_nodes.size()); + + const Vector3d node0_coords = mesh_nodes[0]->owner_coords(&mesh_elem); + const Vector3d node1_coords = mesh_nodes[1]->owner_coords(&mesh_elem); + const Vector3d node2_coords = mesh_nodes[2]->owner_coords(&mesh_elem); + const Segment3d edge0(node0_coords, node1_coords); + const Segment3d edge1(node1_coords, node2_coords); + const Segment3d edge2(node2_coords, node0_coords); + + krino_mesh.determine_node_signs(iface01); + + if (mesh_elem.have_interface(iface01)) + { + EXPECT_EQ(+1, mesh_nodes[0]->get_node_sign()); + EXPECT_EQ(+1, mesh_nodes[1]->get_node_sign()); + EXPECT_EQ(+1, mesh_nodes[2]->get_node_sign()); + } + + krino_mesh.determine_node_signs(iface02); + + if (mesh_elem.have_interface(iface02)) + { + EXPECT_EQ(+1, mesh_nodes[0]->get_node_sign()); + EXPECT_EQ(+1, mesh_nodes[1]->get_node_sign()); + EXPECT_EQ(+1, mesh_nodes[2]->get_node_sign()); + }; + + krino_mesh.determine_node_signs(iface12); + + EXPECT_TRUE(mesh_elem.have_interface(iface12)); + EXPECT_EQ(-1, mesh_nodes[0]->get_node_sign()); + EXPECT_EQ(+1, mesh_nodes[1]->get_node_sign()); + EXPECT_EQ(+1, mesh_nodes[2]->get_node_sign()); + + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface01, edge0)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface01, edge1)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface01, edge2)); + + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface02, edge0)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface02, edge1)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface02, edge2)); + + EXPECT_DOUBLE_EQ(0.5, mesh_elem.interface_crossing_position(iface12, edge0)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface12, edge1)); + EXPECT_DOUBLE_EQ(0.5, mesh_elem.interface_crossing_position(iface12, edge2)); +} + +TEST_F(Mesh_Element_Tri3_Three_LS, Zero_Crossings_For_Phases_Present_Bug) +{ + const LS_Field ls1("LS1", LevelSet_Identifier(1)); + const LS_Field ls2("LS2", LevelSet_Identifier(2)); + const LS_Field ls3("LS3", LevelSet_Identifier(3)); + CDFEM_Support & cdfem_supp = CDFEM_Support::get(elem_fixture.stk_fixture.meta_data()); + cdfem_supp.add_ls_field(ls1); + cdfem_supp.add_ls_field(ls2); + cdfem_supp.add_ls_field(ls3); + const InterfaceID iface01(0,1); + const InterfaceID iface02(0,2); + const InterfaceID iface12(1,2); + krino_mesh.add_interface_id(iface01); + krino_mesh.add_interface_id(iface02); + krino_mesh.add_interface_id(iface12); + Phase_Support::set_one_levelset_per_phase(true); + + std::vector > node_LS_values(4); + node_LS_values[0].resize(3); + node_LS_values[1].resize(3); + node_LS_values[2].resize(3); + node_LS_values[3].resize(3); + // Node 0 + node_LS_values[0][0] = -0.00765969; + node_LS_values[0][1] = 0.532721; + node_LS_values[0][2] = 0.; + // Node 1 + node_LS_values[1][0] = -0.000100754; + node_LS_values[1][1] = 0.; + node_LS_values[1][2] = 0.; + // Node 2 + node_LS_values[2][0] = -0.00666939; + node_LS_values[2][1] = 0.337202; + node_LS_values[2][2] = 0.; + // Node 3 (Mid-node for edge 1) + node_LS_values[3][0] = -2.76e-6; + node_LS_values[3][1] = -0.00614775; + node_LS_values[3][2] = 0.; + + generate_mesh_element_and_cutter(node_LS_values); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_FALSE(mesh_elem.have_interface()); + const NodeVec & mesh_nodes = mesh_elem.get_nodes(); + EXPECT_EQ(3u, mesh_nodes.size()); + + const Vector3d node0_coords = mesh_nodes[0]->owner_coords(&mesh_elem); + const Vector3d node1_coords = mesh_nodes[1]->owner_coords(&mesh_elem); + const Vector3d node2_coords = mesh_nodes[2]->owner_coords(&mesh_elem); + const Segment3d edge0(node0_coords, node1_coords); + const Segment3d edge1(node1_coords, node2_coords); + const Segment3d edge2(node2_coords, node0_coords); + + krino_mesh.determine_node_signs(iface01); + + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface01, edge0)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface01, edge1)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface01, edge2)); + + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface02, edge0)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface02, edge1)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface02, edge2)); + + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface12, edge0)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface12, edge1)); + EXPECT_ANY_THROW(mesh_elem.interface_crossing_position(iface12, edge2)); +} + +typedef Mesh_Element_Fixture Mesh_Element_Tet4; +TEST_F(Mesh_Element_Tet4, generate) +{ + generate_nonconformal_elements(); + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_EQ(elem(), mesh_elem.entity()); + const NodeVec & mesh_nodes = mesh_elem.get_nodes(); + ASSERT_EQ(4u, mesh_nodes.size()); + const stk::mesh::Entity * const elem_nodes = stk_bulk().begin_nodes(elem()); + for(unsigned i=0; i < mesh_nodes.size(); ++i) + { + EXPECT_EQ(mesh_nodes[i]->entity(), elem_nodes[i]); + } +} + +TEST_F(Mesh_Element_Tet4, OneInterfaceCheckNodeScore) +{ + const InterfaceID iface(0,0); + krino_mesh.add_interface_id(iface); + Phase_Support::set_one_levelset_per_phase(false); + + std::vector node_LS_values(4); + node_LS_values[0] = 1.; + node_LS_values[1] = -0.2; + node_LS_values[2] = -0.5; + node_LS_values[3] = -0.8; + + generate_mesh_element_and_cutter(node_LS_values); + krino_mesh.determine_node_signs(iface); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_TRUE(mesh_elem.have_interface()); + const NodeVec & mesh_nodes = mesh_elem.get_nodes(); + ASSERT_EQ(4u, mesh_nodes.size()); + EXPECT_EQ(+1, mesh_nodes[0]->get_node_sign()); + EXPECT_EQ(-1, mesh_nodes[1]->get_node_sign()); + EXPECT_EQ(-1, mesh_nodes[2]->get_node_sign()); + EXPECT_EQ(-1, mesh_nodes[3]->get_node_sign()); + + krino_mesh.decompose_edges(iface); + krino_mesh.determine_node_scores(iface); + + EXPECT_GT(mesh_nodes[0]->get_node_score(), mesh_nodes[3]->get_node_score()); + EXPECT_GT(mesh_nodes[3]->get_node_score(), mesh_nodes[2]->get_node_score()); + EXPECT_GT(mesh_nodes[2]->get_node_score(), mesh_nodes[1]->get_node_score()); +} + +TEST_F(Mesh_Element_Tet4, OneInterfaceCheckNodeScore_ScoreBasedOnAngleNotPosition) +{ + krino_mesh.get_cdfem_support().set_simplex_generation_method(CUT_QUADS_BY_LARGEST_ANGLE); + + const InterfaceID iface(0,0); + krino_mesh.add_interface_id(iface); + Phase_Support::set_one_levelset_per_phase(false); + + std::vector node_LS_values(4); + node_LS_values[0] = -1.0; + node_LS_values[1] = -1.01; + node_LS_values[2] = -1.02; + node_LS_values[3] = 1.0; + + generate_mesh_element_and_cutter(node_LS_values); + krino_mesh.determine_node_signs(iface); + + Mesh_Element & mesh_elem = get_mesh_element(); + EXPECT_TRUE(mesh_elem.have_interface()); + const NodeVec & mesh_nodes = mesh_elem.get_nodes(); + ASSERT_EQ(4u, mesh_nodes.size()); + EXPECT_EQ(-1, mesh_nodes[0]->get_node_sign()); + EXPECT_EQ(-1, mesh_nodes[1]->get_node_sign()); + EXPECT_EQ(-1, mesh_nodes[2]->get_node_sign()); + EXPECT_EQ(+1, mesh_nodes[3]->get_node_sign()); + + krino_mesh.decompose_edges(iface); + krino_mesh.determine_node_scores(iface); + + EXPECT_GT(mesh_nodes[0]->get_node_score(), mesh_nodes[1]->get_node_score()); + EXPECT_GT(mesh_nodes[0]->get_node_score(), mesh_nodes[2]->get_node_score()); + EXPECT_GT(mesh_nodes[2]->get_node_score(), mesh_nodes[1]->get_node_score()); +} + +} diff --git a/packages/krino/krino/unit_tests/Akri_Unit_Element_Cutter.cpp b/packages/krino/krino/unit_tests/Akri_Unit_Element_Cutter.cpp new file mode 100644 index 000000000000..b44c1914a3c8 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_Element_Cutter.cpp @@ -0,0 +1,188 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace krino +{ + +static std::function &)> +build_always_false_diagonal_picker() +{ + auto diagonalPicker = + [](const std::array & faceNodes) + { + return false; + }; + return diagonalPicker; +} + +static void build_simple_parent_edges(const stk::topology topology, + const std::vector & nodeIds, + const std::vector> & nodalIsovars, + ParentEdgeMap & parentEdges, + std::vector & elementParentEdges, + std::vector & areParentEdgesAreOrientedSameAsElementEdges) +{ + const unsigned numEdges = topology.num_edges(); + + elementParentEdges.clear(); + for(unsigned i=0; i < numEdges; ++i) + { + const unsigned * edgeLNN = get_edge_node_ordinals(topology, i); + const unsigned i0 = edgeLNN[0]; + const unsigned i1 = edgeLNN[1]; + const ParentEdgeKey edge_key(nodeIds[i0], nodeIds[i1]); + CDFEM_Parent_Edge & parentEdge = parentEdges[edge_key]; + + if(!parentEdge.valid()) + parentEdge = CDFEM_Parent_Edge({nodalIsovars[i0], nodalIsovars[i1]}); + + elementParentEdges.push_back(&parentEdge); + } + + areParentEdgesAreOrientedSameAsElementEdges.clear(); + areParentEdgesAreOrientedSameAsElementEdges.resize(numEdges, true); +} + +struct ElementWithCutter : public ::testing::Test +{ + ElementWithCutter() {} + + void build_parent_edges_and_cutter(const stk::topology topology, + const std::vector & nodeIds, + const std::vector > & nodalIsovars) + { + Phase_Support::set_one_levelset_per_phase(true); + const auto diagonalPicker = build_always_false_diagonal_picker(); + + const MasterElement & masterElem = MasterElementDeterminer::getMasterElement(topology); + + std::vector elementParentEdges; + std::vector areParentEdgesAreOrientedSameAsElementEdges; + + build_simple_parent_edges(topology, nodeIds, nodalIsovars, parentEdges, elementParentEdges, areParentEdgesAreOrientedSameAsElementEdges); + + cutter.reset( new One_LS_Per_Phase_Cutter(masterElem, elementParentEdges, areParentEdgesAreOrientedSameAsElementEdges, diagonalPicker) ); + } + + ParentEdgeMap parentEdges; + CDFEM_Snapper snapper; + std::unique_ptr cutter; +}; + +struct TriangleWithTriplePoint : public ElementWithCutter +{ + const stk::topology topology{stk::topology::TRIANGLE_3_2D}; + const std::vector > nodalIsovars{ {-1., 0., 0.}, {0., -1., 0.}, {1.,1.,0.} }; + const std::vector nodeIds{1,2,3}; + + const InterfaceID iface01{0,1}; + const InterfaceID iface02{0,2}; + const InterfaceID iface12{1,2}; +}; + +struct TriangleWithFakeTriplePoint : public ElementWithCutter +{ + const stk::topology topology{stk::topology::TRIANGLE_3_2D}; + const std::vector > nodalIsovars{ {2., 2.,-1., 0.}, {-1.,0.5, 2., 0.}, {0.5, -1., 2., 0.} }; + const std::vector nodeIds{1,2,3}; +}; + +TEST_F(TriangleWithTriplePoint, givenCutter_haveExpectedInterfaces) +{ + build_parent_edges_and_cutter(topology, nodeIds, nodalIsovars); + + std::vector interfacesWithCuttingSurface; + cutter->fill_interfaces_with_cutting_surface(interfacesWithCuttingSurface); + EXPECT_EQ(3u, interfacesWithCuttingSurface.size()); + + EXPECT_TRUE(cutter->have_cutting_surface(iface01)); + EXPECT_TRUE(cutter->have_cutting_surface(iface02)); + EXPECT_TRUE(cutter->have_cutting_surface(iface12)); +} + +static bool is_nearly_eq(const Vector3d & v0, const Vector3d & v1, const double relativeTol=1.e-6) +{ + const double absoluteTol = relativeTol * (v0.length() + v1.length()); + for (int i=0; i<3; ++i) + if (std::abs(v0[i]-v1[i]) > absoluteTol) return false; + return true; +} + +void expect_to_find_all_first_in_second(const std::vector & vec0, const std::vector & vec1, const std::string & errorMsg) +{ + for (auto && val0 : vec0) + { + bool found = false; + for (auto && val1 : vec1) + { + if (is_nearly_eq(val0.parametricCoords, val1.parametricCoords) && val0.sortedDomains == val1.sortedDomains) + { + found = true; + break; + } + } + EXPECT_TRUE(found) << errorMsg << val0; + } +} + +void expect_num_interfaces_with_cutting_surface(size_t goldNumInterfacesWithCuttingSurfaces, const Element_Cutter & cutter) +{ + std::vector interfacesWithCuttingSurface; + cutter.fill_interfaces_with_cutting_surface(interfacesWithCuttingSurface); + EXPECT_EQ(goldNumInterfacesWithCuttingSurfaces, interfacesWithCuttingSurface.size()); +} + +void expect_intersections(const std::vector & goldIntersections, const std::vector & actualIntersections) +{ + EXPECT_EQ(goldIntersections.empty(), actualIntersections.empty()); + expect_to_find_all_first_in_second(actualIntersections, goldIntersections, "Actual intersection not found in gold intersections: "); + expect_to_find_all_first_in_second(goldIntersections, actualIntersections, "Gold intersection not found in actual intersections: "); +} + +TEST_F(TriangleWithTriplePoint, whenFindingIntersectionPoints_findPointAtCentroid) +{ + build_parent_edges_and_cutter(topology, nodeIds, nodalIsovars); + + std::vector triangleIntersections; + cutter->fill_interior_intersections(triangleIntersections); + + const std::vector goldIntersections{ {Vector3d{1./3.,1./3.,0.}, std::vector{0,1,2}} }; + expect_intersections(goldIntersections, triangleIntersections); +} + +TEST_F(TriangleWithFakeTriplePoint, whenFindingIntersectionPoints_findCorrectPoints) +{ + build_parent_edges_and_cutter(topology, nodeIds, nodalIsovars); + + std::vector triangleIntersections; + cutter->fill_interior_intersections(triangleIntersections); + + expect_num_interfaces_with_cutting_surface(4u, *cutter); + + const std::vector goldIntersections{ {Vector3d{4./9., 4./9., 0.}, std::vector{0,1,3}} }; + expect_intersections(goldIntersections, triangleIntersections); +} + + +} diff --git a/packages/krino/krino/unit_tests/Akri_Unit_Explicit_Hamilton_Jacobi.cpp b/packages/krino/krino/unit_tests/Akri_Unit_Explicit_Hamilton_Jacobi.cpp new file mode 100644 index 000000000000..33e73c71a2e4 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_Explicit_Hamilton_Jacobi.cpp @@ -0,0 +1,903 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace krino { + +struct ProblemFields +{ + stk::mesh::Field * levelSetField = nullptr; + stk::mesh::Field * coordsField = nullptr; + stk::mesh::Field * RHS = nullptr; + stk::mesh::Field * RHSNorm = nullptr; + stk::mesh::Field * speedField = nullptr; +}; + +void associate_input_mesh(stk::io::StkMeshIoBroker & stkIo, const std::string & meshName) +{ + stkIo.property_add(Ioss::Property("DECOMPOSITION_METHOD", "RIB")); + stkIo.add_mesh_database(meshName, stk::io::READ_MESH); + stkIo.create_input_mesh(); +} + +stk::mesh::BulkData & read_mesh(stk::io::StkMeshIoBroker & stkIo) +{ + stkIo.populate_bulk_data(); + return stkIo.bulk_data(); +} + +void declare_fields(stk::mesh::MetaData & meta, ProblemFields & fields) +{ + fields.levelSetField = &meta.declare_field>(stk::topology::NODE_RANK, "LevelSet", 2); + stk::mesh::put_field_on_mesh(*fields.levelSetField, meta.universal_part(), nullptr); + fields.RHS = &meta.declare_field>(stk::topology::NODE_RANK, "RHS", 1); + stk::mesh::put_field_on_mesh(*fields.RHS, meta.universal_part(), nullptr); + fields.RHSNorm = &meta.declare_field>(stk::topology::NODE_RANK, "RHSNorm", 1); + stk::mesh::put_field_on_mesh(*fields.RHSNorm, meta.universal_part(), nullptr); + auto constCoordsField = static_cast*>(meta.coordinate_field()); + fields.coordsField = const_cast*>(constCoordsField); + + if (true) + { + fields.speedField = &meta.declare_field>(stk::topology::ELEMENT_RANK, "Speed", 1); + stk::mesh::put_field_on_mesh(*fields.speedField, meta.universal_part(), nullptr); + } +} + +const int TetFaceTable[4][3] = { {0, 1, 2}, + {1, 2, 3}, + {0, 2, 3}, + {0, 1, 3} }; + +const int TetEdgeNodeOrder[6][2] = // [edge][edge_node] + { {0,1}, {1,2}, {2,0}, {0,3}, {1,3}, {2,3} }; + +void initialize_level_set(const stk::mesh::BulkData & mesh, const ProblemFields & fields, std::function initial_level_set, const double multiplier = 1.0) +{ + for (auto & bptr : mesh.buckets(stk::topology::NODE_RANK)) + { + for (auto & node : *bptr) + { + const double * x = stk::mesh::field_data(*fields.coordsField, node); + double* LS = stk::mesh::field_data(fields.levelSetField->field_of_state(stk::mesh::StateNP1), node); + *LS = initial_level_set(x) * multiplier; + } + } +} + +double initialize_constant_speed(const stk::mesh::BulkData & mesh, const ProblemFields & fields, const double speed) +{ + stk::mesh::field_fill(speed, *fields.speedField); + return speed; +} + +double compute_tet_vol(const stk::mesh::Entity * elemNodes, const stk::mesh::Field & coordsField) +{ + std::array xNode; + for (int n=0; n<4; ++n) xNode[n] = Vector3d(stk::mesh::field_data(coordsField, elemNodes[n])); + + return Dot(xNode[1]-xNode[0],Cross(xNode[2]-xNode[0],xNode[3]-xNode[0]))/6.; +} + +double compute_tri_vol(const stk::mesh::Entity * elemNodes, const stk::mesh::Field & coordsField) +{ + std::array xNode; + for (int n=0; n<3; ++n) xNode[n] = Vector3d(stk::mesh::field_data(coordsField, elemNodes[n]),2); + + return 0.5*Cross(xNode[1]-xNode[0],xNode[2]-xNode[0]).length(); +} + +double compute_vol(const stk::mesh::Entity * elemNodes, const unsigned numElemNodes, const stk::mesh::Field & coordsField) +{ + if (numElemNodes == 3) + return compute_tri_vol(elemNodes, coordsField); + else + return compute_tet_vol(elemNodes, coordsField); +} + +void compute_tet_gradOP_and_vol(const stk::mesh::Entity * elemNodes, const stk::mesh::Field & coordsField, std::vector & gradOP, double & vol) +{ + std::array xNode; + for (int n=0; n<4; ++n) xNode[n] = Vector3d(stk::mesh::field_data(coordsField, elemNodes[n])); + + std::array xFace; + for (int f=0; f<4; ++f) xFace[f] = (xNode[TetFaceTable[f][0]]+xNode[TetFaceTable[f][1]]+xNode[TetFaceTable[f][2]])/3.; + + std::array xEdge; + for (int e=0; e<6; ++e) xEdge[e] = 0.5*(xNode[TetEdgeNodeOrder[e][0]]+xNode[TetEdgeNodeOrder[e][1]]); + + vol = Dot(xNode[1]-xNode[0],Cross(xNode[2]-xNode[0],xNode[3]-xNode[0]))/6.; + const double norm = 0.5/vol; + + gradOP[0] = (Cross(xEdge[3]-xEdge[0],xNode[0]-xFace[3])+ + Cross(xEdge[2]-xEdge[3],xNode[0]-xFace[2])+ + Cross(xEdge[0]-xEdge[2],xNode[0]-xFace[0]))*norm; + gradOP[1] = (Cross(xEdge[0]-xEdge[4],xNode[1]-xFace[3])+ + Cross(xEdge[1]-xEdge[0],xNode[1]-xFace[0])+ + Cross(xEdge[4]-xEdge[1],xNode[1]-xFace[1]))*norm; + gradOP[2] = (Cross(xEdge[2]-xEdge[1],xNode[2]-xFace[0])+ + Cross(xEdge[5]-xEdge[2],xNode[2]-xFace[2])+ + Cross(xEdge[1]-xEdge[5],xNode[2]-xFace[1]))*norm; + gradOP[3] = (Cross(xEdge[5]-xEdge[4],xNode[3]-xFace[1])+ + Cross(xEdge[3]-xEdge[5],xNode[3]-xFace[2])+ + Cross(xEdge[4]-xEdge[3],xNode[3]-xFace[3]))*norm; +} + +void compute_tri_gradOP_and_vol(const stk::mesh::Entity * elemNodes, const stk::mesh::Field & coordsField, std::vector & gradOP, double & vol) +{ + std::array xNode; + for (int n=0; n<3; ++n) xNode[n] = Vector3d(stk::mesh::field_data(coordsField, elemNodes[n]),2); + + vol = 0.5*Cross(xNode[1]-xNode[0],xNode[2]-xNode[0]).length(); + const double norm = 0.5/vol; + + gradOP[0] = (crossZ(xNode[0]-xNode[2])+crossZ(xNode[1]-xNode[0]))*norm; + gradOP[1] = (crossZ(xNode[1]-xNode[0])+crossZ(xNode[2]-xNode[1]))*norm; + gradOP[2] = (crossZ(xNode[2]-xNode[1])+crossZ(xNode[0]-xNode[2]))*norm; +} + +void compute_gradOP_and_vol(const stk::mesh::Entity * elemNodes, const stk::mesh::Field & coordsField, std::vector & gradOP, double &vol) +{ + if (gradOP.size() == 3) + compute_tri_gradOP_and_vol(elemNodes, coordsField, gradOP, vol); + else + compute_tet_gradOP_and_vol(elemNodes, coordsField, gradOP, vol); +} + +Vector3d compute_scalar_gradient(const std::vector & nodalAreaVectors, const stk::mesh::Entity * elemNodes, const stk::mesh::Field & levelSetField) +{ + Vector3d grad(Vector3d::ZERO); + for (unsigned i=0; i & coordsField) +{ + const unsigned nodesPerElem = mesh.mesh_meta_data().spatial_dimension() + 1; + + double minVol = std::numeric_limits::max(); + for (auto & bptr : mesh.buckets(stk::topology::ELEMENT_RANK)) + { + for (auto & elem : *bptr) + { + const stk::mesh::Entity * elemNodes = mesh.begin_nodes(elem); + minVol = std::min(minVol, compute_vol(elemNodes, nodesPerElem, coordsField)); + } + } + + return std::pow(minVol, 1./mesh.mesh_meta_data().spatial_dimension()); +} + +double mesh_minimum_length_scale(const stk::mesh::BulkData & mesh, const stk::mesh::Field & coordsField) +{ + const unsigned dim = mesh.mesh_meta_data().spatial_dimension(); + std::vector gradOP(dim+1); + + double vol = 0.; + double maxGradLength = 0.; + for (auto & bptr : mesh.buckets(stk::topology::ELEMENT_RANK)) + { + for (auto & elem : *bptr) + { + const stk::mesh::Entity * elemNodes = mesh.begin_nodes(elem); + compute_gradOP_and_vol(elemNodes, coordsField, gradOP, vol); + for (unsigned n=0; n gradOP(dim+1); + + stk::mesh::field_fill(0.0, *fields.RHS); + stk::mesh::field_fill(0.0, *fields.RHSNorm); + + for (auto & bptr : mesh.buckets(stk::topology::ELEMENT_RANK)) + { + for (auto & elem : *bptr) + { + const unsigned numElemNodes = mesh.num_nodes(elem); + const stk::mesh::Entity * elemNodes = mesh.begin_nodes(elem); + double vol = 0.; + compute_gradOP_and_vol(elemNodes, *fields.coordsField, gradOP, vol); + const Vector3d normalDir = compute_scalar_gradient(gradOP, elemNodes, fields.levelSetField->field_of_state(stk::mesh::StateN)).unit_vector(); + + const double elementSpeed = *stk::mesh::field_data(*fields.speedField, elem); + + std::vector volHamiltonianCoeffs(dim+1); // K_i in Barth-Sethian + double sumNegCoeffs = 0.; + double sumPosCoeffs = 0.; + double sumNegContrib = 0.; + double volHamiltonian = 0.; + for (unsigned n=0; nfield_of_state(stk::mesh::StateN), node); + volHamiltonianCoeffs[n] = elementSpeed*vol*Dot(normalDir, gradOP[n]); + volHamiltonian += volHamiltonianCoeffs[n] * LSOld; + + if (volHamiltonianCoeffs[n] < 0.) + { + sumNegCoeffs += volHamiltonianCoeffs[n]; + sumNegContrib += volHamiltonianCoeffs[n] * LSOld; + } + else + { + sumPosCoeffs += volHamiltonianCoeffs[n]; + } + } + + std::vector alpha(dim+1, 0.); // delta phi_i in Barth-Sethian + double sumPosAlpha = 0.; + for (unsigned n=0; nfield_of_state(stk::mesh::StateN), node); + if (volHamiltonianCoeffs[n] > 0.) + { + alpha[n] = volHamiltonianCoeffs[n]/sumPosCoeffs*(sumNegContrib-sumNegCoeffs*LSOld)/volHamiltonian; + if (alpha[n] > 0.) sumPosAlpha += alpha[n]; + } + } + + for (unsigned n=0; n 0.) + { + const double wt = alpha[n]/sumPosAlpha; + residual += wt * volHamiltonian; + residualNorm += wt*vol; + } + } + } + } +} + +void assemble_residual_for_nodal_speed(const stk::mesh::BulkData & mesh, const ProblemFields & fields, const double eps) +{ + // Not extensively tested. This is an adaptation of Barth-Sethian positive coefficient scheme for nodal speed fields + + const unsigned dim = mesh.mesh_meta_data().spatial_dimension(); + std::vector gradOP(dim+1); + + stk::mesh::field_fill(0.0, *fields.RHS); + stk::mesh::field_fill(0.0, *fields.RHSNorm); + + for (auto & bptr : mesh.buckets(stk::topology::ELEMENT_RANK)) + { + for (auto & elem : *bptr) + { + const unsigned numElemNodes = mesh.num_nodes(elem); + const stk::mesh::Entity * elemNodes = mesh.begin_nodes(elem); + double vol = 0.; + compute_gradOP_and_vol(elemNodes, *fields.coordsField, gradOP, vol); + const Vector3d normalDir = compute_scalar_gradient(gradOP, elemNodes, fields.levelSetField->field_of_state(stk::mesh::StateN)).unit_vector(); + + for (unsigned n=0; nfield_of_state(stk::mesh::StateN), elemNodes[n]); + + //const double nodalSpeed = *stk::mesh::field_data(fields.speed, node); + const double nodalSpeed = 1.0; + + const double volHamiltonianCoeffs_n = vol*nodalSpeed*Dot(normalDir, gradOP[n]); // K_n in Barth-Sethian, probably should use nodal volume not elem volume + + if (volHamiltonianCoeffs_n > 0.) + { + double sumNegCoeffs = 0.; + double sumPosCoeffs = 0.; + double sumNegContrib = 0.; + double volHamiltonian = 0.; + for (unsigned j=0; jfield_of_state(stk::mesh::StateN), elemNodes[j]); + const double volHamiltonianCoeffs_j = vol*nodalSpeed*Dot(normalDir, gradOP[j]); + volHamiltonian += volHamiltonianCoeffs_j * LSOldj; + + if (volHamiltonianCoeffs_j < 0.) + { + sumNegCoeffs += volHamiltonianCoeffs_j; + sumNegContrib += volHamiltonianCoeffs_j * LSOldj; + } + else + { + sumPosCoeffs += volHamiltonianCoeffs_j; + } + } + + const double wt = volHamiltonianCoeffs_n/sumPosCoeffs*(sumNegContrib-sumNegCoeffs*LSOld)/volHamiltonian; + if (wt > 0.) + { + double & residual = *stk::mesh::field_data(*fields.RHS, node); + double & residualNorm = *stk::mesh::field_data(*fields.RHSNorm, node); + residual += wt * volHamiltonian; + residualNorm += wt*vol; + } + } + } + } + } +} + +double assemble_and_update_Eikonal(const stk::mesh::BulkData & mesh, const ProblemFields & fields, const double eps, const double dt, const bool computeArrivalTime) +{ + // This is a hybrid between the Barth-Sethian positive coefficient scheme and the Morgan-Waltz scheme for reinitialization. + // Uses element based speed and nodal sign function to assemble nodal contributions for Hamiltonian. + // The assembled nodal Hamiltonian is then used with nodal source term to explicitly update signed distance + // (or arrival time for non-unit speed). + // Unlike the elemental algorithm developed by Barth-Sethian, this algorithm converges to the exact solution + // for the "Distance Function Test" described in Morgan-Waltz. Unlike the Morgan-Waltz algorithm, this + // form converges much faster and is tolerant of meshes with obtuse angles because it uses the positive coefficient + // form in Barth-Sethian. + assert(!computeArrivalTime || nullptr != fields.speedField); + + const unsigned dim = mesh.mesh_meta_data().spatial_dimension(); + std::vector gradOP(dim+1); + + stk::mesh::field_fill(0.0, *fields.RHS); + stk::mesh::field_fill(0.0, *fields.RHSNorm); + + for (auto & bptr : mesh.buckets(stk::topology::ELEMENT_RANK)) + { + for (auto & elem : *bptr) + { + const unsigned numElemNodes = mesh.num_nodes(elem); + const stk::mesh::Entity * elemNodes = mesh.begin_nodes(elem); + double vol = 0.; + compute_gradOP_and_vol(elemNodes, *fields.coordsField, gradOP, vol); + const Vector3d normalDir = compute_scalar_gradient(gradOP, elemNodes, fields.levelSetField->field_of_state(stk::mesh::StateN)).unit_vector(); + + double elementSpeed = 1.0; + if (computeArrivalTime) + { + elementSpeed = *stk::mesh::field_data(*fields.speedField, elem); + } + + for (unsigned n=0; nfield_of_state(stk::mesh::StateN), elemNodes[n]); + + double nodalSpeed = 0.; + + const double sign = LSOld/sqrt(LSOld*LSOld + eps*eps); + nodalSpeed = sign*elementSpeed; + + const double volHamiltonianCoeffs_n = vol*nodalSpeed*Dot(normalDir, gradOP[n]); // K_n in Barth-Sethian, probably should use nodal volume not elem volume + + if (volHamiltonianCoeffs_n > 0.) + { + double sumNegCoeffs = 0.; + double sumPosCoeffs = 0.; + double sumNegContrib = 0.; + double volHamiltonian = 0.; + for (unsigned j=0; jfield_of_state(stk::mesh::StateN), elemNodes[j]); + const double volHamiltonianCoeffs_j = vol*nodalSpeed*Dot(normalDir, gradOP[j]); + volHamiltonian += volHamiltonianCoeffs_j * LSOldj; + + if (volHamiltonianCoeffs_j < 0.) + { + sumNegCoeffs += volHamiltonianCoeffs_j; + sumNegContrib += volHamiltonianCoeffs_j * LSOldj; + } + else + { + sumPosCoeffs += volHamiltonianCoeffs_j; + } + } + + const double wt = volHamiltonianCoeffs_n/sumPosCoeffs*(sumNegContrib-sumNegCoeffs*LSOld)/volHamiltonian; + if (wt > 0.) + { + double & residual = *stk::mesh::field_data(*fields.RHS, node); + double & residualNorm = *stk::mesh::field_data(*fields.RHSNorm, node); + residual += wt * volHamiltonian; + residualNorm += wt*vol; + } + } + } + } + } + + double sumSqrResid = 0.; + size_t sumCount = 0; + + for (auto & bptr : mesh.buckets(stk::topology::NODE_RANK)) + { + for (auto & node : *bptr) + { + const double residField = *stk::mesh::field_data(*fields.RHS, node); + const double residNormField = *stk::mesh::field_data(*fields.RHSNorm, node); + + const double Hamiltonian = (residNormField > 0.) ? (residField/residNormField) : 0.; + + const double LSOld = *stk::mesh::field_data(fields.levelSetField->field_of_state(stk::mesh::StateN), node); + double & LS = *stk::mesh::field_data(fields.levelSetField->field_of_state(stk::mesh::StateNP1), node); + const double sign = LSOld/sqrt(LSOld*LSOld + eps*eps); + + LS = LSOld - dt * (Hamiltonian - sign); + + if (computeArrivalTime || std::abs(LSOld) < eps) + { + sumSqrResid += (Hamiltonian - sign)*(Hamiltonian - sign); + sumCount++; + } + } + } + return std::sqrt(sumSqrResid/sumCount); +} + + +double apply_level_set_update(const stk::mesh::BulkData & mesh, const ProblemFields & fields, const double eps, const double dt) +{ + double sumResid = 0.; + size_t sumCount = 0; + + for (auto & bptr : mesh.buckets(stk::topology::NODE_RANK)) + { + for (auto & node : *bptr) + { + const double residField = *stk::mesh::field_data(*fields.RHS, node); + const double residNormField = *stk::mesh::field_data(*fields.RHSNorm, node); + + const double resid = (residNormField > 0.) ? (residField/residNormField) : 0.; + + const double LSOld = *stk::mesh::field_data(fields.levelSetField->field_of_state(stk::mesh::StateN), node); + double & LS = *stk::mesh::field_data(fields.levelSetField->field_of_state(stk::mesh::StateNP1), node); + + LS = LSOld - dt * resid; + + if (std::abs(LSOld) < eps) + { + sumResid += std::abs(resid); + sumCount++; + } + } + } + return sumResid/sumCount; +} + +bool domain_contains_interface(const stk::mesh::BulkData & mesh, const stk::mesh::Field & levelSetField) +{ + bool hasNeg = false; + bool hasPos = false; + + for (auto & bptr : mesh.buckets(stk::topology::NODE_RANK)) + { + for (auto & node : *bptr) + { + const double LS = *stk::mesh::field_data(levelSetField, node); + + if (LS < 0.) hasNeg = true; + if (LS > 0.) hasPos = true; + + if (hasNeg && hasPos) return true; + } + } + return false; +} + +void evolve_level_set(const stk::mesh::BulkData & mesh, const ProblemFields & fields, const double eps, const double dt) +{ + assemble_residual_for_element_speed(mesh, fields, eps); + apply_level_set_update(mesh, fields, eps, dt); +} + +void reinitialize_level_set( + stk::mesh::BulkData & mesh, + const ProblemFields & fields, + const double eps, + const double dtau, + stk::io::StkMeshIoBroker * stkIo = nullptr, + const double outputStartTime = 0., + const double outputStopTime = 0., + const size_t outputFileIndex = 0) +{ + if (!domain_contains_interface(mesh, fields.levelSetField->field_of_state(stk::mesh::StateNP1))) + { + return; + } + + const double convergedTol = 0.01; + bool converged = false; + const int maxIters = 1000; + const int printFreq = 50; + const double dOutputTime = (outputStopTime-outputStartTime)/(maxIters+1); + for (int iter = 0; iterfield_of_state(stk::mesh::StateNP1))) + { + std::cout << "Error, input level set field does not contain zero level set for initializing arrival time calculation." << std::endl; + return; + } + + bool converged = false; + const int maxIters = 5000; + const int printFreq = 50; + for (int iter = 0; iter eps) return 1.; + + static const double pi = std::atan(1)*4; + return 0.5*(1+ signedDist/eps + std::sin(pi*signedDist/eps)/pi); +} + +double delta(const double signedDist, const double eps) +{ + if (signedDist < -eps || signedDist > eps) return 0.; + + static const double pi = std::atan(1)*4; + return 0.5/eps * (1. + std::cos(pi*signedDist/eps)); +} + +double compute_level_set_volume(stk::mesh::BulkData & mesh, const ProblemFields & fields, const double eps) +{ + double vol = 0.; + for (auto & bptr : mesh.buckets(stk::topology::ELEMENT_RANK)) + { + for (auto & elem : *bptr) + { + const unsigned numElemNodes = mesh.num_nodes(elem); + const stk::mesh::Entity * elemNodes = mesh.begin_nodes(elem); + + double avgLS = 0.; + for (unsigned n=0; nfield_of_state(stk::mesh::StateNP1), elemNodes[n]); + avgLS += LS/numElemNodes; + } + + vol += Heaviside(avgLS, eps) * compute_vol(elemNodes, numElemNodes, *fields.coordsField); + } + } + return vol; +} + +double compute_unit_radius_error_norm(stk::mesh::BulkData & mesh, const ProblemFields & fields) +{ + double norm = 0.; + unsigned normCount = 0; + for (auto & bptr : mesh.buckets(stk::topology::NODE_RANK)) + { + for (auto & node : *bptr) + { + double & LS = *stk::mesh::field_data(fields.levelSetField->field_of_state(stk::mesh::StateNP1), node); + const Vector3d x(stk::mesh::field_data(*fields.coordsField, node), mesh.mesh_meta_data().spatial_dimension()); + + const double error = LS - (x.length() - 1.0); + norm += std::abs(error); + ++normCount; + } + } + return norm/normCount; +} + +double poor_initial_condition_for_unit_circle(const double * x) +{ + return ((x[0]-1.)*(x[0]-1.)+(x[1]-1.)*(x[1]-1.)+0.1)*(std::sqrt(x[0]*x[0]+x[1]*x[1])-1.); +} + +double poor_initial_condition_for_unit_sphere(const double * x) +{ + return ((x[0]-1.)*(x[0]-1.)+(x[1]-1.)*(x[1]-1.)+(x[2]-1.)*(x[2]-1.)+0.1)*(std::sqrt(x[0]*x[0]+x[1]*x[1]+x[2]*x[2])-1.); +} + +double flower_2D(const double * x) +{ + const int Nlobes = 3; + const double r = std::sqrt(x[0]*x[0]+x[1]*x[1]); + const double theta = std::atan2(x[1],x[0]); + const double rSurf = 0.2 + 0.1*std::sin(Nlobes*theta); + return r-rSurf; +} + +TEST(HamiltonJacobi, 2DPoorInitialCondition_ComputingArrivalTimeProducesLowErrorEverywhere) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + if (parallel_size > 1) return; + + stk::io::StkMeshIoBroker stkIo(pm); + ProblemFields fields; + + associate_input_mesh(stkIo, "square.g"); + declare_fields(stkIo.meta_data(),fields); + stk::mesh::BulkData & mesh = read_mesh(stkIo); + + initialize_level_set(mesh, fields, poor_initial_condition_for_unit_circle); + initialize_constant_speed(mesh, fields, 1.0); + + const double dx = mesh_minimum_length_scale(mesh, *fields.coordsField); + const double eps = 1.5*dx; // Should have same units as level set + const double dtau = 0.2*dx; // Reinitialization time step, based on unit advection speed used in reinitialization + + compute_arrival_time(mesh, fields, eps, dtau); + + const double errorNorm = compute_unit_radius_error_norm(mesh, fields); + std::cout << "Error norm " << errorNorm << std::endl; + EXPECT_LT(errorNorm, 0.001); +} + +TEST(HamiltonJacobi, 3DPoorInitialCondition_ComputingArrivalTimeProducesLowErrorEverywhere) +{ +#ifdef NDEBUG +#else + return; // Optimized only due to length +#endif + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + if (parallel_size > 1) return; + + stk::io::StkMeshIoBroker stkIo(pm); + ProblemFields fields; + + associate_input_mesh(stkIo, "cube_coarse.g"); + declare_fields(stkIo.meta_data(),fields); + stk::mesh::BulkData & mesh = read_mesh(stkIo); + + initialize_level_set(mesh, fields, poor_initial_condition_for_unit_sphere); + initialize_constant_speed(mesh, fields, 1.0); + + const double dx = mesh_minimum_length_scale(mesh, *fields.coordsField); + const double eps = 1.5*dx; // Should have same units as level set + const double dtau = 0.2*dx; // Reinitialization time step, based on unit advection speed used in reinitialization + + compute_arrival_time(mesh, fields, eps, dtau); + + const double errorNorm = compute_unit_radius_error_norm(mesh, fields); + std::cout << "Error norm " << errorNorm << std::endl; + EXPECT_LT(errorNorm, 0.01); +} + +void test_circle_with_flower(const double speed) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + if (parallel_size > 1) return; + + stk::io::StkMeshIoBroker stkIo(pm); + ProblemFields fields; + + associate_input_mesh(stkIo, "circle.g"); + declare_fields(stkIo.meta_data(),fields); + stk::mesh::BulkData & mesh = read_mesh(stkIo); + + const double maxSpeed = initialize_constant_speed(mesh, fields, speed); + initialize_level_set(mesh, fields, flower_2D, 1./maxSpeed); + + const double dx = mesh_minimum_length_scale(mesh, *fields.coordsField); + const double eps = 1.5*dx / maxSpeed; // Should have same units as level set + const double dtau = 0.2*dx / maxSpeed; // Units of time + + compute_arrival_time(mesh, fields, eps, dtau); +} + +TEST(HamiltonJacobi, CircleWithFlowerIC_ArrivalTimeConvergesForAnySpeed) +{ + // Probably would be better to test that these converge in exactly the same number of steps + test_circle_with_flower(1.0); + test_circle_with_flower(10.0); +} + +TEST(HamiltonJacobi, CircleWithFlowerIC_ReinitializationThenEvolveRuns) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + if (parallel_size > 1) return; + + stk::io::StkMeshIoBroker stkIo(pm); + ProblemFields fields; + + associate_input_mesh(stkIo, "circle.g"); + declare_fields(stkIo.meta_data(), fields); + stk::mesh::BulkData & mesh = read_mesh(stkIo); + + std::string outputFileName = "circle_flower.e"; + auto outputFileIndex = create_mesh(outputFileName, stkIo); + + const double maxSpeed = initialize_constant_speed(mesh, fields, 5.0); + initialize_level_set(mesh, fields, flower_2D); + + const double Courant = 0.25; + const double Ttotal = 0.1; + const double dx = mesh_minimum_length_scale(mesh, *fields.coordsField); + const unsigned Nt = (Ttotal+0.5*(Courant*dx/maxSpeed))/(Courant*dx/maxSpeed); + const double dt = Ttotal/Nt; + const double eps = 1.5*dx; + const double dtau = 0.2*dx; // Reinitialization time step, based on unit advection speed used in reinitialization + + write_fields(stkIo, outputFileIndex, 0.0); + reinitialize_level_set(mesh, fields, eps, dtau, &stkIo, 0.0, dt, outputFileIndex); + + std::cout << "Evolving to time " << Ttotal << " with " << Nt << " steps with Courant number " << Courant << std::endl; + + double time = 0.0; + for (unsigned n=0; n 1) return; + + stk::io::StkMeshIoBroker stkIo(pm); + ProblemFields fields; + + associate_input_mesh(stkIo, "cylinder_coarse.g"); + declare_fields(stkIo.meta_data(), fields); + stk::mesh::BulkData & mesh = read_mesh(stkIo); + + std::string outputFileName = "cylinder_flower.e"; + auto outputFileIndex = create_mesh(outputFileName, stkIo); + + const double maxSpeed = initialize_constant_speed(mesh, fields, 5.0); + initialize_level_set(mesh, fields, flower_2D); + + const double Courant = 0.25; + const double Ttotal = 0.01; + const double dx = mesh_minimum_length_scale(mesh, *fields.coordsField); + const unsigned Nt = (Ttotal+0.5*(Courant*dx/maxSpeed))/(Courant*dx/maxSpeed); + const double dt = Ttotal/Nt; + const double eps = 1.5*dx; + const double dtau = 0.2*dx; // Reinitialization time step, based on unit advection speed used in reinitialization + + write_fields(stkIo, outputFileIndex, 0.0); + reinitialize_level_set(mesh, fields, eps, dtau, &stkIo, 0.0, dt, outputFileIndex); + + std::cout << "Evolving to time " << Ttotal << " with " << Nt << " steps with Courant number " << Courant << std::endl; + + double time = 0.0; + for (unsigned n=0; n + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace krino +{ + +namespace { + int num_random_cases() { return 10000; } + double clip(const double in) { return std::floor(1000*in)/1000.; } +} // namespace + +TEST(Plane_Cutting_Surface, random_edge_cuts) +{ + const bool debug_output = false; + const Vector3d plane_pt0(0., 0., 0.); + const Vector3d plane_pt1(1., 0., 0.); + const Vector3d plane_pt2(0., 1., 0.); + Plane_Cutting_Surface surf(plane_pt0, plane_pt1, plane_pt2); + + + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_rank = stk::parallel_machine_rank(pm); + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution coord(-1., 1.); + + const int num_cases = num_random_cases(); + for (int icase=0; icase::epsilon()); + } + else + { + EXPECT_ANY_THROW(surf.interface_crossing_position(segment)); + } + } +} + +TEST(Intersecting_Planes_Cutting_Surface, planar_with_random_edge_cuts) +{ + const Vector3d plane_pt0(0., 0., 0.); + const Vector3d plane_pt1(1., 0., 0.); + const Vector3d plane_pt2(1., 1., 0.); + const Vector3d plane_pt3(0., 1., 0.); + Intersecting_Planes_Cutting_Surface surf(plane_pt0, plane_pt1, plane_pt2, plane_pt3); + + + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_rank = stk::parallel_machine_rank(pm); + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution coord(-1., 1.); + + const int num_cases = num_random_cases(); + for (int icase=0; icase::epsilon()); + } + else + { + EXPECT_ANY_THROW(surf.interface_crossing_position(segment)); + } + } +} + +TEST(Intersecting_Planes_Cutting_Surface, positive_dihedral_with_random_edge_cuts) +{ + const bool debug_output = false; + const Vector3d plane_pt0(0., 0., 0.); + const Vector3d plane_pt1(0., 1., 1.); + const Vector3d plane_pt2(1., 0., 0.); + const Vector3d plane_pt3(0., 1., 0.); + Intersecting_Planes_Cutting_Surface surf(plane_pt0, plane_pt1, plane_pt2, plane_pt3); + + + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_rank = stk::parallel_machine_rank(pm); + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution coord(-1., 1.); + + const int num_cases = num_random_cases(); + for (int icase=0; icase 0.5*pt0[1]) ? 1 : 0) + ((pt1[2] > 0.5*pt1[1]) ? 2 : 0); + switch (case_id) + { + case 0: EXPECT_NEAR(position, pos1, std::numeric_limits::epsilon()); break; + case 3: EXPECT_NEAR(position, pos0, std::numeric_limits::epsilon()); break; + } + } + else + { + EXPECT_ANY_THROW(surf.interface_crossing_position(segment)); + } + } +} + +TEST(Intersecting_Planes_Cutting_Surface, negative_dihedral_with_random_edge_cuts) +{ + const bool debug_output = false; + const Vector3d plane_pt0(0., 0., 0.); + const Vector3d plane_pt1(0., 1., 0.); + const Vector3d plane_pt2(1., 0., 0.); + const Vector3d plane_pt3(0., 1., 1.); + Intersecting_Planes_Cutting_Surface surf(plane_pt0, plane_pt1, plane_pt2, plane_pt3); + + + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_rank = stk::parallel_machine_rank(pm); + std::mt19937 mt(std::mt19937::default_seed + parallel_rank); + std::uniform_real_distribution coord(-1., 1.); + + const int num_cases = num_random_cases(); + for (int icase=0; icase 0.5*pt0[1]) ? 1 : 0) + ((pt1[2] > 0.5*pt1[1]) ? 2 : 0); + switch (case_id) + { + case 0: EXPECT_NEAR(position, pos1, std::numeric_limits::epsilon()); break; + case 3: EXPECT_NEAR(position, pos0, std::numeric_limits::epsilon()); break; + } + } + else + { + EXPECT_ANY_THROW(surf.interface_crossing_position(segment)); + } + } +} + +TEST(Intersecting_Planes_Cutting_Surface, infinitesimal_triangle_that_requires_robust_dihedral_angle) +{ + const double goldPosition = 0.5181038869168293; + const double otherCrossing = 0.4818961330726974; + const Vector3d plane_pt0(otherCrossing, 0., 1.-otherCrossing); + const Vector3d plane_pt1(0., 0., goldPosition); + const Vector3d plane_pt2(0., (1.-1.e-10), 0.); + const Vector3d plane_pt3(1.e-10, (1.-1.e-10), 0.); + Intersecting_Planes_Cutting_Surface surf(plane_pt0, plane_pt1, plane_pt2, plane_pt3); + + const Vector3d node0(0., 0., 0.); + const Vector3d node3(0., 0., 1.); + const double position = surf.interface_crossing_position(Segment3d(node0,node3)); + EXPECT_DOUBLE_EQ(position, goldPosition); +} + +void expect_intersection(const Plane3d & plane0, const Plane3d & plane1, const Plane3d & plane2, const Vector3d & goldIntersection) +{ + Vector3d intersectionPoint; + EXPECT_TRUE(find_intersection_of_three_planes(plane0, plane1, plane2, intersectionPoint)); + expect_eq(goldIntersection, intersectionPoint); +} + +void expect_intersection_with_side_of_tet(const int side, const Plane3d & plane0, const Plane3d & plane1, const Vector3d & goldIntersection) +{ + Vector3d intersectionPoint; + EXPECT_TRUE(find_intersection_of_two_planes_and_side_of_tet(side, plane0, plane1, intersectionPoint)); + expect_eq(goldIntersection, intersectionPoint); +} + +void expect_no_intersection(const Plane3d & plane0, const Plane3d & plane1, const Plane3d & plane2) +{ + Vector3d intersectionPoint; + EXPECT_FALSE(find_intersection_of_three_planes(plane0, plane1, plane2, intersectionPoint)); +} + +void expect_no_intersection_within_tet(const Plane3d & plane0, const Plane3d & plane1, const Plane3d & plane2) +{ + Vector3d intersectionPoint; + EXPECT_FALSE(find_intersection_of_three_planes_within_tet(plane0, plane1, plane2, intersectionPoint)); +} + +void expect_no_intersection_with_side_of_tet(const int side, const Plane3d & plane0, const Plane3d & plane1) +{ + Vector3d intersectionPoint; + EXPECT_FALSE(find_intersection_of_two_planes_and_side_of_tet(side, plane0, plane1, intersectionPoint)); +} + +struct IntersectPlanes : public ::testing::Test +{ + const Vector3d pt0{0., 0., 0.}; + const Vector3d pt1{1., 0., 0.}; + const Vector3d pt2{0., 1., 0.}; + const Vector3d pt3{0., 0., 1.}; + const Plane3d plane0{pt0, pt1, pt3}; + const Plane3d plane1{pt1, pt2, pt3}; + const Plane3d plane2{pt2, pt0, pt3}; + const Plane3d plane3{pt0, pt2, pt1}; + + const Vector3d offsetPt0{-0.1, -0.1, -0.1}; + const Vector3d offsetPt1{1.1, -0.1, -0.1}; + const Vector3d offsetPt2{-0.1, 1.1, -0.1}; + const Vector3d offsetPt3{-0.1, -0.1, 1.1}; + const Plane3d offsetPlane0{offsetPt0, offsetPt1, offsetPt3}; + const Plane3d offsetPlane1{offsetPt1, offsetPt2, offsetPt3}; + const Plane3d offsetPlane2{offsetPt2, offsetPt0, offsetPt3}; + const Plane3d offsetPlane3{offsetPt0, offsetPt2, offsetPt1}; + +}; + +TEST_F(IntersectPlanes, given3Planes_FindCorrectIntersection) +{ + expect_intersection(plane0, plane1, plane2, Vector3d{0., 0., 1.}); + expect_intersection(plane0, plane1, plane3, Vector3d{1., 0., 0.}); + expect_intersection(plane1, plane2, plane3, Vector3d{0., 1., 0.}); + expect_intersection(plane0, plane2, plane3, Vector3d{0., 0., 0.}); + + expect_no_intersection(plane0, plane0, plane1); +} + +TEST_F(IntersectPlanes, given3PlanesThatDoNotIntersectWithinTetBounds_FindNoIntersection) +{ + expect_no_intersection_within_tet(offsetPlane0, offsetPlane1, offsetPlane2); + expect_no_intersection_within_tet(offsetPlane0, offsetPlane1, offsetPlane3); + expect_no_intersection_within_tet(offsetPlane1, offsetPlane2, offsetPlane3); + expect_no_intersection_within_tet(offsetPlane0, offsetPlane2, offsetPlane3); +} + +TEST_F(IntersectPlanes, given2PlanesThatIntersectSideOfTet_FindCorrectIntersection) +{ + expect_intersection_with_side_of_tet(0, plane1, plane2, Vector3d{0., 0., 1.}); + expect_intersection_with_side_of_tet(0, plane1, plane3, Vector3d{1., 0., 0.}); + expect_intersection_with_side_of_tet(0, plane2, plane3, Vector3d{0., 0., 0.}); + + expect_intersection_with_side_of_tet(1, plane2, plane3, Vector3d{0., 1., 0.}); + expect_intersection_with_side_of_tet(1, plane0, plane2, Vector3d{0., 0., 1.}); + expect_intersection_with_side_of_tet(1, plane0, plane3, Vector3d{1., 0., 0.}); + + expect_intersection_with_side_of_tet(2, plane0, plane1, Vector3d{0., 0., 1.}); + expect_intersection_with_side_of_tet(2, plane0, plane3, Vector3d{0., 0., 0.}); + expect_intersection_with_side_of_tet(2, plane1, plane3, Vector3d{0., 1., 0.}); + + expect_intersection_with_side_of_tet(3, plane0, plane1, Vector3d{1., 0., 0.}); + expect_intersection_with_side_of_tet(3, plane0, plane2, Vector3d{0., 0., 0.}); + expect_intersection_with_side_of_tet(3, plane1, plane2, Vector3d{0., 1., 0.}); +} + +TEST_F(IntersectPlanes, given2PlanesThatDoNotIntersectSideOfTetAtAll_FindNoIntersection) +{ + expect_no_intersection_with_side_of_tet(0, plane0, plane2); + expect_no_intersection_with_side_of_tet(1, plane1, plane2); + expect_no_intersection_with_side_of_tet(2, plane2, plane1); + expect_no_intersection_with_side_of_tet(3, plane3, plane2); +} + +TEST_F(IntersectPlanes, given2PlanesThatDoNotIntersectSideOfTetWithinTetBounds_FindNoIntersection) +{ + expect_no_intersection_with_side_of_tet(0, offsetPlane1, offsetPlane2); + expect_no_intersection_with_side_of_tet(0, offsetPlane1, offsetPlane3); + expect_no_intersection_with_side_of_tet(0, offsetPlane2, offsetPlane3); + + expect_no_intersection_with_side_of_tet(1, offsetPlane2, offsetPlane3); + expect_no_intersection_with_side_of_tet(1, offsetPlane0, offsetPlane2); + expect_no_intersection_with_side_of_tet(1, offsetPlane0, offsetPlane3); + + expect_no_intersection_with_side_of_tet(2, offsetPlane0, offsetPlane1); + expect_no_intersection_with_side_of_tet(2, offsetPlane0, offsetPlane3); + expect_no_intersection_with_side_of_tet(2, offsetPlane1, offsetPlane3); + + expect_no_intersection_with_side_of_tet(3, offsetPlane0, offsetPlane1); + expect_no_intersection_with_side_of_tet(3, offsetPlane0, offsetPlane2); + expect_no_intersection_with_side_of_tet(3, offsetPlane1, offsetPlane2); +} + +static Vector3d compute_2d_plane_direction(const Vector3d & pt0, const Vector3d & pt1) +{ + return Vector3d(pt1[1]-pt0[1],pt0[0]-pt1[0],0.); +} + +static Plane3d build_2d_plane(const Vector3d & pt0, const Vector3d & pt1) +{ + return Plane3d(compute_2d_plane_direction(pt0,pt1),pt0); +} + +struct Intersect2DPlanes : public ::testing::Test +{ + Intersect2DPlanes() + : plane0{build_2d_plane(pt0, pt1)}, + plane1{build_2d_plane(pt1, pt2)}, + plane2{build_2d_plane(pt0, pt2)}, + offsetPlane0{build_2d_plane(offsetPt0, offsetPt1)}, + offsetPlane1{build_2d_plane(offsetPt1, offsetPt2)}, + offsetPlane2{build_2d_plane(offsetPt0, offsetPt2)} + { + } + + const Vector3d pt0{0., 0., 0.}; + const Vector3d pt1{1., 0., 0.}; + const Vector3d pt2{0., 1., 0.}; + + const Vector3d offsetPt0{-0.1, -0.1, 0.}; + const Vector3d offsetPt1{1.1, -0.1, 0.}; + const Vector3d offsetPt2{-0.1, 1.1, 0.}; + + Plane3d plane0; + Plane3d plane1; + Plane3d plane2; + + Plane3d offsetPlane0; + Plane3d offsetPlane1; + Plane3d offsetPlane2; +}; + +void expect_2d_intersection(const Plane3d & plane0, const Plane3d & plane1, const Vector3d & goldIntersection) +{ + Vector3d intersectionPoint; + EXPECT_TRUE(find_intersection_of_two_2D_planes(plane0, plane1, intersectionPoint)); + expect_eq(goldIntersection, intersectionPoint); +} + +void expect_no_2d_intersection_within_tri(const Plane3d & plane0, const Plane3d & plane1) +{ + Vector3d intersectionPoint; + EXPECT_FALSE(find_intersection_of_two_2D_planes_within_tri(plane0, plane1, intersectionPoint)); +} + +void expect_no_2d_intersection(const Plane3d & plane0, const Plane3d & plane1) +{ + Vector3d intersectionPoint; + EXPECT_FALSE(find_intersection_of_two_2D_planes(plane0, plane1, intersectionPoint)); +} + +TEST_F(Intersect2DPlanes, given2Plane2Ds_FindCorrectIntersection) +{ + expect_2d_intersection(plane0, plane1, Vector3d{1., 0., 0.}); + expect_2d_intersection(plane1, plane2, Vector3d{0., 1., 0.}); + expect_2d_intersection(plane0, plane2, Vector3d{0., 0., 0.}); + + expect_no_2d_intersection(plane0, plane0); +} + +TEST_F(Intersect2DPlanes, given2Plane2DsThatDoNotIntersectWithinTriBounds_FindNoIntersection) +{ + expect_no_2d_intersection_within_tri(offsetPlane0, offsetPlane1); + expect_no_2d_intersection_within_tri(offsetPlane1, offsetPlane2); + expect_no_2d_intersection_within_tri(offsetPlane0, offsetPlane2); +} + +TEST(ProjectionOf3DPointsIntoTriangle, givenTriangleCheckProjectionOfPointOnAndOffPlane) +{ + const Vector3d pt0{0., 0., 0.}; + const Vector3d pt1{1., 0., 0.}; + const Vector3d pt2{0., 1., 1.}; + const std::array triCoords{{ pt0, pt1, pt2 }}; + + expect_eq(Vector3d{0.,0.,0.}, triangle_parametric_coordinates_of_projected_point(triCoords, pt0)); + expect_eq(Vector3d{1.,0.,0.}, triangle_parametric_coordinates_of_projected_point(triCoords, pt1)); + expect_eq(Vector3d{0.,1.,0.}, triangle_parametric_coordinates_of_projected_point(triCoords, pt2)); + + const Vector3d normal = Cross(pt1-pt0, pt2-pt0).unit_vector(); + expect_eq(Vector3d{0.,0.,0.}, triangle_parametric_coordinates_of_projected_point(triCoords, pt0+normal)); + expect_eq(Vector3d{1.,0.,0.}, triangle_parametric_coordinates_of_projected_point(triCoords, pt1+normal)); + expect_eq(Vector3d{0.,1.,0.}, triangle_parametric_coordinates_of_projected_point(triCoords, pt2+normal)); + + const double oneThird = 1./3.; + const Vector3d midPt = oneThird*(pt0+pt1+pt2); + expect_eq(Vector3d{oneThird,oneThird,0.}, triangle_parametric_coordinates_of_projected_point(triCoords, midPt+normal)); +} + +} // namespace krino diff --git a/packages/krino/krino/unit_tests/Akri_Unit_LogRedirecter.cpp b/packages/krino/krino/unit_tests/Akri_Unit_LogRedirecter.cpp new file mode 100644 index 000000000000..bf1aa985a09b --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_LogRedirecter.cpp @@ -0,0 +1,27 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include + +namespace krino { + +LogRedirecter::LogRedirecter() +: myOriginalBuffer(krinolog.getStream().rdbuf()) +{ + krinolog.getStream().rdbuf(&myBuffer); +} + +LogRedirecter::~LogRedirecter() +{ + krinolog.getStream().rdbuf(myOriginalBuffer); +} + +} + diff --git a/packages/krino/krino/unit_tests/Akri_Unit_LogRedirecter.hpp b/packages/krino/krino/unit_tests/Akri_Unit_LogRedirecter.hpp new file mode 100644 index 000000000000..9b26c01006fc --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_LogRedirecter.hpp @@ -0,0 +1,32 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef AKRI_UNIT_LOGREDIRECTER_H_ +#define AKRI_UNIT_LOGREDIRECTER_H_ + +#include + +namespace krino { + +class LogRedirecter +{ +public: + LogRedirecter(); + ~LogRedirecter(); + + void clear() { myBuffer.str(""); } + std::string get_log() const { return myBuffer.str(); } +private: + std::stringbuf myBuffer; + std::streambuf * myOriginalBuffer; +}; + +} + + +#endif diff --git a/packages/krino/krino/unit_tests/Akri_Unit_LowerEnvelope.cpp b/packages/krino/krino/unit_tests/Akri_Unit_LowerEnvelope.cpp new file mode 100644 index 000000000000..de5b17ccc7d5 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_LowerEnvelope.cpp @@ -0,0 +1,260 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include + + +namespace krino +{ + +TEST(Lower_Envelope, Find_Crossing_Segment) +{ + double s = 0.0; + + ASSERT_TRUE(find_lower_envelope_crossing_point({{{0., 1.}, {0., 0.}}}, s)); + EXPECT_DOUBLE_EQ(1.0, s); + + ASSERT_TRUE(find_lower_envelope_crossing_point({{{0., 1.}, {1., 0.}}}, s)); + EXPECT_DOUBLE_EQ(0.5, s); + + ASSERT_TRUE(find_lower_envelope_crossing_point({{{0., 1.}, {0.5, 0.}}}, s)); + EXPECT_DOUBLE_EQ(2./3., s); +} + +TEST(Lower_Envelope, Find_Crossing_Triangle) +{ + std::array pt = {{0., 0.}}; + + const std::vector phi000 = {0., 0., 0.}; + const std::vector phi011 = {0., 1., 1.}; + const std::vector phi101 = {1., 0., 1.}; + const std::vector phi110 = {1., 1., 0.}; + + EXPECT_FALSE(find_lower_envelope_crossing_point({{phi110, phi110, phi101}}, pt)); + + ASSERT_TRUE(find_lower_envelope_crossing_point({{phi110, phi011, phi101}}, pt)); + std::array goldPt = {{1./3., 1./3.}}; + EXPECT_EQ(goldPt, pt); + + ASSERT_TRUE(find_lower_envelope_crossing_point({{{1., 1., 0.75}, {0., 1., 0.75}, {1., 0., 0.75}}}, pt)); + goldPt = {{0.25, 0.25}}; + EXPECT_EQ(goldPt, pt); + + ASSERT_TRUE(find_lower_envelope_crossing_point({{phi000, phi011, phi101}}, pt)); + goldPt = {{0., 0.}}; + + ASSERT_TRUE(find_lower_envelope_crossing_point({{phi011, phi000, phi101}}, pt)); + goldPt = {{1., 0.}}; + + ASSERT_TRUE(find_lower_envelope_crossing_point({{phi011, phi101, phi000}}, pt)); + goldPt = {{0., 1.}}; + EXPECT_EQ(goldPt, pt); +} + +TEST(Lower_Envelope, Find_Crossing_Tetrahedron) +{ + std::array pt = {{0., 0., 0.}}; + + const std::vector phi0000 = {0., 0., 0., 0.}; + const std::vector phi1110 = {1., 1., 1., 0.}; + const std::vector phi1101 = {1., 1., 0., 1.}; + const std::vector phi1011 = {1., 0., 1., 1.}; + const std::vector phi0111 = {0., 1., 1., 1.}; + + EXPECT_FALSE(find_lower_envelope_crossing_point({{phi1110, phi1110, phi1011, phi0111}}, pt)); + + ASSERT_TRUE(find_lower_envelope_crossing_point({{phi1110, phi1101, phi1011, phi0111}}, pt)); + std::array goldPt = {{0.25, 0.25, 0.25}}; + EXPECT_EQ(goldPt, pt); + + ASSERT_TRUE(find_lower_envelope_crossing_point({{phi0000, phi0111, phi1011, phi1101}}, pt)); + goldPt = {{0., 0., 0.}}; + EXPECT_EQ(goldPt, pt); + + ASSERT_TRUE(find_lower_envelope_crossing_point({{phi0111, phi0000, phi1011, phi1101}}, pt)); + goldPt = {{1., 0., 0.}}; + EXPECT_EQ(goldPt, pt); + + ASSERT_TRUE(find_lower_envelope_crossing_point({{phi0111, phi1011, phi0000, phi1101}}, pt)); + goldPt = {{0., 1., 0.}}; + EXPECT_EQ(goldPt, pt); + + ASSERT_TRUE(find_lower_envelope_crossing_point({{phi0111, phi1011, phi1101, phi0000}}, pt)); + goldPt = {{0., 0., 1.}}; + EXPECT_EQ(goldPt, pt); +} + + +TEST(Lower_Envelope, Two_LS_Bug) +{ + Segment_Vector envelope = SegmentLowerEnvelope::find_lower_envelope({0., 0.}, {1., 0.}); + ASSERT_EQ(1u, envelope.size()); + EXPECT_EQ(1, envelope[0].ls_index()); + EXPECT_DOUBLE_EQ(0.0, envelope[0].left_endpoint()); + EXPECT_DOUBLE_EQ(1., envelope[0].right_endpoint()); +} + +TEST(Lower_Envelope, Two_LS_Lower_Envelope) +{ + { + // 2 level sets, infinitesimal crossing on left by convention that highest level set index wins + Segment_Vector envelope = SegmentLowerEnvelope::find_lower_envelope({0., 0.}, {0.25, 1.}); + ASSERT_EQ(2u, envelope.size()); + const LS_Segment & env1 = envelope[0]; + const LS_Segment & env2 = envelope[1]; + EXPECT_EQ(1, env1.ls_index()); + EXPECT_EQ(0, env2.ls_index()); + EXPECT_DOUBLE_EQ(0.0, env1.left_endpoint()); + EXPECT_DOUBLE_EQ(0.0, env1.right_endpoint()); + EXPECT_DOUBLE_EQ(0.0, env2.left_endpoint()); + EXPECT_DOUBLE_EQ(1.0, env2.right_endpoint()); + } + + { + // 2 level sets, infinitesimal crossing on right by convention that highest level set index wins + Segment_Vector envelope = SegmentLowerEnvelope::find_lower_envelope({0.3, 0.6}, {0., 0.}); + ASSERT_EQ(2u, envelope.size()); + const LS_Segment & env1 = envelope[0]; + const LS_Segment & env2 = envelope[1]; + EXPECT_EQ(0, env1.ls_index()); + EXPECT_EQ(1, env2.ls_index()); + EXPECT_DOUBLE_EQ(0.0, env1.left_endpoint()); + EXPECT_DOUBLE_EQ(1.0, env1.right_endpoint()); + EXPECT_DOUBLE_EQ(1.0, env2.left_endpoint()); + EXPECT_DOUBLE_EQ(1.0, env2.right_endpoint()); + } +} + +void expect_good_edge(const Segment_Vector & envelope) +{ + for (size_t i=0; i= SegmentLowerEnvelope::MinSize()) << "Unexpected infinitesimal, non-zero segment at end of edge " << envelope; + } + else + { + EXPECT_GE(envelope[i].length(), SegmentLowerEnvelope::MinSize()) << "Unexpected internal infinitesimal internal segment on edge " << envelope; + } + + if (i & phi0, const std::vector & phi1) +{ + ASSERT_FALSE(envelope.empty()); + EXPECT_EQ(get_min(phi0), envelope.front().ls_index()) << "Front segment of edge does not match phase at node on edge " << envelope; + EXPECT_EQ(get_min(phi1), envelope.back().ls_index()) << "Back segment of edge does not match phase at node on edge " << envelope; +} + +TEST(Lower_Envelope, Three_LS_Lower_Envelope) +{ + { + // 3 level sets, edge is B from 0 to 0.5, C from 0.5 to 1.0 and + // 1st level set intersects both others at 0.5 but is never the lowest + Segment_Vector envelope = SegmentLowerEnvelope::find_lower_envelope({0.5, 0., 1.}, {0.5, 1., 0.}); + ASSERT_EQ(2u, envelope.size()); + const LS_Segment & env1 = envelope[0]; + const LS_Segment & env2 = envelope[1]; + EXPECT_EQ(1, env1.ls_index()); + EXPECT_EQ(2, env2.ls_index()); + EXPECT_DOUBLE_EQ(0.0, env1.left_endpoint()); + EXPECT_DOUBLE_EQ(0.5, env1.right_endpoint()); + EXPECT_DOUBLE_EQ(0.5, env2.left_endpoint()); + EXPECT_DOUBLE_EQ(1., env2.right_endpoint()); + + expect_good_edge(envelope); + } + + { + // 3 level sets, left is A, middle C, right B + Segment_Vector envelope = SegmentLowerEnvelope::find_lower_envelope({0., 1., 0.25}, {1., 0., 0.25}); + ASSERT_EQ(3u, envelope.size()); + const LS_Segment & env1 = envelope[0]; + const LS_Segment & env2 = envelope[1]; + const LS_Segment & env3 = envelope[2]; + EXPECT_EQ(0, env1.ls_index()); + EXPECT_EQ(2, env2.ls_index()); + EXPECT_EQ(1, env3.ls_index()); + EXPECT_DOUBLE_EQ(0.0, env1.left_endpoint()); + EXPECT_DOUBLE_EQ(0.25, env1.right_endpoint()); + EXPECT_DOUBLE_EQ(0.25, env2.left_endpoint()); + EXPECT_DOUBLE_EQ(0.75, env2.right_endpoint()); + EXPECT_DOUBLE_EQ(0.75, env3.left_endpoint()); + EXPECT_DOUBLE_EQ(1.0, env3.right_endpoint()); + + expect_good_edge(envelope); + } + + { + // 3 level sets with infinitesmal transitions near ends + const std::vector phi0 = {1.56125e-17, -4.77049e-18, 0.}; + const std::vector phi1 = {-0.0417425, 0.0477226, 0.}; + + { + Segment_Vector envelope = SegmentLowerEnvelope::find_lower_envelope(phi0, phi1); + + expect_good_edge(envelope); + expect_end_segments_to_match_end_phases(envelope, phi0, phi1); + } + { + Segment_Vector envelope = SegmentLowerEnvelope::find_lower_envelope(phi1, phi0); + + expect_good_edge(envelope); + expect_end_segments_to_match_end_phases(envelope, phi1, phi0); + } + } + + { + // 3 level sets with infinitesmal transitions near middle + const std::vector phi0 = {-1., (1.+1.e-12), 0.}; + const std::vector phi1 = {(1.+1.e-12), -1., 0.}; + + { + Segment_Vector envelope01 = SegmentLowerEnvelope::find_lower_envelope(phi0, phi1); + expect_good_edge(envelope01); + expect_end_segments_to_match_end_phases(envelope01, phi0, phi1); + EXPECT_EQ(2u, envelope01.size()); + + Segment_Vector envelope10 = SegmentLowerEnvelope::find_lower_envelope(phi1, phi0); + expect_good_edge(envelope10); + expect_end_segments_to_match_end_phases(envelope10, phi1, phi0); + EXPECT_EQ(2u, envelope10.size()); + + EXPECT_DOUBLE_EQ(envelope01[0].right_endpoint(), envelope10[0].right_endpoint()); + } + } +} + +void expect_segments_lengths(const Segment_Vector & segments, const std::vector goldSegmentLengthsByLS) +{ + for (auto && segment : segments) + { + ASSERT_TRUE(segment.ls_index() < (int)goldSegmentLengthsByLS.size()); + EXPECT_DOUBLE_EQ(goldSegmentLengthsByLS[segment.ls_index()], segment.length()); + } +} + +TEST(Lower_Envelope,Sensitive_LS) +{ + std::array,2> phi = {{ {-1.e-17,-2.e-17,1.0,0}, {2.e-17,1.0,-2.e-17,0} }}; + const double goldSegmentLengthForPhi0 = phi[0][0]/(phi[0][0]-phi[1][0]); + const std::vector goldSegmentLengthsByLS = { goldSegmentLengthForPhi0, 0., 0., 1.-goldSegmentLengthForPhi0 }; + + Segment_Vector envelope = SegmentLowerEnvelope::find_lower_envelope(phi[0], phi[1]); + expect_segments_lengths(envelope, goldSegmentLengthsByLS); +} + +} // namespace krino diff --git a/packages/krino/krino/unit_tests/Akri_Unit_MeshHelpers.cpp b/packages/krino/krino/unit_tests/Akri_Unit_MeshHelpers.cpp new file mode 100644 index 000000000000..a7a04758b117 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_MeshHelpers.cpp @@ -0,0 +1,278 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include // for declare_element +#include + +#include + +namespace krino { + +void build_mesh(stk::mesh::BulkData & mesh, const std::vector & elem_nodes, const std::vector & elem_procs, std::vector & elem_parts) +{ + if (elem_nodes.empty()) return; + + const size_t numElem = elem_nodes.size(); + EXPECT_TRUE(elem_procs.size() == numElem); + EXPECT_TRUE(elem_parts.size() == numElem); + const size_t nodesPerElem = elem_nodes[0].size(); + const int parallel_size = mesh.parallel_size(); + const int parallel_rank = mesh.parallel_rank(); + + mesh.modification_begin(); + + // Create nodes and elements + std::set nodes_I_declared; + for (size_t e=0; e 1) + { + for (size_t e=0; e sides; + stk::mesh::get_entities( mesh, mesh.mesh_meta_data().side_rank(), sides ); + + EXPECT_TRUE(1 == sides.size()); + + for (auto && side : sides) + { + EXPECT_TRUE(mesh.bucket(side).member(block_1)); + EXPECT_TRUE(mesh.bucket(side).member(block_2)); + } + + // cleanup + mesh.modification_begin(); + for (auto && side : sides) + { + EXPECT_TRUE(disconnect_and_destroy_entity(mesh, side)); + } + mesh.modification_end(); +} +} + +TEST(MeshHelpers, DeclareElementSide) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + if (parallel_size > 2) return; + + // This test will create a two element mesh (quad4 elements) on 1 or 2 processors. + + /* Mesh + * 4---5---6 P0 owns nodes 1,2,4,5; P, elem 1 + * | 1 | 2 | P1 : 3,6, elem 2 + * 1---2---3 + */ + + unsigned spatialDim = 2; + stk::mesh::MetaData meta(spatialDim); + stk::mesh::BulkData mesh(meta, pm); + + stk::mesh::Part& block_1 = meta.declare_part_with_topology("block_1", stk::topology::QUAD_4_2D); + stk::mesh::Part& block_2 = meta.declare_part_with_topology("block_2", stk::topology::QUAD_4_2D); + stk::mesh::Part& surface_1 = meta.declare_part_with_topology("surface_1", stk::topology::LINE_2); + + meta.commit(); + + const std::vector elem_nodes{ {1, 2, 5, 4}, {2, 3, 6, 5} }; + const std::vector elem_procs{0, 1}; + std::vector elem_parts{{&block_1}, {&block_2}}; + + build_mesh(mesh, elem_nodes, elem_procs, elem_parts); + + // All elements and nodes appear everywhere via aura + stk::mesh::Entity element1 = mesh.get_entity(stk::topology::ELEMENT_RANK, 1); + stk::mesh::Entity element2 = mesh.get_entity(stk::topology::ELEMENT_RANK, 2); + stk::mesh::Entity node2 = mesh.get_entity(stk::topology::NODE_RANK, 2); + stk::mesh::Entity node5 = mesh.get_entity(stk::topology::NODE_RANK, 5); + + std::vector side_nodes; + side_nodes.push_back(node2); + side_nodes.push_back(node5); + + mesh.initialize_face_adjacent_element_graph(); + + { + // Case 1: Declare side on owning processor for both elements + mesh.modification_begin(); + + if (mesh.bucket(element1).owned()) + { + const unsigned elem1_local_side_id = stk::mesh::get_entity_subcell_id(mesh, element1, meta.side_rank(), surface_1.topology(), side_nodes); + mesh.declare_element_side(element1, elem1_local_side_id, stk::mesh::ConstPartVector{&surface_1}); + } + if (mesh.bucket(element2).owned()) + { + const unsigned elem2_local_side_id = stk::mesh::get_entity_subcell_id(mesh, element2, meta.side_rank(), surface_1.topology(), side_nodes); + mesh.declare_element_side(element2, elem2_local_side_id, stk::mesh::ConstPartVector{&surface_1}); + } + + mesh.modification_end(); + + test_and_cleanup_internal_side(mesh, block_1, block_2); + } + + { + // Case 2: Declare side of element1 on processor that owns element1 + mesh.modification_begin(); + + if (mesh.bucket(element1).owned()) + { + const unsigned elem1_local_side_id = stk::mesh::get_entity_subcell_id(mesh, element1, meta.side_rank(), surface_1.topology(), side_nodes); + mesh.declare_element_side(element1, elem1_local_side_id, stk::mesh::ConstPartVector{&surface_1}); + } + + mesh.modification_end(); + + test_and_cleanup_internal_side(mesh, block_1, block_2); + } + + { + // Case 3: Declare side of element2 on processor that owns element2 + mesh.modification_begin(); + + if (mesh.bucket(element2).owned()) + { + const unsigned elem2_local_side_id = stk::mesh::get_entity_subcell_id(mesh, element2, meta.side_rank(), surface_1.topology(), side_nodes); + mesh.declare_element_side(element2, elem2_local_side_id, stk::mesh::ConstPartVector{&surface_1}); + } + + mesh.modification_end(); + + test_and_cleanup_internal_side(mesh, block_1, block_2); + } +} + +TEST(MeshHelpers, FullyCoincidentVolumeElements) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + + // This test will create a two element mesh (quad4 elements), on more than 2 processors only + // ranks 0 and 1 will have any elements. We test larger number of processors to ensure that + // we get a parallel-consistent result to avoid potential parallel hangs in the full app. + + unsigned spatialDim = 2; + stk::mesh::MetaData meta(spatialDim); + stk::mesh::BulkData mesh(meta, pm); + + stk::mesh::Part& block_1 = meta.declare_part_with_topology("block_1", stk::topology::QUAD_4_2D); + stk::mesh::Part& active_part = meta.declare_part("active"); + meta.commit(); + + const std::vector elem_nodes{ {1, 2, 3, 4}, {2, 3, 4, 1} }; + const std::vector elem_procs{0, 1}; + std::vector elem_parts{{&block_1}, {&block_1}}; + + build_mesh(mesh, elem_nodes, elem_procs, elem_parts); + + const bool ok = check_coincident_elements(mesh, active_part); + EXPECT_FALSE(ok); +} + +TEST(MeshHelpers, PartiallyCoincidentActiveVolumeElements) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + if (parallel_size > 2) return; + + // This test will create a two element mesh (quad4 elements) on 1 or 2 processors. + + unsigned spatialDim = 2; + stk::mesh::MetaData meta(spatialDim); + stk::mesh::BulkData mesh(meta, pm); + + stk::mesh::Part& block_1 = meta.declare_part_with_topology("block_1", stk::topology::QUAD_4_2D); + stk::mesh::Part& active_part = meta.declare_part("active"); + meta.commit(); + + const std::vector elem_nodes{ {1, 2, 3, 4}, {1, 2, 5, 6} }; + const std::vector elem_procs{0, 1}; + std::vector elem_parts{{&block_1, &active_part}, {&block_1, &active_part}}; + + build_mesh(mesh, elem_nodes, elem_procs, elem_parts); + + const bool ok = check_coincident_elements(mesh, active_part); + EXPECT_FALSE(ok); +} + +TEST(MeshHelpers, NotCoincidentActiveDegenerateVolumeElements) +{ + stk::ParallelMachine pm = MPI_COMM_WORLD; + const int parallel_size = stk::parallel_machine_size(pm); + if (parallel_size > 2) return; + + // This test will create a two element mesh (quad4 elements) on 1 or 2 processors. + + unsigned spatialDim = 2; + stk::mesh::MetaData meta(spatialDim); + stk::mesh::BulkData mesh(meta, pm); + + stk::mesh::Part& block_1 = meta.declare_part_with_topology("block_1", stk::topology::QUAD_4_2D); + stk::mesh::Part& active_part = meta.declare_part("active"); + meta.commit(); + + const std::vector elem_nodes{ {1, 2, 2, 3}, {3, 2, 2, 4} }; + const std::vector elem_procs{0, 1}; + std::vector elem_parts{{&block_1, &active_part}, {&block_1, &active_part}}; + + build_mesh(mesh, elem_nodes, elem_procs, elem_parts); + + const bool ok = check_coincident_elements(mesh, active_part); + EXPECT_TRUE(ok); +} + +} diff --git a/packages/krino/krino/unit_tests/Akri_Unit_MeshHelpers.hpp b/packages/krino/krino/unit_tests/Akri_Unit_MeshHelpers.hpp new file mode 100644 index 000000000000..976a24bacbcd --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_MeshHelpers.hpp @@ -0,0 +1,24 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef KRINO_UNIT_TESTS_INCLUDE_AKRI_UNIT_MESHHELPERS_H_ +#define KRINO_UNIT_TESTS_INCLUDE_AKRI_UNIT_MESHHELPERS_H_ + +#include +#include + +namespace krino +{ + +void build_mesh(stk::mesh::BulkData & mesh, + const std::vector & elem_nodes, + const std::vector & elem_procs, + std::vector & elem_parts); +} + +#endif /* KRINO_UNIT_TESTS_INCLUDE_AKRI_UNIT_MESHHELPERS_H_ */ diff --git a/packages/krino/krino/unit_tests/Akri_Unit_MortonIndex.cpp b/packages/krino/krino/unit_tests/Akri_Unit_MortonIndex.cpp new file mode 100644 index 000000000000..805731063269 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_MortonIndex.cpp @@ -0,0 +1,52 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include +#include +#include + +namespace krino +{ + +TEST(MortonIndex, specified_index) +{ + const std::vector valid_indices {0, 1, 2, 4, 3434, 23, 1414415, 76285, MAX_VALID_MORTON3D_INDEX}; + for (int valid_index : valid_indices) + { + EXPECT_EQ(valid_index, morton3d_decode_index(morton3d_encode_index(valid_index))); + } +#ifndef NDEBUG + const std::vector invalid_indices {MAX_VALID_MORTON3D_INDEX+1, 3000000, 2147483647}; + for (int invalid_index : invalid_indices) + { + EXPECT_ANY_THROW(morton3d_encode_index(invalid_index)); + } +#endif +} + +TEST(MortonIndex, specified_indices) +{ + const std::vector valid_indices {0, 1, 2, 4, 3434, 23, 1414415, 76285, MAX_VALID_MORTON3D_INDEX}; + for (int ix : valid_indices) + { + for (int iy : valid_indices) + { + for (int iz : valid_indices) + { + std::array out_indices = morton3d_decode_indices(morton3d_encode_indices({{ix,iy,iz}})); + EXPECT_EQ(ix, out_indices[0]); + EXPECT_EQ(iy, out_indices[1]); + EXPECT_EQ(iz, out_indices[2]); + } + } + } +} + +} // namespace krino diff --git a/packages/krino/krino/unit_tests/Akri_Unit_ParallelErrorMessage.cpp b/packages/krino/krino/unit_tests/Akri_Unit_ParallelErrorMessage.cpp new file mode 100644 index 000000000000..99d3c4f1a609 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_ParallelErrorMessage.cpp @@ -0,0 +1,42 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include + +namespace krino +{ + +template void write_rank_message(T & os, const int rank) +{ + os << "Rank " << rank << " message.\n"; +} + +TEST(ParallelErrorMessage, ConcatenateErrors) +{ + stk::Parallel world_comm(MPI_COMM_WORLD); + ParallelErrorMessage err(world_comm); + + write_rank_message(err, world_comm.parallel_rank()); + auto global_message = err.gather_message(); + EXPECT_TRUE(global_message.first); + + if (world_comm.parallel_rank() == 0) + { + std::ostringstream expected; + for (int i = 0; i < world_comm.parallel_size(); ++i) + { + write_rank_message(expected, i); + expected << std::endl; + } + EXPECT_EQ(expected.str(), global_message.second); + } +} +} diff --git a/packages/krino/krino/unit_tests/Akri_Unit_Part_Decomposition_Fixture.cpp b/packages/krino/krino/unit_tests/Akri_Unit_Part_Decomposition_Fixture.cpp new file mode 100644 index 000000000000..4d56eb46bd9f --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_Part_Decomposition_Fixture.cpp @@ -0,0 +1,197 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include + +namespace krino +{ + +Part_Decomposition_Fixture::Part_Decomposition_Fixture() +: fixture(3) +{ + AuxMetaData & aux_meta = AuxMetaData::get(get_meta_data()); + aux_meta.declare_io_part_with_topology("block_1", stk::topology::TETRAHEDRON_4); + aux_meta.declare_io_part_with_topology("block_2", stk::topology::TETRAHEDRON_4); + aux_meta.declare_io_part_with_topology("surface_1", stk::topology::TRIANGLE_3); +} + +Part_Decomposition_Fixture::~Part_Decomposition_Fixture() +{ +} + +Block_Surface_Connectivity Part_Decomposition_Fixture::addOneSidedSideset() +{ + Block_Surface_Connectivity block_surface_info; + stk::mesh::PartOrdinal surface_1_ordinal = get_meta_data().get_part("surface_1")->mesh_meta_data_ordinal(); + stk::mesh::PartOrdinal block_1_ordinal = get_meta_data().get_part("block_1")->mesh_meta_data_ordinal(); + block_surface_info.add_surface(surface_1_ordinal, {block_1_ordinal}); + return block_surface_info; +} + +Block_Surface_Connectivity Part_Decomposition_Fixture::addTwoSidedSideset() +{ + Block_Surface_Connectivity block_surface_info; + stk::mesh::PartOrdinal block_1_ordinal = get_meta_data().get_part("block_1")->mesh_meta_data_ordinal(); + stk::mesh::PartOrdinal block_2_ordinal = get_meta_data().get_part("block_2")->mesh_meta_data_ordinal(); + + stk::mesh::Part & surface_1 = *get_meta_data().get_part("surface_1"); + stk::mesh::Part & surface_1_block_1 = get_meta_data().declare_part_with_topology("surface_block_1_tri3_1", stk::topology::TRIANGLE_3); + stk::mesh::Part & surface_1_block_2 = get_meta_data().declare_part_with_topology("surface_block_2_tri3_1", stk::topology::TRIANGLE_3); + get_meta_data().declare_part_subset(surface_1, surface_1_block_1); + get_meta_data().declare_part_subset(surface_1, surface_1_block_2); + block_surface_info.add_surface(surface_1.mesh_meta_data_ordinal(), {block_1_ordinal, block_2_ordinal}); + block_surface_info.add_surface(surface_1_block_1.mesh_meta_data_ordinal(), {block_1_ordinal}); + block_surface_info.add_surface(surface_1_block_2.mesh_meta_data_ordinal(), {block_2_ordinal}); + return block_surface_info; +} + +stk::mesh::MetaData & Part_Decomposition_Fixture::get_meta_data() +{ + return fixture.meta_data(); +} + +stk::mesh::Part * Part_Decomposition_Fixture::findPart(const std::string & part_name) +{ + stk::mesh::Part * result = nullptr; + const stk::mesh::PartVector & mesh_parts = fixture.meta_data().get_parts(); + stk::mesh::PartVector::const_iterator found; + found = std::find_if(mesh_parts.begin(), mesh_parts.end(), PartNameIs(part_name)); + if( found != mesh_parts.end() ) + { + result = *found; + } + return result; +} + +stk::mesh::Part * Part_Decomposition_Fixture::findSuperset(const std::string & superset_name, const stk::mesh::Part * const part) +{ + stk::mesh::Part * result = nullptr; + stk::mesh::PartVector::const_iterator found; + found = std::find_if(part->supersets().begin(), part->supersets().end(), PartNameIs(superset_name)); + if( found != part->supersets().end() ) + { + result = *found; + } + return result; +} + +PhaseVec Part_Decomposition_Fixture::ls_phases(int num_ls, bool one_phase_per_ls) +{ + static bool init = false; + static std::vector phase_tags; + static std::vector named_phases; + if(!init) + { + const LevelSet_Identifier id0(0); + const LevelSet_Identifier id1(1); + const LevelSet_Identifier id2(2); + const LevelSet_Identifier id3(3); + PhaseTag pp, nn, pn, np; + pp.add(id0,1); pp.add(id1,1); + nn.add(id0,-1); nn.add(id1,-1); + pn.add(id0,1); pn.add(id1,-1); + np.add(id0,-1); np.add(id1,1); + + phase_tags.push_back(pp); + phase_tags.push_back(nn); + phase_tags.push_back(pn); + phase_tags.push_back(np); + + named_phases.push_back(NamedPhase("A", pp)); + named_phases.push_back(NamedPhase("B", nn)); + named_phases.push_back(NamedPhase("C", pn)); + named_phases.push_back(NamedPhase("D", np)); + + PhaseTag ls1, ls2, ls3, ls4; + ls1.add(id0,-1); + ls2.add(id1,-1); + ls3.add(id2,-1); + ls4.add(id3,-1); + named_phases.push_back(NamedPhase("LS1", ls1)); + named_phases.push_back(NamedPhase("LS2", ls2)); + named_phases.push_back(NamedPhase("LS3", ls3)); + named_phases.push_back(NamedPhase("LS4", ls4)); + + init = true; + } + + PhaseVec result; + if(!one_phase_per_ls) + { + result.push_back(named_phases[0]); + result.push_back(named_phases[1]); + if(num_ls == 2) + { + result.push_back(named_phases[2]); + result.push_back(named_phases[3]); + } + } + else + { + for(int i=0; i < num_ls; ++i) + { + result.push_back(named_phases[4+i]); + } + } + return result; +} + +PhaseVec Part_Decomposition_Fixture::death_phases() +{ + static bool init = false; + const LevelSet_Identifier id0(0); + static PhaseTag pos, neg; + pos.add(id0,1); neg.add(id0,-1); + static NamedPhase dead("dead", pos), alive("", neg); + static PhaseVec named_phases; + if(!init) + { + named_phases.push_back(alive); + named_phases.push_back(dead); + init = true; + } + + return named_phases; +} + +const Interface_Name_Generator & Part_Decomposition_Fixture::ls_name_generator() +{ + static LS_Name_Generator name_gen; + return name_gen; +} + +const Interface_Name_Generator & Part_Decomposition_Fixture::death_name_generator() +{ + static Death_Name_Generator name_gen("test"); + return name_gen; +} + +void Part_Decomposition_Fixture::performDecomposition(const stk::mesh::PartVector & used_blocks, + const Block_Surface_Connectivity & input_block_surface_info, + bool cdfem_death, int num_ls, bool one_ls_per_phase) +{ + Phase_Support & phase_support = Phase_Support::get(get_meta_data()); + phase_support.set_input_block_surface_connectivity(input_block_surface_info); + if(cdfem_death) + { + phase_support.decompose_blocks(used_blocks, + Part_Decomposition_Fixture::death_phases(), Part_Decomposition_Fixture::death_name_generator()); + phase_support.build_decomposed_block_surface_connectivity(); + } + else + { + phase_support.decompose_blocks(used_blocks, + Part_Decomposition_Fixture::ls_phases(num_ls, one_ls_per_phase), Part_Decomposition_Fixture::ls_name_generator()); + phase_support.build_decomposed_block_surface_connectivity(); + } +} + +} // namespace krino diff --git a/packages/krino/krino/unit_tests/Akri_Unit_Part_Decomposition_Fixture.hpp b/packages/krino/krino/unit_tests/Akri_Unit_Part_Decomposition_Fixture.hpp new file mode 100644 index 000000000000..5239d8f69a28 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_Part_Decomposition_Fixture.hpp @@ -0,0 +1,75 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef AKRI_UNIT_PHASE_SUPPORT_H_ +#define AKRI_UNIT_PHASE_SUPPORT_H_ + +#include + +#include +#include +#include +#include + +namespace stk { namespace mesh { class Part; } } +namespace stk { namespace mesh { class MetaData; } } +namespace krino { class Block_Surface_Connectivity; } +namespace krino { class Interface_Name_Generator; } +namespace krino { class NamedPhase; } + +namespace krino +{ + +struct PartNameIs +{ + PartNameIs(const std::string & name) : match_name(name) {} + bool operator()(const stk::mesh::Part * part) { + return part->name() == match_name; + } + std::string match_name; +}; + +class Part_Decomposition_Fixture : public ::testing::Test +{ +public: + Part_Decomposition_Fixture(); + virtual ~Part_Decomposition_Fixture(); + + void performDecomposition(const std::vector & used_blocks, const Block_Surface_Connectivity & input_block_surface_info, bool cdfem_death, int num_ls = 1, bool one_ls_per_phase=false); + + stk::mesh::MetaData & get_meta_data(); + + Block_Surface_Connectivity addOneSidedSideset(); + Block_Surface_Connectivity addTwoSidedSideset(); + +protected: + stk::mesh::Part * findPart(const std::string & part_name); + stk::mesh::Part * findSuperset(const std::string & superset_name, const stk::mesh::Part * const part); + void assert_conformal_part_exists(const std::string & conformal_part_name, const std::string & nonconformal_part_name) + { + const stk::mesh::Part * conformal_part = findPart(conformal_part_name); + Phase_Support & phase_support = Phase_Support::get(get_meta_data()); + ASSERT_TRUE( conformal_part != NULL ); + EXPECT_TRUE( phase_support.is_conformal(conformal_part) ); + EXPECT_EQ(nonconformal_part_name, phase_support.find_nonconformal_part(*conformal_part)->name() ); + } + +private: + static PhaseVec ls_phases(int num_ls, bool one_ls_per_phase=false); + static PhaseVec death_phases(); + + static const Interface_Name_Generator & ls_name_generator(); + static const Interface_Name_Generator & death_name_generator(); + + SimpleStkFixture fixture; + LogRedirecter log; +}; + +} // namespace krino + +#endif /* AKRI_UNIT_PHASE_SUPPORT_H_ */ diff --git a/packages/krino/krino/unit_tests/Akri_Unit_Phase_Support.cpp b/packages/krino/krino/unit_tests/Akri_Unit_Phase_Support.cpp new file mode 100644 index 000000000000..f6b66c10a291 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_Phase_Support.cpp @@ -0,0 +1,231 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include + +#include + +namespace krino +{ + +TEST_F(Part_Decomposition_Fixture, One_Block_LS) +{ + Block_Surface_Connectivity block_surface_info; + performDecomposition({findPart("block_1")}, block_surface_info, false); + + assert_conformal_part_exists("block_1_A", "block_1_nonconformal"); + assert_conformal_part_exists("block_1_B", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_A_B", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_B_A", "block_1_nonconformal"); +} + +TEST_F(Part_Decomposition_Fixture, One_Block_Death) +{ + Block_Surface_Connectivity block_surface_info; + performDecomposition({findPart("block_1")}, block_surface_info, true); + + assert_conformal_part_exists("block_1", "block_1_nonconformal"); + assert_conformal_part_exists("block_1_dead", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_test", "block_1_nonconformal"); +} + +TEST_F(Part_Decomposition_Fixture, OneSidedSideset_LS) +{ + Block_Surface_Connectivity block_surface_info = addOneSidedSideset(); + performDecomposition({findPart("block_1")}, block_surface_info, false); + + assert_conformal_part_exists("surface_1_A", "surface_1_nonconformal"); + assert_conformal_part_exists("surface_1_B", "surface_1_nonconformal"); +} + +TEST_F(Part_Decomposition_Fixture, OneSidedSideset_Death) +{ + Block_Surface_Connectivity block_surface_info = addOneSidedSideset(); + performDecomposition({findPart("block_1")}, block_surface_info, true); + + assert_conformal_part_exists("surface_1", "surface_1_nonconformal"); + assert_conformal_part_exists("surface_1_dead", "surface_1_nonconformal"); +} + +TEST_F(Part_Decomposition_Fixture, TwoSidedSideset_LS) +{ + Block_Surface_Connectivity block_surface_info = addTwoSidedSideset(); + performDecomposition({findPart("block_1")}, block_surface_info, false); + + //const stk::mesh::Part * surf_1_A_block_1 = findPart("surface_1_A_block_1"); // No support for aliases in stk yet + const stk::mesh::Part * surf_1_A_block_1 = findPart("surface_block_1_tri3_1_A"); + ASSERT_TRUE( surf_1_A_block_1 != NULL ); + EXPECT_TRUE( findSuperset("surface_1_A", surf_1_A_block_1) != NULL ); + + //const stk::mesh::Part * surf_1_B_block_1 = findPart("surface_1_B_block_1"); // No support for aliases in stk yet + const stk::mesh::Part * surf_1_B_block_1 = findPart("surface_block_1_tri3_1_B"); + ASSERT_TRUE( surf_1_B_block_1 != NULL ); + EXPECT_TRUE( findSuperset("surface_1_B", surf_1_B_block_1) != NULL ); + + //const stk::mesh::Part * surf_1_A_block_2 = findPart("surface_1_A_block_2"); // No support for aliases in stk yet + const stk::mesh::Part * surf_1_A_block_2 = findPart("surface_block_2_tri3_1_A"); + ASSERT_TRUE( surf_1_A_block_2 != NULL ); + EXPECT_TRUE( findSuperset("surface_1_A", surf_1_A_block_2) != NULL ); + + //const stk::mesh::Part * surf_1_B_block_2 = findPart("surface_1_B_block_2"); // No support for aliases in stk yet + const stk::mesh::Part * surf_1_B_block_2 = findPart("surface_block_2_tri3_1_B"); + ASSERT_TRUE( surf_1_B_block_2 != NULL ); + EXPECT_TRUE( findSuperset("surface_1_B", surf_1_B_block_2) != NULL ); +} + +TEST_F(Part_Decomposition_Fixture, TwoSidedSideset_Death) +{ + Block_Surface_Connectivity block_surface_info = addTwoSidedSideset(); + performDecomposition({findPart("block_1")}, block_surface_info, true); + + //const stk::mesh::Part * surf_1_block_1 = findPart("surface_1_block_1"); // No support for aliases in stk yet + const stk::mesh::Part * surf_1_block_1 = findPart("surface_block_1_tri3_1"); + ASSERT_TRUE( surf_1_block_1 != NULL ); + EXPECT_TRUE( findSuperset("surface_1", surf_1_block_1) != NULL ); + + //const stk::mesh::Part * surf_1_dead_block_1 = findPart("surface_1_dead_block_1"); // No support for aliases in stk yet + const stk::mesh::Part * surf_1_dead_block_1 = findPart("surface_block_1_tri3_1_dead"); + ASSERT_TRUE( surf_1_dead_block_1 != NULL ); + EXPECT_TRUE( findSuperset("surface_1_dead", surf_1_dead_block_1) != NULL ); + + //const stk::mesh::Part * surf_1_block_2 = findPart("surface_1_block_2"); // No support for aliases in stk yet + const stk::mesh::Part * surf_1_block_2 = findPart("surface_block_2_tri3_1"); + ASSERT_TRUE( surf_1_block_2 != NULL ); + EXPECT_TRUE( findSuperset("surface_1", surf_1_block_2) != NULL ); + + //const stk::mesh::Part * surf_1_dead_block_2 = findPart("surface_1_dead_block_2"); // No support for aliases in stk yet + const stk::mesh::Part * surf_1_dead_block_2 = findPart("surface_block_2_tri3_1_dead"); + ASSERT_TRUE( surf_1_dead_block_2 != NULL ); + EXPECT_TRUE( findSuperset("surface_1_dead", surf_1_dead_block_2) != NULL ); +} + +TEST_F(Part_Decomposition_Fixture, Multiple_LS_Decomposition) +{ + Block_Surface_Connectivity block_surface_info; + performDecomposition({findPart("block_1")}, block_surface_info, false, 2); + + assert_conformal_part_exists("block_1_A", "block_1_nonconformal"); + assert_conformal_part_exists("block_1_B", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_A_B", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_B_A", "block_1_nonconformal"); + + assert_conformal_part_exists("block_1_C", "block_1_nonconformal"); + assert_conformal_part_exists("block_1_D", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_C_D", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_D_C", "block_1_nonconformal"); + + assert_conformal_part_exists("surface_block_1_A_C", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_C_A", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_A_D", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_D_A", "block_1_nonconformal"); + + assert_conformal_part_exists("surface_block_1_B_C", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_C_B", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_B_D", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_D_B", "block_1_nonconformal"); +} + +TEST_F(Part_Decomposition_Fixture, find_conformal_io_part) +{ + std::vector decomposed_blocks; + decomposed_blocks.push_back("block_1"); + Block_Surface_Connectivity block_surface_info; + + stk::mesh::Part * block_1 = findPart("block_1"); + performDecomposition({block_1}, block_surface_info, false, 2); + const LevelSet_Identifier id0(0); + const LevelSet_Identifier id1(1); + LS_SideTag p0(id0,1); + LS_SideTag p1(id1,1); + LS_SideTag n0(id0,-1); + LS_SideTag n1(id1,-1); + PhaseTag pp, nn, pn, np; + pp.add(p0); pp.add(p1); // "A" + nn.add(n0); nn.add(n1); // "B" + pn.add(p0); pn.add(n1); // "C" + np.add(n0); np.add(p1); // "D" + + // Test volume conformal io part lookup + Phase_Support & phase_support = Phase_Support::get(get_meta_data()); + const stk::mesh::Part * block_1_A = phase_support.find_conformal_io_part(*block_1, pp); + ASSERT_TRUE( block_1_A != NULL ); + EXPECT_EQ( "block_1_A", block_1_A->name() ); + + const stk::mesh::Part * block_1_B = phase_support.find_conformal_io_part(*block_1, nn); + ASSERT_TRUE( block_1_B != NULL ); + EXPECT_EQ( "block_1_B", block_1_B->name() ); + + const stk::mesh::Part * block_1_C = phase_support.find_conformal_io_part(*block_1, pn); + ASSERT_TRUE( block_1_C != NULL ); + EXPECT_EQ( "block_1_C", block_1_C->name() ); + + const stk::mesh::Part * block_1_D = phase_support.find_conformal_io_part(*block_1, np); + ASSERT_TRUE( block_1_D != NULL ); + EXPECT_EQ( "block_1_D", block_1_D->name() ); + + const stk::mesh::Part * surface_block_1_A_B = phase_support.find_interface_part(*block_1_A, *block_1_B); + ASSERT_TRUE( surface_block_1_A_B != NULL ); + EXPECT_EQ( "surface_block_1_A_B", surface_block_1_A_B->name() ); + + const stk::mesh::Part * surface_block_1_A_C = phase_support.find_interface_part(*block_1_A, *block_1_C); + ASSERT_TRUE( surface_block_1_A_C != NULL ); + EXPECT_EQ( "surface_block_1_A_C", surface_block_1_A_C->name() ); + + const stk::mesh::Part * surface_block_1_A_D = phase_support.find_interface_part(*block_1_A, *block_1_D); + ASSERT_TRUE( surface_block_1_A_D != NULL ); + EXPECT_EQ( "surface_block_1_A_D", surface_block_1_A_D->name() ); + + const stk::mesh::Part * surface_block_1_C_D = phase_support.find_interface_part(*block_1_C, *block_1_D); + ASSERT_TRUE( surface_block_1_C_D != NULL ); + EXPECT_EQ( "surface_block_1_C_D", surface_block_1_C_D->name() ); +} + +TEST_F(Part_Decomposition_Fixture, get_blocks_touching_surface) +{ + Block_Surface_Connectivity block_surface_info = addTwoSidedSideset(); + performDecomposition({findPart("block_1")}, block_surface_info, false); + + std::vector blocks; + + blocks = get_meta_data().get_blocks_touching_surface(get_meta_data().get_part("surface_1_A")); + EXPECT_EQ( 2u, blocks.size() ); + EXPECT_TRUE( blocks.end() != std::find(blocks.begin(), blocks.end(), get_meta_data().get_part("block_1_A"))); + EXPECT_TRUE( blocks.end() != std::find(blocks.begin(), blocks.end(), get_meta_data().get_part("block_2"))); + + blocks = get_meta_data().get_blocks_touching_surface(get_meta_data().get_part("surface_block_1_tri3_1_A")); + EXPECT_EQ( 1u, blocks.size() ); + EXPECT_TRUE( blocks.end() != std::find(blocks.begin(), blocks.end(), get_meta_data().get_part("block_1_A"))); + + blocks = get_meta_data().get_blocks_touching_surface(get_meta_data().get_part("surface_block_2_tri3_1_A")); + EXPECT_EQ( 1u, blocks.size() ); + EXPECT_TRUE( blocks.end() != std::find(blocks.begin(), blocks.end(), get_meta_data().get_part("block_2"))); + + blocks = get_meta_data().get_blocks_touching_surface(get_meta_data().get_part("surface_block_1_A_B")); + EXPECT_EQ( 1u, blocks.size() ); + EXPECT_TRUE( blocks.end() != std::find(blocks.begin(), blocks.end(), get_meta_data().get_part("block_1_A"))); + + blocks = get_meta_data().get_blocks_touching_surface(get_meta_data().get_part("surface_block_1_B_A")); + EXPECT_EQ( 1u, blocks.size() ); + EXPECT_TRUE( blocks.end() != std::find(blocks.begin(), blocks.end(), get_meta_data().get_part("block_1_B"))); + +} + +TEST_F(Part_Decomposition_Fixture, One_Block_Two_LS_One_LS_Per_Phase) +{ + Block_Surface_Connectivity block_surface_info; + performDecomposition({findPart("block_1")}, block_surface_info, false, 2, true); + + assert_conformal_part_exists("block_1_LS1", "block_1_nonconformal"); + assert_conformal_part_exists("block_1_LS2", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_LS1_LS2", "block_1_nonconformal"); + assert_conformal_part_exists("surface_block_1_LS2_LS1", "block_1_nonconformal"); +} + +} // namespace krino diff --git a/packages/krino/krino/unit_tests/Akri_Unit_RebalanceUtils.cpp b/packages/krino/krino/unit_tests/Akri_Unit_RebalanceUtils.cpp new file mode 100644 index 000000000000..388ddd4a5c9b --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_RebalanceUtils.cpp @@ -0,0 +1,446 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace krino { +namespace rebalance_utils { + +namespace { + +/* + * Builds a single tri mesh with 1 level of adaptivity. Parent element is ID 1, + * Child elem and node IDs are in the ascii art below + * + * 3 + * / \ + * / 5 \ + * 6 /_____\ 5 + * /\ /\ + * / \ 3 / \ + * / 2 \ / 4 \ + * /______\/_____\ + * 1 4 2 + */ + +void create_block_and_register_fields(SimpleStkFixture & fixture) +{ + auto & meta = fixture.meta_data(); + meta.declare_part_with_topology("block_1", stk::topology::TRIANGLE_3_2D); + + meta.declare_field > + (stk::topology::NODE_RANK, "coordinates"); + auto & load_field = + meta.declare_field>(stk::topology::ELEMENT_RANK, "element_weights"); + stk::mesh::put_field_on_mesh(load_field, meta.universal_part(), nullptr); + + fixture.commit(); +} + +void build_unadapted_single_tri_mesh(SimpleStkFixture & fixture) +{ + auto & bulk_data = fixture.bulk_data(); + auto & block_1 = *fixture.meta_data().get_part("block_1"); + + bulk_data.modification_begin(); + if(bulk_data.parallel_rank() == 0) + { + stk::mesh::declare_element(bulk_data, block_1, 1, {1, 2, 3}); + } + bulk_data.modification_end(); +} + +void build_one_level_adapted_single_tri_mesh(SimpleStkFixture & fixture) +{ + auto & bulk_data = fixture.bulk_data(); + auto & block_1 = *fixture.meta_data().get_part("block_1"); + + bulk_data.modification_begin(); + if(bulk_data.parallel_rank() == 0) + { + auto parent_elem = stk::mesh::declare_element(bulk_data, block_1, 1, {1, 2, 3}); + auto child1 = stk::mesh::declare_element(bulk_data, block_1, 2, {1, 4, 6}); + auto child2 = stk::mesh::declare_element(bulk_data, block_1, 3, {4, 5, 6}); + auto child3 = stk::mesh::declare_element(bulk_data, block_1, 4, {4, 2, 5}); + auto child4 = stk::mesh::declare_element(bulk_data, block_1, 5, {6, 5, 3}); + auto family_tree = bulk_data.declare_constraint(1); + bulk_data.declare_relation(family_tree, parent_elem, 0); + bulk_data.declare_relation(family_tree, child1, 1); + bulk_data.declare_relation(family_tree, child2, 2); + bulk_data.declare_relation(family_tree, child3, 3); + bulk_data.declare_relation(family_tree, child4, 4); + } + bulk_data.modification_end(); +} + +void build_two_level_adapted_single_tri_mesh(SimpleStkFixture & fixture) +{ + build_one_level_adapted_single_tri_mesh(fixture); + + auto & bulk_data = fixture.bulk_data(); + auto & block_1 = *fixture.meta_data().get_part("block_1"); + + // Refine child elem 3 an additional time and make others into transition elements + bulk_data.modification_begin(); + if(bulk_data.parallel_rank() == 0) + { + { + auto parent = bulk_data.get_entity(stk::topology::ELEMENT_RANK, 3); + auto child1 = stk::mesh::declare_element(bulk_data, block_1, 6, {4, 8, 7}); + auto child2 = stk::mesh::declare_element(bulk_data, block_1, 7, {7, 8, 9}); + auto child3 = stk::mesh::declare_element(bulk_data, block_1, 8, {8, 5, 9}); + auto child4 = stk::mesh::declare_element(bulk_data, block_1, 9, {9, 6, 7}); + + auto family_tree = bulk_data.declare_constraint(2); + bulk_data.declare_relation(family_tree, parent, 0); + bulk_data.declare_relation(family_tree, child1, 1); + bulk_data.declare_relation(family_tree, child2, 2); + bulk_data.declare_relation(family_tree, child3, 3); + bulk_data.declare_relation(family_tree, child4, 4); + } + + { + auto parent = bulk_data.get_entity(stk::topology::ELEMENT_RANK, 2); + auto child1 = stk::mesh::declare_element(bulk_data, block_1, 10, {1, 4, 7}); + auto child2 = stk::mesh::declare_element(bulk_data, block_1, 11, {1, 7, 6}); + + auto family_tree = bulk_data.declare_constraint(3); + bulk_data.declare_relation(family_tree, parent, 0); + bulk_data.declare_relation(family_tree, child1, 1); + bulk_data.declare_relation(family_tree, child2, 2); + } + { + auto parent = bulk_data.get_entity(stk::topology::ELEMENT_RANK, 4); + auto child1 = stk::mesh::declare_element(bulk_data, block_1, 12, {4, 2, 8}); + auto child2 = stk::mesh::declare_element(bulk_data, block_1, 13, {2, 5, 8}); + + auto family_tree = bulk_data.declare_constraint(4); + bulk_data.declare_relation(family_tree, parent, 0); + bulk_data.declare_relation(family_tree, child1, 1); + bulk_data.declare_relation(family_tree, child2, 2); + } + { + auto parent = bulk_data.get_entity(stk::topology::ELEMENT_RANK, 4); + auto child1 = stk::mesh::declare_element(bulk_data, block_1, 14, {9, 5, 3}); + auto child2 = stk::mesh::declare_element(bulk_data, block_1, 15, {9, 3, 6}); + + auto family_tree = bulk_data.declare_constraint(5); + bulk_data.declare_relation(family_tree, parent, 0); + bulk_data.declare_relation(family_tree, child1, 1); + bulk_data.declare_relation(family_tree, child2, 2); + } + } + bulk_data.modification_end(); +} + +} + +TEST(UpdateRebalanceForAdaptivity, OneLevel) +{ + SimpleStkFixture fixture(2, MPI_COMM_SELF); + create_block_and_register_fields(fixture); + build_one_level_adapted_single_tri_mesh(fixture); + + const auto & mesh = fixture.bulk_data(); + + stk::balance::DecompositionChangeList change_list(fixture.bulk_data(), {}); + auto parent_elem = mesh.get_entity(stk::topology::ELEMENT_RANK, 1); + + stk::mesh::EntityVector child_elems; + for(int child_id=2; child_id < 6; ++child_id) + { + auto child_elem = mesh.get_entity(stk::topology::ELEMENT_RANK, child_id); + ASSERT_TRUE(mesh.is_valid(child_elem)); + child_elems.push_back(child_elem); + } + + const int dest_proc = 2; + change_list.set_entity_destination(parent_elem, dest_proc); + + for(auto && child_elem : child_elems) + { + ASSERT_FALSE(change_list.has_entity(child_elem)); + } + + impl::update_rebalance_for_adaptivity(change_list, mesh); + + for(auto && child_elem : child_elems) + { + EXPECT_TRUE(change_list.has_entity(child_elem)); + EXPECT_EQ(dest_proc, change_list.get_entity_destination(child_elem)); + } +} + +TEST(UpdateRebalanceForAdaptivity, OneLevelChildMovedWithoutParent) +{ + SimpleStkFixture fixture(2, MPI_COMM_SELF); + create_block_and_register_fields(fixture); + build_one_level_adapted_single_tri_mesh(fixture); + + const auto & mesh = fixture.bulk_data(); + + stk::balance::DecompositionChangeList change_list(fixture.bulk_data(), {}); + + stk::mesh::EntityVector child_elems; + for(int child_id=2; child_id < 6; ++child_id) + { + auto child_elem = mesh.get_entity(stk::topology::ELEMENT_RANK, child_id); + ASSERT_TRUE(mesh.is_valid(child_elem)); + child_elems.push_back(child_elem); + change_list.set_entity_destination(child_elem, child_id); + } + + impl::update_rebalance_for_adaptivity(change_list, mesh); + + for(auto && child_elem : child_elems) + { + EXPECT_FALSE(change_list.has_entity(child_elem)); + } +} + +TEST(UpdateRebalanceForAdaptivity, TwoLevels) +{ + SimpleStkFixture fixture(2, MPI_COMM_SELF); + create_block_and_register_fields(fixture); + build_two_level_adapted_single_tri_mesh(fixture); + + const auto & mesh = fixture.bulk_data(); + + stk::balance::DecompositionChangeList change_list(fixture.bulk_data(), {}); + auto parent_elem = mesh.get_entity(stk::topology::ELEMENT_RANK, 1); + + stk::mesh::EntityVector child_elems; + for(int child_id=2; child_id < 15; ++child_id) + { + auto child_elem = mesh.get_entity(stk::topology::ELEMENT_RANK, child_id); + ASSERT_TRUE(mesh.is_valid(child_elem)); + child_elems.push_back(child_elem); + } + + const int dest_proc = 2; + change_list.set_entity_destination(parent_elem, dest_proc); + + impl::update_rebalance_for_adaptivity(change_list, mesh); + + for(auto && child_elem : child_elems) + { + EXPECT_TRUE(change_list.has_entity(child_elem)); + EXPECT_EQ(dest_proc, change_list.get_entity_destination(child_elem)); + } + + stk::mesh::EntityVector family_trees; + mesh.get_entities( + stk::topology::CONSTRAINT_RANK, mesh.mesh_meta_data().locally_owned_part(), family_trees); + for(auto && ft : family_trees) + { + EXPECT_TRUE(change_list.has_entity(ft)); + EXPECT_EQ(dest_proc, change_list.get_entity_destination(ft)); + } +} + +TEST(UpdateRebalanceForAdaptivity, TwoLevelsFirstLevelChildInitialDifferentProc) +{ + SimpleStkFixture fixture(2, MPI_COMM_SELF); + create_block_and_register_fields(fixture); + build_two_level_adapted_single_tri_mesh(fixture); + + const auto & mesh = fixture.bulk_data(); + + stk::balance::DecompositionChangeList change_list(fixture.bulk_data(), {}); + auto parent_elem = mesh.get_entity(stk::topology::ELEMENT_RANK, 1); + + stk::mesh::EntityVector child_elems; + for(int child_id=2; child_id < 15; ++child_id) + { + auto child_elem = mesh.get_entity(stk::topology::ELEMENT_RANK, child_id); + ASSERT_TRUE(mesh.is_valid(child_elem)); + child_elems.push_back(child_elem); + } + + const int dest_proc = 2; + change_list.set_entity_destination(parent_elem, dest_proc); + change_list.set_entity_destination(mesh.get_entity(stk::topology::ELEMENT_RANK, 3), 5); + + impl::update_rebalance_for_adaptivity(change_list, mesh); + + for(auto && child_elem : child_elems) + { + EXPECT_TRUE(change_list.has_entity(child_elem)); + EXPECT_EQ(dest_proc, change_list.get_entity_destination(child_elem)); + } +} + +TEST(AccumulateAdaptivityChildWeights, TwoLevelAdaptedTri) +{ + SimpleStkFixture fixture(2, MPI_COMM_SELF); + create_block_and_register_fields(fixture); + build_two_level_adapted_single_tri_mesh(fixture); + + const auto & mesh = fixture.bulk_data(); + + stk::mesh::Field & weights_field = static_cast &>( + *fixture.meta_data().get_field(stk::topology::ELEMENT_RANK, "element_weights")); + + auto parent_elem = mesh.get_entity(stk::topology::ELEMENT_RANK, 1); + double & parent_weight = *stk::mesh::field_data(weights_field, parent_elem); + parent_weight = 10.; + stk::mesh::EntityVector child_elems; + for (int child_id = 2; child_id < 15; ++child_id) + { + auto child_elem = mesh.get_entity(stk::topology::ELEMENT_RANK, child_id); + ASSERT_TRUE(mesh.is_valid(child_elem)); + child_elems.push_back(child_elem); + + double & weight = *stk::mesh::field_data(weights_field, child_elem); + weight = 1.; + } + + impl::accumulate_adaptivity_child_weights_to_parents(mesh, weights_field); + + for (auto && child_elem : child_elems) + { + const double & weight = *stk::mesh::field_data(weights_field, child_elem); + EXPECT_DOUBLE_EQ(0., weight); + } + EXPECT_DOUBLE_EQ(23., parent_weight); +} + +TEST(AccumulateAdaptivityChildWeights, UnadaptedElement) +{ + SimpleStkFixture fixture(2, MPI_COMM_SELF); + create_block_and_register_fields(fixture); + build_unadapted_single_tri_mesh(fixture); + + const auto & mesh = fixture.bulk_data(); + + stk::mesh::Field & weights_field = static_cast &>( + *fixture.meta_data().get_field(stk::topology::ELEMENT_RANK, "element_weights")); + + auto parent_elem = mesh.get_entity(stk::topology::ELEMENT_RANK, 1); + double & parent_weight = *stk::mesh::field_data(weights_field, parent_elem); + parent_weight = 10.; + + impl::accumulate_adaptivity_child_weights_to_parents(mesh, weights_field); + + EXPECT_DOUBLE_EQ(10., parent_weight); +} + +TEST(Rebalance, MultipleWeightFields) +{ + SimpleStkFixture fixture(2, MPI_COMM_WORLD); + + auto & mesh = fixture.bulk_data(); + auto & meta = fixture.meta_data(); + + if (mesh.parallel_size() != 2) return; + + stk::mesh::Part & block_1 = meta.declare_part_with_topology("block_1", stk::topology::QUAD_4_2D); + stk::mesh::Part & block_2 = meta.declare_part_with_topology("block_2", stk::topology::QUAD_4_2D); + + auto & coords_field = meta.declare_field>( + stk::topology::NODE_RANK, "coordinates"); + stk::mesh::put_field_on_mesh(coords_field, meta.universal_part(), nullptr); + + auto & weights_field_1 = meta.declare_field>( + stk::topology::ELEMENT_RANK, "element_weights_1"); + stk::mesh::put_field_on_mesh(weights_field_1, block_1, nullptr); + + auto & weights_field_2 = meta.declare_field>( + stk::topology::ELEMENT_RANK, "element_weights_2"); + stk::mesh::put_field_on_mesh(weights_field_2, block_2, nullptr); + + meta.commit(); + + // Create mesh with two disconnected blocks each containing two elements. Initially block_1 will + // be + // all owned by P0 and block_2 all by P1. Each element gets a weight of 1 in the weight field for + // its + // block, rebalance should put one element from each block on each proc. + const std::vector elem_nodes{ + {1, 2, 5, 4}, {2, 3, 6, 5}, {7, 8, 11, 12}, {8, 9, 10, 11}}; + const std::vector elem_procs{0, 0, 1, 1}; + std::vector elem_parts{{&block_1}, {&block_1}, {&block_2}, {&block_2}}; + std::vector> node_coords{{0, 0}, + {1, 0}, + {2, 0}, + {2, 1}, + {1, 1}, + {0, 1}, + {3, 0}, + {4, 0}, + {5, 0}, + {3, 1}, + {4, 1}, + {5, 1}}; + + build_mesh(mesh, elem_nodes, elem_procs, elem_parts); + + for (auto && b : mesh.buckets(stk::topology::NODE_RANK)) + { + for (auto && node : *b) + { + double * coords = stk::mesh::field_data(coords_field, node); + coords[0] = node_coords[mesh.identifier(node) - 1][0]; + coords[1] = node_coords[mesh.identifier(node) - 1][1]; + } + } + + stk::mesh::field_fill(1., weights_field_1); + stk::mesh::field_fill(1., weights_field_2); + + const auto parallel_rank = mesh.parallel_rank(); + const auto & owned_part = meta.locally_owned_part(); + if (parallel_rank == 0) + { + EXPECT_EQ(2u, + stk::mesh::count_selected_entities( + owned_part & block_1, mesh.buckets(stk::topology::ELEMENT_RANK))); + EXPECT_EQ(0u, + stk::mesh::count_selected_entities( + owned_part & block_2, mesh.buckets(stk::topology::ELEMENT_RANK))); + } + if (parallel_rank == 1) + { + EXPECT_EQ(0u, + stk::mesh::count_selected_entities( + owned_part & block_1, mesh.buckets(stk::topology::ELEMENT_RANK))); + EXPECT_EQ(2u, + stk::mesh::count_selected_entities( + owned_part & block_2, mesh.buckets(stk::topology::ELEMENT_RANK))); + } + + rebalance_utils::rebalance_mesh( + mesh, nullptr, {weights_field_1.name(), weights_field_2.name()}, "coordinates", "parmetis"); + + EXPECT_EQ(1u, + stk::mesh::count_selected_entities( + owned_part & block_1, mesh.buckets(stk::topology::ELEMENT_RANK))); + EXPECT_EQ(1u, + stk::mesh::count_selected_entities( + owned_part & block_2, mesh.buckets(stk::topology::ELEMENT_RANK))); +} +} +} + diff --git a/packages/krino/krino/unit_tests/Akri_Unit_Single_Element_Fixtures.cpp b/packages/krino/krino/unit_tests/Akri_Unit_Single_Element_Fixtures.cpp new file mode 100644 index 000000000000..6db2f2adce90 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_Single_Element_Fixtures.cpp @@ -0,0 +1,136 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace krino { + +void SimpleStkFixture::write_results(const std::string & filename, stk::mesh::BulkData & mesh, const bool use64bitIds) +{ + stk::io::StkMeshIoBroker io(mesh.parallel()); + io.set_bulk_data(mesh); + + Ioss::PropertyManager properties; + if (use64bitIds) + { + properties.add(Ioss::Property("INTEGER_SIZE_API", 8)); + properties.add(Ioss::Property("INTEGER_SIZE_DB", 8)); + } + + auto index = io.create_output_mesh(filename, stk::io::WRITE_RESULTS, properties); + io.write_output_mesh(index); + + for(auto && field : mesh.mesh_meta_data().get_fields()) + { + io.add_field(index, *field); + } + + io.begin_output_step(index, 0.); + io.write_defined_output_fields(index); + io.end_output_step(index); +} + +SingleElementFixture::SingleElementFixture(const stk::topology & topology) + : my_topology(topology), + stk_fixture(my_topology.dimension()) +{ + AuxMetaData & aux_meta = AuxMetaData::get(stk_fixture.meta_data()); + block_part = &stk_fixture.meta_data().declare_part_with_topology("block_1", my_topology); + const FieldType & vec_type = (my_topology.dimension() == 3) ? FieldType::VECTOR_3D : FieldType::VECTOR_2D; + coord_field = aux_meta.register_field("coordinates", vec_type, stk::topology::NODE_RANK, 1u, 1u, *block_part); + CDFEM_Support::get(stk_fixture.meta_data()).set_coords_field( coord_field ); + scalar_field = aux_meta.register_field("scalar_field", FieldType::REAL, stk::topology::NODE_RANK, 1u, 1u, *block_part); + stk_fixture.commit(); +} + +void +SingleElementFixture::generate_mesh() +{ + const AuxMetaData & aux_meta = AuxMetaData::get(stk_fixture.meta_data()); + stk_fixture.bulk_data().modification_begin(); + { + stk::mesh::PartVector elem_parts; + elem_parts.push_back(block_part); + elem_parts.push_back(&aux_meta.active_part()); + elem_parts.push_back(&stk_fixture.meta_data().locally_owned_part()); + stk::mesh::EntityIdVector node_ids(my_topology.num_nodes()); + for(unsigned i=0; i < node_ids.size(); ++i) + { + node_ids[i] = i+1; + } + const stk::mesh::EntityId elem_id = 1; + my_elem = stk::mesh::declare_element( stk_fixture.bulk_data(), elem_parts, elem_id, node_ids ); + const stk::mesh::Entity * const nodes = stk_fixture.bulk_data().begin_nodes(my_elem); + for(unsigned i=0; i < node_ids.size(); ++i) + { + EXPECT_EQ(node_ids[i], stk_fixture.bulk_data().identifier(nodes[i])); + } + } + stk_fixture.bulk_data().modification_end(); + + // set node coordinates + stk::mesh::field_fill(0., coord_field); + const stk::mesh::Entity * const nodes = stk_fixture.bulk_data().begin_nodes(my_elem); + for(unsigned i=0; i < my_topology.num_nodes(); ++i) + { + if (i > 0) + { + double * node_coords = field_data(coord_field, nodes[i]); + node_coords[i-1] = 1.0; + } + } +} + +TEST(SingleElementFixture, tri3) +{ + stk::topology tri3 = stk::topology::TRIANGLE_3_2D; + SingleElementFixture test_fixture(tri3); + + test_fixture.generate_mesh(); + + EXPECT_EQ(2u, test_fixture.stk_fixture.meta_data().spatial_dimension()); + + const stk::mesh::BucketVector & elem_buckets = test_fixture.stk_fixture.bulk_data().buckets(stk::topology::ELEMENT_RANK); + ASSERT_EQ(1u, elem_buckets.size()); + EXPECT_EQ(1u, (*elem_buckets.begin())->size()); + EXPECT_EQ(3u, test_fixture.stk_fixture.bulk_data().num_nodes((*elem_buckets[0])[0])); + const stk::mesh::BucketVector & node_buckets = test_fixture.stk_fixture.bulk_data().buckets(stk::topology::NODE_RANK); + ASSERT_EQ(1u, node_buckets.size()); + EXPECT_EQ(3u, (*node_buckets.begin())->size()); +} + +TEST(SingleElementFixture, tet4) +{ + stk::topology tet4 = stk::topology::TETRAHEDRON_4; + SingleElementFixture test_fixture(tet4); + + test_fixture.generate_mesh(); + + EXPECT_EQ(3u, test_fixture.stk_fixture.meta_data().spatial_dimension()); + + const stk::mesh::BucketVector & elem_buckets = test_fixture.stk_fixture.bulk_data().buckets(stk::topology::ELEMENT_RANK); + ASSERT_EQ(1u, elem_buckets.size()); + EXPECT_EQ(1u, (*elem_buckets.begin())->size()); + EXPECT_EQ(4u, test_fixture.stk_fixture.bulk_data().num_nodes((*elem_buckets[0])[0])); + const stk::mesh::BucketVector & node_buckets = test_fixture.stk_fixture.bulk_data().buckets(stk::topology::NODE_RANK); + ASSERT_EQ(1u, node_buckets.size()); + EXPECT_EQ(4u, (*node_buckets.begin())->size()); +} + +} diff --git a/packages/krino/krino/unit_tests/Akri_Unit_Single_Element_Fixtures.hpp b/packages/krino/krino/unit_tests/Akri_Unit_Single_Element_Fixtures.hpp new file mode 100644 index 000000000000..f3d969b484ba --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_Single_Element_Fixtures.hpp @@ -0,0 +1,79 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef AKRI_UNIT_SINGLE_ELEMENT_FIXTURES_H_ +#define AKRI_UNIT_SINGLE_ELEMENT_FIXTURES_H_ + +#include // for BulkData +#include // for MetaData + +#include "Akri_AuxMetaData.hpp" +#include "Akri_FieldRef.hpp" + +namespace krino { + +inline std::vector entity_rank_names_with_ft() +{ + auto entity_rank_names = stk::mesh::entity_rank_names(); + entity_rank_names.push_back("FAMILY_TREE"); + return entity_rank_names; +} + +class SimpleStkFixture +{ +public: + SimpleStkFixture(unsigned dimension, MPI_Comm comm = MPI_COMM_WORLD) + : meta(dimension, entity_rank_names_with_ft()), + bulk(meta, comm) + { + meta.set_mesh_bulk_data(&bulk); + AuxMetaData::create(meta); + } + void commit() { meta.commit(); } + void write_results(const std::string & filename) { write_results(filename, bulk); } + static void write_results(const std::string & filename, stk::mesh::BulkData & mesh, const bool use64bitIds = true); + stk::mesh::MetaData & meta_data() { return meta; } + stk::mesh::BulkData & bulk_data() { return bulk; } + +private: + stk::mesh::MetaData meta; + stk::mesh::BulkData bulk; +}; + +class SimpleStkFixture2d : public SimpleStkFixture +{ +public: + SimpleStkFixture2d(MPI_Comm comm = MPI_COMM_WORLD) : SimpleStkFixture(2) {} +}; + +class SimpleStkFixture3d : public SimpleStkFixture +{ +public: + SimpleStkFixture3d(MPI_Comm comm = MPI_COMM_WORLD) : SimpleStkFixture(3) {} +}; + +// Fixture to create a single element stk mesh of the given topology. +class SingleElementFixture +{ +public: + SingleElementFixture(const stk::topology & topology); + + void generate_mesh(); + + stk::topology my_topology; + SimpleStkFixture stk_fixture; + stk::mesh::Part * block_part; + FieldRef coord_field; + FieldRef scalar_field; + stk::mesh::Entity my_elem; +}; + + +} + +#endif /* AKRI_UNIT_SINGLE_ELEMENT_FIXTURES_H_ */ diff --git a/packages/krino/krino/unit_tests/Akri_Unit_main.cpp b/packages/krino/krino/unit_tests/Akri_Unit_main.cpp new file mode 100644 index 000000000000..d079d13ad299 --- /dev/null +++ b/packages/krino/krino/unit_tests/Akri_Unit_main.cpp @@ -0,0 +1,32 @@ +// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering +// Solutions of Sandia, LLC (NTESS). Under the terms of Contract +// DE-NA0003525 with NTESS, the U.S. Government retains certain rights +// in this software. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include + +#include +#include +#include + +#include + +int main(int argc, char **argv) { + Kokkos::ScopeGuard guard(argc, argv); + + sierra::Env::set_input_file_required(false); + + testing::InitGoogleTest(&argc, argv); + + krino::Startup startup__(argc, argv); + + stk::unit_test_util::create_parallel_output(sierra::Env::parallel_rank()); + + return RUN_ALL_TESTS(); +} diff --git a/packages/krino/krino/unit_tests/CMakeLists.txt b/packages/krino/krino/unit_tests/CMakeLists.txt new file mode 100644 index 000000000000..3d92207e1423 --- /dev/null +++ b/packages/krino/krino/unit_tests/CMakeLists.txt @@ -0,0 +1,14 @@ + +INCLUDE_DIRECTORIES(${${PACKAGE_NAME}_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +FILE(GLOB SOURCES *.cpp) + +TRIBITS_ADD_EXECUTABLE_AND_TEST( + krino_unit + SOURCES ${SOURCES} + COMM serial mpi + NUM_MPI_PROCS 1-4 + NOEXEPREFIX NOEXESUFFIX + ) diff --git a/packages/minitensor/src/MiniTensor_LinearAlgebra.h b/packages/minitensor/src/MiniTensor_LinearAlgebra.h index cf373bfd617c..c702a66486df 100644 --- a/packages/minitensor/src/MiniTensor_LinearAlgebra.h +++ b/packages/minitensor/src/MiniTensor_LinearAlgebra.h @@ -183,10 +183,7 @@ I3(Tensor const & A); /// Exponential map. /// \return \f$ \exp A \f$ /// -template -KOKKOS_INLINE_FUNCTION -Tensor -exp(Tensor const & A); +template Tensor exp(Tensor const &A); /// /// Exponential map by Taylor series, radius of convergence is infinity @@ -202,10 +199,7 @@ exp_taylor(Tensor const & A); /// See algorithm 10.20 in Functions of Matrices, N.J. Higham, SIAM, 2008. /// \return \f$ \exp A \f$ /// -template -KOKKOS_INLINE_FUNCTION -Tensor -exp_pade(Tensor const & A); +template Tensor exp_pade(Tensor const &A); /// /// Logarithmic map. @@ -338,20 +332,16 @@ norm_off_diagonal(Tensor const & A); /// that rely on Jacobi-type procedures. /// \return \f$ (p,q) = arg max_{i,j} |a_{ij}| \f$ /// -template -KOKKOS_INLINE_FUNCTION -std::pair -arg_max_abs(Tensor const & A); +template +std::pair arg_max_abs(Tensor const &A); /// /// Arg max off-diagonal. Useful for SVD and other algorithms /// that rely on Jacobi-type procedures. /// \return \f$ (p,q) = arg max_{i \neq j} |a_{ij}| \f$ /// -template -KOKKOS_INLINE_FUNCTION -std::pair -arg_max_off_diagonal(Tensor const & A); +template +std::pair arg_max_off_diagonal(Tensor const &A); /// /// Sort and index. Useful for ordering singular values @@ -360,18 +350,15 @@ arg_max_off_diagonal(Tensor const & A); /// \param u vector to sort /// \return v P sorted vector, permutation matrix such that v = P^T u /// -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -sort_permutation(Vector const & u); +template +std::pair, Tensor> sort_permutation(Vector const &u); /// /// Singular value decomposition (SVD) /// \return \f$ A = USV^T\f$ /// -template -boost::tuple, Tensor, Tensor> -svd(Tensor const & A); +template +std::tuple, Tensor, Tensor> svd(Tensor const &A); /// /// Project to O(N) (Orthogonal Group) using a Newton-type algorithm. @@ -392,62 +379,54 @@ polar_rotation(Tensor const & A); /// \param A tensor (often a deformation-gradient-like tensor) /// \return \f$ VR = A \f$ with \f$ R \in SO(N) \f$ and \f$ V \in SPD(N) \f$ /// -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -polar_left(Tensor const & A); +template +std::pair, Tensor> polar_left(Tensor const &A); /// /// Right polar decomposition /// \param A tensor (often a deformation-gradient-like tensor) /// \return \f$ RU = A \f$ with \f$ R \in SO(N) \f$ and \f$ U \in SPD(N) \f$ /// -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -polar_right(Tensor const & A); +template +std::pair, Tensor> polar_right(Tensor const &A); /// /// Left polar decomposition computed with eigenvalue decomposition /// \param A tensor (often a deformation-gradient-like tensor) /// \return \f$ VR = A \f$ with \f$ R \in SO(N) \f$ and \f$ V \in SPD(N) \f$ /// -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -polar_left_eig(Tensor const & A); +template +std::pair, Tensor> polar_left_eig(Tensor const &A); /// /// R^3 right polar decomposition /// \param A tensor (often a deformation-gradient-like tensor) /// \return \f$ RU = F \f$ with \f$ R \in SO(N) \f$ and \f$ U \in SPD(N) \f$ /// -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -polar_right_eig(Tensor const & A); +template +std::pair, Tensor> polar_right_eig(Tensor const &A); /// /// Left polar decomposition with matrix logarithm for V /// \param F tensor (often a deformation-gradient-like tensor) /// \return \f$ VR = F \f$ with \f$ R \in SO(N) \f$ and V SPD, and log V /// -template -boost::tuple, Tensor, Tensor> -polar_left_logV(Tensor const & F); +template +std::tuple, Tensor, Tensor> +polar_left_logV(Tensor const &F); -template -boost::tuple, Tensor, Tensor> -polar_left_logV_eig(Tensor const & F); +template +std::tuple, Tensor, Tensor> +polar_left_logV_eig(Tensor const &F); /// /// Left polar decomposition with matrix logarithm for V using eig_spd_cos /// \param F tensor (often a deformation-gradient-like tensor) /// \return \f$ VR = F \f$ with \f$ R \in SO(N) \f$ and V SPD, and log V /// -template -boost::tuple, Tensor, Tensor> -polar_left_logV_lame(Tensor const & F); +template +std::tuple, Tensor, Tensor> +polar_left_logV_lame(Tensor const &F); /// /// Logarithmic map using BCH expansion (4 terms) @@ -465,37 +444,28 @@ bch(Tensor const & v, Tensor const & r); /// \param \f$ A = [f, g; g, h] \in S(2) \f$ /// \return \f$ c, s \rightarrow [c, -s; s, c]\f$ diagonalizes A$ /// -template -KOKKOS_INLINE_FUNCTION -std::pair -schur_sym(const T f, const T g, const T h); +template +std::pair schur_sym(const T f, const T g, const T h); /// /// Givens rotation. [c, -s; s, c] [a; b] = [r; 0] /// \return c and s /// -template -KOKKOS_INLINE_FUNCTION -std::pair -givens(T const & a, T const & b); +template std::pair givens(T const &a, T const &b); /// /// Eigenvalue decomposition for symmetric 2nd-order tensor /// \return V eigenvectors, D eigenvalues in diagonal Matlab-style /// -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -eig_sym(Tensor const & A); +template +std::pair, Tensor> eig_sym(Tensor const &A); /// /// Eigenvalue decomposition for SPD 2nd-order tensor /// \return V eigenvectors, D eigenvalues in diagonal Matlab-style /// -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -eig_spd(Tensor const & A); +template +std::pair, Tensor> eig_spd(Tensor const &A); /// /// Eigenvalue decomposition for SPD 2nd-order tensor @@ -503,10 +473,8 @@ eig_spd(Tensor const & A); /// This algorithm comes from the journal article /// Scherzinger and Dohrmann, CMAME 197 (2008) 4007-4015 /// -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -eig_spd_cos(Tensor const & A); +template +std::pair, Tensor> eig_spd_cos(Tensor const &A); /// /// Cholesky decomposition, rank-1 update algorithm @@ -515,10 +483,8 @@ eig_spd_cos(Tensor const & A); /// \return G Cholesky factor A = GG^T and completed (bool) /// algorithm ran to completion /// -template -KOKKOS_INLINE_FUNCTION -std::pair, bool > -cholesky(Tensor const & A); +template +std::pair, bool> cholesky(Tensor const &A); /// /// Preconditioner types @@ -535,10 +501,9 @@ enum class PreconditionerType /// Compute a preconditioner for improving the conditioning of a /// linear system. /// -template -KOKKOS_INLINE_FUNCTION -std::pair, RHS> -precon(PreconditionerType const pt, Tensor const & A, RHS const & B); +template +std::pair, RHS> precon(PreconditionerType const pt, + Tensor const &A, RHS const &B); /// /// Solve linear system of equations. @@ -551,11 +516,9 @@ precon(PreconditionerType const pt, Tensor const & A, RHS const & B); /// \param b rhs of the system Ax=b /// \return x solution(s) to the system Ax=b /// -template -KOKKOS_INLINE_FUNCTION -RHS -solve(Tensor const & A, RHS const & b, - PreconditionerType const pt = PreconditionerType::IDENTITY); +template +RHS solve(Tensor const &A, RHS const &b, + PreconditionerType const pt = PreconditionerType::IDENTITY); template KOKKOS_INLINE_FUNCTION @@ -565,18 +528,12 @@ solve_full_pivot(Tensor const & A, RHS const & b); /// /// Condition number: ratio of largest to smalest singular values. /// -template -KOKKOS_INLINE_FUNCTION -T -cond(Tensor const & A); +template T cond(Tensor const &A); /// /// Reciprocal condition number: ratio of smallest to largest singular values. /// -template -KOKKOS_INLINE_FUNCTION -T -inv_cond(Tensor const & A); +template T inv_cond(Tensor const &A); } // namespace minitensor diff --git a/packages/minitensor/src/MiniTensor_LinearAlgebra.i.h b/packages/minitensor/src/MiniTensor_LinearAlgebra.i.h index 91e6123822dd..9b57ef699f94 100644 --- a/packages/minitensor/src/MiniTensor_LinearAlgebra.i.h +++ b/packages/minitensor/src/MiniTensor_LinearAlgebra.i.h @@ -107,40 +107,39 @@ norm_1(Tensor const & A) switch (dimension) { - default: - - for (Index i = 0; i < dimension; ++i) { - T t = 0.0; - for (Index j = 0; j < dimension; ++j) { - t += std::abs(A(j, i)); - } - v(i) = t; - } + default: - for (Index i = 0; i < dimension; ++i) { - s = std::max(s, v(i)); + for (Index i = 0; i < dimension; ++i) { + T t = 0.0; + for (Index j = 0; j < dimension; ++j) { + t += minitensor::abs(A(j, i)); } - break; + v(i) = t; + } - case 3: - v(0) = std::abs(A(0,0)) + std::abs(A(1,0)) + std::abs(A(2,0)); - v(1) = std::abs(A(0,1)) + std::abs(A(1,1)) + std::abs(A(2,1)); - v(2) = std::abs(A(0,2)) + std::abs(A(1,2)) + std::abs(A(2,2)); + for (Index i = 0; i < dimension; ++i) { + s = max(s, v(i)); + } + break; - s = std::max(std::max(v(0),v(1)),v(2)); - break; + case 3: + v(0) = minitensor::abs(A(0, 0)) + minitensor::abs(A(1, 0)) + minitensor::abs(A(2, 0)); + v(1) = minitensor::abs(A(0, 1)) + minitensor::abs(A(1, 1)) + minitensor::abs(A(2, 1)); + v(2) = minitensor::abs(A(0, 2)) + minitensor::abs(A(1, 2)) + minitensor::abs(A(2, 2)); - case 2: - v(0) = std::abs(A(0,0)) + std::abs(A(1,0)); - v(1) = std::abs(A(0,1)) + std::abs(A(1,1)); + s = max(max(v(0), v(1)), v(2)); + break; - s = std::max(v(0),v(1)); - break; + case 2: + v(0) = minitensor::abs(A(0, 0)) + minitensor::abs(A(1, 0)); + v(1) = minitensor::abs(A(0, 1)) + minitensor::abs(A(1, 1)); - case 1: - s = std::abs(A(0,0)); - break; + s = max(v(0), v(1)); + break; + case 1: + s = minitensor::abs(A(0, 0)); + break; } return s; @@ -169,33 +168,33 @@ norm_infinity(Tensor const & A) for (Index i = 0; i < dimension; ++i) { T t = 0.0; for (Index j = 0; j < dimension; ++j) { - t += std::abs(A(i, j)); + t += minitensor::abs(A(i, j)); } v(i) = t; } for (Index i = 0; i < dimension; ++i) { - s = std::max(s, v(i)); + s = max(s, v(i)); } break; case 3: - v(0) = std::abs(A(0,0)) + std::abs(A(0,1)) + std::abs(A(0,2)); - v(1) = std::abs(A(1,0)) + std::abs(A(1,1)) + std::abs(A(1,2)); - v(2) = std::abs(A(2,0)) + std::abs(A(2,1)) + std::abs(A(2,2)); + v(0) = minitensor::abs(A(0, 0)) + minitensor::abs(A(0, 1)) + minitensor::abs(A(0, 2)); + v(1) = minitensor::abs(A(1, 0)) + minitensor::abs(A(1, 1)) + minitensor::abs(A(1, 2)); + v(2) = minitensor::abs(A(2, 0)) + minitensor::abs(A(2, 1)) + minitensor::abs(A(2, 2)); - s = std::max(std::max(v(0),v(1)),v(2)); + s = max(max(v(0), v(1)), v(2)); break; case 2: - v(0) = std::abs(A(0,0)) + std::abs(A(0,1)); - v(1) = std::abs(A(1,0)) + std::abs(A(1,1)); + v(0) = minitensor::abs(A(0, 0)) + minitensor::abs(A(0, 1)); + v(1) = minitensor::abs(A(1, 0)) + minitensor::abs(A(1, 1)); - s = std::max(v(0),v(1)); + s = max(v(0), v(1)); break; case 1: - s = std::abs(A(0,0)); + s = minitensor::abs(A(0, 0)); break; } @@ -375,10 +374,11 @@ I2(Tensor const & A) default: #ifdef KOKKOS_ENABLE_CUDA Kokkos::abort("I2 for N > 3 not implemented."); + return T(); #else std::cerr << "I2 for N > 3 not implemented." << std::endl; -#endif exit(1); +#endif break; case 3: @@ -420,10 +420,11 @@ I3(Tensor const & A) default: #ifdef KOKKOS_ENABLE_CUDA Kokkos::abort("I3 for N > 3 not implemented."); -#else + return T(); +#else std::cerr << "I3 for N > 3 not implemented." << std::endl; -#endif exit(1); +#endif break; case 3: @@ -446,16 +447,11 @@ I3(Tensor const & A) // // Condition number. // -template -KOKKOS_INLINE_FUNCTION -T -cond(Tensor const & A) -{ +template T cond(Tensor const &A) { Index const dimension = A.get_dimension(); - Tensor const - S = boost::get<1>(svd(A)); + Tensor const S = std::get<1>(svd(A)); T const k = S(0, 0) / S(dimension - 1, dimension - 1); @@ -466,16 +462,11 @@ cond(Tensor const & A) // // Reciprocal condition number. // -template -KOKKOS_INLINE_FUNCTION -T -inv_cond(Tensor const & A) -{ +template T inv_cond(Tensor const &A) { Index const dimension = A.get_dimension(); - Tensor const - S = boost::get<1>(svd(A)); + Tensor const S = std::get<1>(svd(A)); T const k = S(dimension - 1, dimension - 1) / S(0, 0); @@ -487,11 +478,8 @@ inv_cond(Tensor const & A) // Sort and index in descending order. Useful for ordering singular values // and eigenvalues and corresponding vectors in the respective decompositions. // -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -sort_permutation(Vector const & u) -{ +template +std::pair, Tensor> sort_permutation(Vector const &u) { Index const dimension = u.get_dimension(); @@ -517,7 +505,6 @@ sort_permutation(Vector const & u) } return std::make_pair(v, P); - } } // namespace minitensor diff --git a/packages/minitensor/src/MiniTensor_LinearAlgebra.t.h b/packages/minitensor/src/MiniTensor_LinearAlgebra.t.h index 85d14a5e164d..bb6674069e1f 100644 --- a/packages/minitensor/src/MiniTensor_LinearAlgebra.t.h +++ b/packages/minitensor/src/MiniTensor_LinearAlgebra.t.h @@ -39,6 +39,7 @@ // ************************************************************************ // @HEADER +#include "Kokkos_ArithTraits.hpp" #if !defined(MiniTensor_LinearAlgebra_t_h) #define MiniTensor_LinearAlgebra_t_h @@ -289,11 +290,7 @@ subtensor(Tensor const & A, Index const i, Index const j) // // Exponential map // -template -KOKKOS_INLINE_FUNCTION -Tensor -exp(Tensor const & A) -{ +template Tensor exp(Tensor const &A) { return exp_pade(A); } @@ -438,11 +435,9 @@ polynomial_coefficient(Index const order, Index const index) // // Padé approximant polynomial odd and even terms. // -template -KOKKOS_INLINE_FUNCTION +template std::pair, Tensor> -pade_polynomial_terms(Tensor const & A, Index const order) -{ +pade_polynomial_terms(Tensor const &A, Index const order) { Index const dimension = A.get_dimension(); @@ -549,11 +544,7 @@ binary_powering(Tensor const & A, Index const exponent) // \param A tensor // \return \f$ \exp A \f$ // -template -KOKKOS_INLINE_FUNCTION -Tensor -exp_pade(Tensor const & A) -{ +template Tensor exp_pade(Tensor const &A) { Index const dimension = A.get_dimension(); @@ -588,7 +579,7 @@ exp_pade(Tensor const & A) Tensor V; - boost::tie(U, V) = pade_polynomial_terms(A, order); + std::tie(U, V) = pade_polynomial_terms(A, order); B = inverse(V - U) * (U + V); @@ -810,7 +801,7 @@ log_eig_sym(Tensor const & A) Tensor D(dimension); - boost::tie(V, D) = eig_sym(A); + std::tie(V, D) = eig_sym(A); for (Index i = 0; i < dimension; ++i) { D(i, i) = std::log(D(i, i)); @@ -1159,11 +1150,8 @@ norm_off_diagonal(Tensor const & A) // \param A // \return \f$ (p,q) = arg max_{i,j} |a_{ij}| \f$ // -template -//KOKKOS_INLINE_FUNCTION -std::pair -arg_max_abs(Tensor const & A) -{ +template +std::pair arg_max_abs(Tensor const &A) { Index p = 0; Index q = 0; @@ -1193,11 +1181,8 @@ arg_max_abs(Tensor const & A) // \param A // \return \f$ (p,q) = arg max_{i \neq j} |a_{ij}| \f$ // -template -KOKKOS_INLINE_FUNCTION -std::pair -arg_max_off_diagonal(Tensor const & A) -{ +template +std::pair arg_max_off_diagonal(Tensor const &A) { Index p = 0; Index q = 1; @@ -1229,10 +1214,9 @@ namespace { // \param f, g, h where A = [f, g; 0, h] // \return \f$ A = USV^T\f$ // -template -boost::tuple, Tensor, Tensor> -svd_bidiagonal(T f, T g, T h) -{ +template +std::tuple, Tensor, Tensor> svd_bidiagonal(T f, T g, + T h) { T fa = std::abs(f); T ga = std::abs(g); T ha = std::abs(h); @@ -1312,7 +1296,7 @@ svd_bidiagonal(T f, T g, T h) Tensor S(s0, 0.0, 0.0, s1); Tensor V(cv, -sv, sv, cv); - return boost::make_tuple(U, S, V); + return std::make_tuple(U, S, V); } // @@ -1320,16 +1304,15 @@ svd_bidiagonal(T f, T g, T h) // \param A tensor // \return \f$ A = USV^T\f$ // -template -boost::tuple, Tensor, Tensor> -svd_2x2(Tensor const & A) -{ +template +std::tuple, Tensor, Tensor> +svd_2x2(Tensor const &A) { assert(A.get_dimension() == 2); // First compute a givens rotation to eliminate 1,0 entry in tensor T c = 1.0; T s = 0.0; - boost::tie(c, s) = givens(A(0,0), A(1,0)); + std::tie(c, s) = givens(A(0, 0), A(1, 0)); Tensor R(c, -s, s, c); @@ -1341,13 +1324,13 @@ svd_2x2(Tensor const & A) Tensor X(2), S(2), V(2); - boost::tie(X, S, V) = svd_bidiagonal(B(0,0), B(0,1), B(1,1)); + std::tie(X, S, V) = svd_bidiagonal(B(0, 0), B(0, 1), B(1, 1)); // Complete general 2x2 SVD with givens rotation calculated above Tensor U = transpose(R) * X; - return boost::make_tuple(U, S, V); + return std::make_tuple(U, S, V); } // @@ -1355,10 +1338,9 @@ svd_2x2(Tensor const & A) // \param A tensor // \return \f$ A = USV^T\f$ // -template -boost::tuple, Tensor, Tensor> -svd_NxN(Tensor const & A) -{ +template +std::tuple, Tensor, Tensor> +svd_NxN(Tensor const &A) { // Scale first T const norm_a = norm(A); @@ -1399,7 +1381,7 @@ svd_NxN(Tensor const & A) Index q = 0; - boost::tie(p,q) = arg_max_off_diagonal(S); + std::tie(p, q) = arg_max_off_diagonal(S); if (p > q) { std::swap(p, q); @@ -1412,7 +1394,7 @@ svd_NxN(Tensor const & A) Tensor L(2), D(2), R(2); - boost::tie(L, D, R) = svd_2x2(Spq); + std::tie(L, D, R) = svd_2x2(Spq); T const & cl = L(0,0); @@ -1456,12 +1438,12 @@ svd_NxN(Tensor const & A) Vector s(dimension); Tensor P(dimension); - boost::tie(s, P) = sort_permutation(diag(S)); + std::tie(s, P) = sort_permutation(diag(S)); S = scale * diag(s); U = U * P; V = V * P; - return boost::make_tuple(U, diag(diag(S)), transpose(V)); + return std::make_tuple(U, diag(diag(S)), transpose(V)); } } // anonymous namespace @@ -1471,10 +1453,9 @@ svd_NxN(Tensor const & A) // \param A tensor // \return \f$ A = USV^T\f$ // -template -boost::tuple, Tensor, Tensor> -svd(Tensor const & A) -{ +template +std::tuple, Tensor, Tensor> +svd(Tensor const &A) { Index const dimension = A.get_dimension(); @@ -1484,16 +1465,16 @@ svd(Tensor const & A) switch (dimension) { default: - boost::tie(U, S, V) = svd_NxN(A); + std::tie(U, S, V) = svd_NxN(A); break; case 2: - boost::tie(U, S, V) = svd_2x2(A); + std::tie(U, S, V) = svd_2x2(A); break; } - return boost::make_tuple(U, S, V); + return std::make_tuple(U, S, V); } // @@ -1519,8 +1500,8 @@ polar_rotation(Tensor const & A) T const tol_scale = 0.01; - T const - tol_conv = std::sqrt(dimension) * machine_epsilon(); + T const tol_conv = + Kokkos::ArithTraits::sqrt(dimension) * machine_epsilon(); Tensor X = A; @@ -1588,11 +1569,8 @@ polar_rotation(Tensor const & A) // \param A tensor (often a deformation-gradient-like tensor) // \return \f$ VR = A \f$ with \f$ R \in SO(N) \f$ and \f$ V \in SPD(N) \f$ // -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -polar_left(Tensor const & A) -{ +template +std::pair, Tensor> polar_left(Tensor const &A) { Tensor R = polar_rotation(A); @@ -1607,11 +1585,8 @@ polar_left(Tensor const & A) // \param A tensor (often a deformation-gradient-like tensor) // \return \f$ RU = A \f$ with \f$ R \in SO(N) \f$ and \f$ U \in SPD(N) \f$ // -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -polar_right(Tensor const & A) -{ +template +std::pair, Tensor> polar_right(Tensor const &A) { Tensor R = polar_rotation(A); @@ -1626,11 +1601,8 @@ polar_right(Tensor const & A) // \param F tensor (often a deformation-gradient-like tensor) // \return \f$ VR = F \f$ with \f$ R \in SO(3) \f$ and V SPD(3) // -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -polar_left_eig(Tensor const & F) -{ +template +std::pair, Tensor> polar_left_eig(Tensor const &F) { assert(F.get_dimension() == 3); // set up return tensors @@ -1654,7 +1626,7 @@ polar_left_eig(Tensor const & F) Tensor eVec(3); - boost::tie(eVec, eVal) = eig_spd(b); + std::tie(eVec, eVal) = eig_spd(b); // compute sqrt() and inv(sqrt()) of eigenvalues Tensor @@ -1683,11 +1655,8 @@ polar_left_eig(Tensor const & F) // \param F tensor (often a deformation-gradient-like tensor) // \return \f$ RU = F \f$ with \f$ R \in SO(3) \f$ and U SPD(3) // -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -polar_right_eig(Tensor const & F) -{ +template +std::pair, Tensor> polar_right_eig(Tensor const &F) { Index const dimension = F.get_dimension(); @@ -1714,7 +1683,7 @@ polar_right_eig(Tensor const & F) Tensor eVec(dimension); - boost::tie(eVec, eVal) = eig_spd(C); + std::tie(eVec, eVal) = eig_spd(C); // compute sqrt() and inv(sqrt()) of eigenvalues Tensor @@ -1744,17 +1713,16 @@ polar_right_eig(Tensor const & F) // \param F tensor (often a deformation-gradient-like tensor) // \return \f$ VR = F \f$ with \f$ R \in SO(N) \f$ and V SPD(N), and log V // -template -boost::tuple, Tensor, Tensor> -polar_left_logV(Tensor const & F) -{ +template +std::tuple, Tensor, Tensor> +polar_left_logV(Tensor const &F) { Index const dimension = F.get_dimension(); Tensor X(dimension), S(dimension), Y(dimension); - boost::tie(X, S, Y) = svd(F); + std::tie(X, S, Y) = svd(F); Tensor R = X * transpose(Y); @@ -1772,13 +1740,12 @@ polar_left_logV(Tensor const & F) Tensor v = X * s * transpose(X); - return boost::make_tuple(V, R, v); + return std::make_tuple(V, R, v); } -template -boost::tuple, Tensor, Tensor> -polar_left_logV_eig(Tensor const & F) -{ +template +std::tuple, Tensor, Tensor> +polar_left_logV_eig(Tensor const &F) { Index const dimension = F.get_dimension(); @@ -1788,7 +1755,7 @@ polar_left_logV_eig(Tensor const & F) Tensor V(dimension), D(dimension); - boost::tie(V, D) = eig_sym(b); + std::tie(V, D) = eig_sym(b); Tensor DQ(dimension, Filler::ZEROS), DI(dimension, Filler::ZEROS), DL(dimension, Filler::ZEROS); @@ -1808,7 +1775,7 @@ polar_left_logV_eig(Tensor const & F) Tensor const x = V * dot_t(DL, V); - return boost::make_tuple(X, R, x); + return std::make_tuple(X, R, x); } // @@ -1816,10 +1783,9 @@ polar_left_logV_eig(Tensor const & F) // \param F tensor (often a deformation-gradient-like tensor) // \return \f$ VR = F \f$ with \f$ R \in SO(N) \f$ and V SPD(N), and log V // -template -boost::tuple, Tensor, Tensor> -polar_left_logV_lame(Tensor const & F) -{ +template +std::tuple, Tensor, Tensor> +polar_left_logV_lame(Tensor const &F) { Index const dimension = F.get_dimension(); @@ -1832,7 +1798,7 @@ polar_left_logV_lame(Tensor const & F) // get eigenvalues/eigenvectors Tensor eVal(dimension); Tensor eVec(dimension); - boost::tie(eVec,eVal) = eig_spd_cos(b); + std::tie(eVec, eVal) = eig_spd_cos(b); // compute sqrt() and inv(sqrt()) of eigenvalues Tensor x = zero(3); @@ -1853,7 +1819,7 @@ polar_left_logV_lame(Tensor const & F) v = eVec*lnx*transpose(eVec); R = Vinv*F; - return boost::make_tuple(V,R,v); + return std::make_tuple(V, R, v); } // @@ -1888,11 +1854,8 @@ bch(Tensor const & x, Tensor const & y) // \param \f$ A = [f, g; g, h] \in S(2) \f$ // \return \f$ c, s \rightarrow [c, -s; s, c]\f diagonalizes A$ // -template -KOKKOS_INLINE_FUNCTION -std::pair -schur_sym(T const f, T const g, T const h) -{ +template +std::pair schur_sym(T const f, T const g, T const h) { T c = 1.0; T s = 0.0; @@ -1916,11 +1879,7 @@ schur_sym(T const f, T const g, T const h) // \param a, b // \return c, s // -template -KOKKOS_INLINE_FUNCTION -std::pair -givens(T const & a, T const & b) -{ +template std::pair givens(T const &a, T const &b) { T c = 1.0; T s = 0.0; @@ -1947,11 +1906,8 @@ namespace { // \return V eigenvectors, D eigenvalues in diagonal Matlab-style // See algorithm 8.4.2 in Matrix Computations, Golub & Van Loan 1996 // -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -eig_sym_NxN(Tensor const & A) -{ +template +std::pair, Tensor> eig_sym_NxN(Tensor const &A) { Tensor D = sym(A); @@ -1984,7 +1940,7 @@ eig_sym_NxN(Tensor const & A) Index q = 0; - boost::tie(p,q) = arg_max_off_diagonal(D); + std::tie(p, q) = arg_max_off_diagonal(D); if (p > q) { std::swap(p,q); } @@ -2002,7 +1958,7 @@ eig_sym_NxN(Tensor const & A) T c, s; - boost::tie(c, s) = schur_sym(f, g, h); + std::tie(c, s) = schur_sym(f, g, h); // Apply Givens rotation to matrices // that are converging to eigenvalues and eigenvectors @@ -2018,7 +1974,7 @@ eig_sym_NxN(Tensor const & A) Vector d(dimension); Tensor P(dimension); - boost::tie(d, P) = sort_permutation(diag(D)); + std::tie(d, P) = sort_permutation(diag(D)); D = diag(d); V = V * P; @@ -2030,11 +1986,8 @@ eig_sym_NxN(Tensor const & A) // \param A tensor // \return V eigenvectors, D eigenvalues in diagonal Matlab-style // -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -eig_sym_2x2(Tensor const & A) -{ +template +std::pair, Tensor> eig_sym_2x2(Tensor const &A) { assert(A.get_dimension() == 2); T const f = A(0,0); @@ -2093,15 +2046,15 @@ eig_sym_2x2(Tensor const & A) T c, s; - boost::tie(c, s) = schur_sym(f, g, h); + std::tie(c, s) = schur_sym(f, g, h); Tensor V(c, -s, s, c); if (swap_diag == true) { // swap eigenvectors if eigenvalues were swapped - std::swap(V(0,0), V(0,1)); - std::swap(V(1,0), V(1,1)); + std::swap(V(0, 0), V(0, 1)); + std::swap(V(1, 0), V(1, 1)); } return std::make_pair(V, D); @@ -2114,11 +2067,8 @@ eig_sym_2x2(Tensor const & A) // \param A tensor // \return V eigenvectors, D eigenvalues in diagonal Matlab-style // -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -eig_sym(Tensor const & A) -{ +template +std::pair, Tensor> eig_sym(Tensor const &A) { Index const dimension = A.get_dimension(); @@ -2128,11 +2078,11 @@ eig_sym(Tensor const & A) switch (dimension) { default: - boost::tie(V, D) = eig_sym_NxN(A); + std::tie(V, D) = eig_sym_NxN(A); break; case 2: - boost::tie(V, D) = eig_sym_2x2(A); + std::tie(V, D) = eig_sym_2x2(A); break; } @@ -2145,11 +2095,8 @@ eig_sym(Tensor const & A) // \param A tensor // \return V eigenvectors, D eigenvalues in diagonal Matlab-style // -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -eig_spd(Tensor const & A) -{ +template +std::pair, Tensor> eig_spd(Tensor const &A) { return eig_sym(A); } @@ -2158,11 +2105,8 @@ eig_spd(Tensor const & A) // \param A tensor // \return V eigenvectors, D eigenvalues in diagonal Matlab-style // -template -KOKKOS_INLINE_FUNCTION -std::pair, Tensor> -eig_spd_cos(Tensor const & A) -{ +template +std::pair, Tensor> eig_spd_cos(Tensor const &A) { Index const dimension = A.get_dimension(); @@ -2415,11 +2359,8 @@ eig_spd_cos(Tensor const & A) // \return G Cholesky factor A = GG^T // \return completed (bool) algorithm ran to completion // -template -KOKKOS_INLINE_FUNCTION -std::pair, bool > -cholesky(Tensor const & A) -{ +template +std::pair, bool> cholesky(Tensor const &A) { Tensor G = sym(A); @@ -2465,22 +2406,18 @@ namespace { // // // -template -KOKKOS_INLINE_FUNCTION -std::pair, RHS> -identity_precon(Tensor const & A, RHS const & B) -{ +template +std::pair, RHS> identity_precon(Tensor const &A, + RHS const &B) { return std::make_pair(A, B); } // // // -template -KOKKOS_INLINE_FUNCTION -std::pair, RHS> -diagonal_precon(Tensor const & A, RHS const & B) -{ +template +std::pair, RHS> diagonal_precon(Tensor const &A, + RHS const &B) { Vector const d = diag(A); @@ -2496,11 +2433,8 @@ diagonal_precon(Tensor const & A, RHS const & B) // // // -template -KOKKOS_INLINE_FUNCTION -std::pair, RHS> -maxabsrow_precon(Tensor const & A, RHS & B) -{ +template +std::pair, RHS> maxabsrow_precon(Tensor const &A, RHS &B) { Index const dimension = A.get_dimension(); @@ -2519,11 +2453,9 @@ maxabsrow_precon(Tensor const & A, RHS & B) // // // -template -KOKKOS_INLINE_FUNCTION -std::pair, RHS> -precon(PreconditionerType const pt, Tensor const & A, RHS const & B) -{ +template +std::pair, RHS> precon(PreconditionerType const pt, + Tensor const &A, RHS const &B) { switch (pt) { default: MT_ERROR_EXIT("Unknown preconditioner type."); @@ -2550,18 +2482,15 @@ precon(PreconditionerType const pt, Tensor const & A, RHS const & B) // as it just Gauss-Jordan elimination. It is intended to be used in // conjunction with Kokkos to take advantage of thread parallelism. // -template -KOKKOS_INLINE_FUNCTION -RHS -solve(Tensor const & A, RHS const & b, PreconditionerType const pt) -{ +template +RHS solve(Tensor const &A, RHS const &b, PreconditionerType const pt) { Tensor PA; RHS Pb; - boost::tie(PA, Pb) = precon(pt, A, b); + std::tie(PA, Pb) = precon(pt, A, b); return solve_full_pivot(PA, Pb); } diff --git a/packages/minitensor/src/MiniTensor_Mechanics.t.h b/packages/minitensor/src/MiniTensor_Mechanics.t.h index d9d4ec23d23c..61df4c1950bc 100644 --- a/packages/minitensor/src/MiniTensor_Mechanics.t.h +++ b/packages/minitensor/src/MiniTensor_Mechanics.t.h @@ -663,7 +663,7 @@ check_strong_ellipticity(Tensor4 const & A) Tensor D; - boost::tie(V, D) = eig_sym(Q); + std::tie(V, D) = eig_sym(Q); curr_eigenvalue = D(dimension - 1, dimension - 1); diff --git a/packages/minitensor/src/MiniTensor_Solvers.t.h b/packages/minitensor/src/MiniTensor_Solvers.t.h index 2cf4dd758d2a..ec6a37b34b2b 100644 --- a/packages/minitensor/src/MiniTensor_Solvers.t.h +++ b/packages/minitensor/src/MiniTensor_Solvers.t.h @@ -564,7 +564,7 @@ step(Tensor const & Hessian, Vector const & gradient) bool is_posdef{false}; - boost::tie(L, is_posdef) = cholesky(K); + std::tie(L, is_posdef) = cholesky(K); if (is_posdef == false) { MT_ERROR_EXIT("Trust region subproblem encountered singular Hessian."); @@ -635,7 +635,7 @@ step(Tensor const & Hessian, Vector const & gradient) bool is_posdef{false}; - boost::tie(L, is_posdef) = cholesky(K); + std::tie(L, is_posdef) = cholesky(K); if (is_posdef == false) { MT_ERROR_EXIT("Trust region subproblem encountered singular Hessian."); diff --git a/packages/minitensor/src/MiniTensor_Storage.h b/packages/minitensor/src/MiniTensor_Storage.h index 56b00c903253..5fa993169ec5 100644 --- a/packages/minitensor/src/MiniTensor_Storage.h +++ b/packages/minitensor/src/MiniTensor_Storage.h @@ -39,6 +39,7 @@ // ************************************************************************ // @HEADER +#include "Kokkos_Macros.hpp" #if !defined(MiniTensor_Storage_h) #define MiniTensor_Storage_h @@ -49,12 +50,12 @@ namespace minitensor { /// Set to constant value if not dynamic template struct dimension_const { - static Index const value = C; + static constexpr Index value = C; }; template struct dimension_const { - static Index const value = DYNAMIC; + static constexpr Index value = DYNAMIC; }; /// Validate dimension @@ -64,13 +65,13 @@ struct check_static { #if defined(KOKKOS_ENABLE_CUDA) // Empty #else - static Index const - maximum_dimension = static_cast(std::numeric_limits::digits); + static constexpr Index maximum_dimension = + static_cast(std::numeric_limits::digits); static_assert(D > maximum_dimension, "Dimension is too large"); #endif - static Index const value = D; + static constexpr Index value = D; }; template @@ -91,132 +92,105 @@ check_dynamic(Index const dimension) /// Integer power template restricted to orders defined below template struct dimension_power { - static Index const value = 0; + static constexpr Index value = 0; }; template struct dimension_power { - static Index const value = D; + static constexpr Index value = D; }; template struct dimension_power { - static Index const value = D * D; + static constexpr Index value = D * D; }; template struct dimension_power { - static Index const value = D * D * D; + static constexpr Index value = D * D * D; }; template struct dimension_power { - static Index const value = D * D * D * D; + static constexpr Index value = D * D * D * D; }; /// Integer square for manipulations between 2nd and 4rd-order tensors. template struct dimension_square { - static Index const value = 0; + static constexpr Index value = 0; }; template<> struct dimension_square { - static Index const value = DYNAMIC; + static constexpr Index value = DYNAMIC; }; -template<> -struct dimension_square<1> { - static Index const value = 1; -}; +template <> struct dimension_square<1> { static constexpr Index value = 1; }; -template<> -struct dimension_square<2> { - static Index const value = 4; -}; +template <> struct dimension_square<2> { static constexpr Index value = 4; }; -template<> -struct dimension_square<3> { - static Index const value = 9; -}; +template <> struct dimension_square<3> { static constexpr Index value = 9; }; -template<> -struct dimension_square<4> { - static Index const value = 16; -}; +template <> struct dimension_square<4> { static constexpr Index value = 16; }; /// Integer square root template restricted to dimensions defined below. /// Useful for constructing a 2nd-order tensor from a 4th-order /// tensor with static storage. -template -struct dimension_sqrt { - static Index const value = 0; -}; +template struct dimension_sqrt { static constexpr Index value = 0; }; template<> struct dimension_sqrt { - static Index const value = DYNAMIC; + static constexpr Index value = DYNAMIC; }; -template<> -struct dimension_sqrt<1> { - static Index const value = 1; -}; +template <> struct dimension_sqrt<1> { static constexpr Index value = 1; }; -template<> -struct dimension_sqrt<4> { - static Index const value = 2; -}; +template <> struct dimension_sqrt<4> { static constexpr Index value = 2; }; -template<> -struct dimension_sqrt<9> { - static Index const value = 3; -}; +template <> struct dimension_sqrt<9> { static constexpr Index value = 3; }; -template<> -struct dimension_sqrt<16> { - static Index const value = 4; -}; +template <> struct dimension_sqrt<16> { static constexpr Index value = 4; }; /// Manipulation of static and dynamic dimensions. template struct dimension_add { - static Index const value = N + P; + static constexpr Index value = N + P; }; template struct dimension_add { - static Index const value = DYNAMIC; + static constexpr Index value = DYNAMIC; }; template struct dimension_subtract { - static Index const value = N - P; + static constexpr Index value = N - P; }; template struct dimension_subtract { - static Index const value = DYNAMIC; + static constexpr Index value = DYNAMIC; }; template struct dimension_product { - static Index const value = N * P; + static constexpr Index value = N * P; }; template struct dimension_product { - static Index const value = DYNAMIC; + static constexpr Index value = DYNAMIC; }; template struct dimension_product { - static Index const value = DYNAMIC; + static constexpr Index value = DYNAMIC; }; template<> struct dimension_product { - static Index const value = DYNAMIC; + static constexpr Index value = DYNAMIC; }; /// @@ -240,13 +214,12 @@ class Storage bool IS_DYNAMIC = false; + KOKKOS_INLINE_FUNCTION Storage() { } - explicit - Storage(Index const number_entries) - { + explicit KOKKOS_INLINE_FUNCTION Storage(Index const number_entries) { resize(number_entries); } @@ -255,6 +228,7 @@ class Storage Storage & operator=(Storage const & s) = delete; + KOKKOS_INLINE_FUNCTION ~Storage() { } @@ -316,12 +290,7 @@ class Storage return &storage_[0]; } - static constexpr - Index - static_size() - { - return N; - } + static KOKKOS_INLINE_FUNCTION constexpr Index static_size() { return N; } private: @@ -353,13 +322,12 @@ class Storage bool IS_STATIC = false; + KOKKOS_INLINE_FUNCTION Storage() { } - explicit - Storage(Index const number_entries) - { + explicit KOKKOS_INLINE_FUNCTION Storage(Index const number_entries) { resize(number_entries); } @@ -368,6 +336,7 @@ class Storage Storage & operator=(Storage const & s) = delete; + KOKKOS_INLINE_FUNCTION ~Storage() { clear(); @@ -432,12 +401,7 @@ class Storage return storage_; } - static constexpr - Index - static_size() - { - return 0; - } + static KOKKOS_INLINE_FUNCTION constexpr Index static_size() { return 0; } private: diff --git a/packages/minitensor/src/MiniTensor_Tensor.i.h b/packages/minitensor/src/MiniTensor_Tensor.i.h index e92855003ed8..7bde2f3b1302 100644 --- a/packages/minitensor/src/MiniTensor_Tensor.i.h +++ b/packages/minitensor/src/MiniTensor_Tensor.i.h @@ -1650,22 +1650,19 @@ transpose(Tensor const & A) default: for (Index i = 0; i < dimension; ++i) { for (Index j = i + 1; j < dimension; ++j) { - std::swap(B(i, j), B(j, i)); + minitensor::swap(B(i, j), B(j, i)); } } break; case 3: - std::swap(B(0, 1), B(1, 0)); - std::swap(B(0, 2), B(2, 0)); - - std::swap(B(1, 2), B(2, 1)); - + minitensor::swap(B(0, 1), B(1, 0)); + minitensor::swap(B(0, 2), B(2, 0)); + minitensor::swap(B(1, 2), B(2, 1)); break; case 2: - std::swap(B(0, 1), B(1, 0)); - + minitensor::swap(B(0, 1), B(1, 0)); break; } diff --git a/packages/minitensor/src/MiniTensor_TensorBase.i.h b/packages/minitensor/src/MiniTensor_TensorBase.i.h index cc2b782cd7d9..572ffd60b702 100644 --- a/packages/minitensor/src/MiniTensor_TensorBase.i.h +++ b/packages/minitensor/src/MiniTensor_TensorBase.i.h @@ -399,6 +399,8 @@ TensorBase::fill(Filler const value) } break; +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST + case Filler::RANDOM: for (Index i = 0; i < number_components; ++i) { auto & entry = (*this)[i]; @@ -423,6 +425,8 @@ TensorBase::fill(Filler const value) } break; +#endif + case Filler::NANS: for (Index i = 0; i < number_components; ++i) { auto & entry = (*this)[i]; @@ -432,7 +436,8 @@ TensorBase::fill(Filler const value) break; default: - MT_ERROR_EXIT("Unknown specification of value for filling components."); + MT_ERROR_EXIT("Unknown or undefined (in execution space) specification of " + "value for filling components."); break; } diff --git a/packages/minitensor/src/MiniTensor_Utilities.h b/packages/minitensor/src/MiniTensor_Utilities.h index 565536f1ccaa..2e6091fee574 100644 --- a/packages/minitensor/src/MiniTensor_Utilities.h +++ b/packages/minitensor/src/MiniTensor_Utilities.h @@ -49,6 +49,14 @@ namespace minitensor { +// +// abs function +// +template +KOKKOS_INLINE_FUNCTION +T +abs(T const & a); + // //swap function // @@ -63,7 +71,7 @@ swap(T & a, T & b); template KOKKOS_INLINE_FUNCTION T -max(const T & a,const T & b); +max(T const & a, T const & b); // // max function @@ -71,7 +79,7 @@ max(const T & a,const T & b); template KOKKOS_INLINE_FUNCTION T -min(const T & a,const T & b); +min(T const & a,T const & b); /// /// Sign function @@ -141,26 +149,17 @@ tau(); /// Random number generation. Uniform distribution U(-1,1) /// which is the Teuchos default (!). /// -template -KOKKOS_INLINE_FUNCTION -typename Sacado::ScalarType::type -random(); +template typename Sacado::ScalarType::type random(); /// /// Random number generation. Uniform distribution U(0,1). /// -template -KOKKOS_INLINE_FUNCTION -typename Sacado::ScalarType::type -random_uniform(); +template typename Sacado::ScalarType::type random_uniform(); /// /// Random number generation. Normal distribution N(0,1). /// -template -KOKKOS_INLINE_FUNCTION -typename Sacado::ScalarType::type -random_normal(); +template typename Sacado::ScalarType::type random_normal(); /// /// Fill all levels of AD to specified constant. diff --git a/packages/minitensor/src/MiniTensor_Utilities.i.h b/packages/minitensor/src/MiniTensor_Utilities.i.h index 60b7a7b52d1a..9e19785e7a5c 100644 --- a/packages/minitensor/src/MiniTensor_Utilities.i.h +++ b/packages/minitensor/src/MiniTensor_Utilities.i.h @@ -50,6 +50,17 @@ namespace minitensor { +// +// +// +template +KOKKOS_INLINE_FUNCTION +T +abs(T const & a) +{ + return a < T(0) ? -a : a; +} + // // // @@ -61,12 +72,9 @@ swap(T & a, T & b) // Guard against the same memory location. if (&a == &b) return; - // XOR algorithm - a ^= b; - b ^= a; - a ^= b; - - return; + auto const c = a; + a = b; + b = c; } // @@ -75,7 +83,7 @@ swap(T & a, T & b) template KOKKOS_INLINE_FUNCTION T -max(const T & a, const T & b) +max(T const & a, T const & b) { return a > b ? a : b; } @@ -86,7 +94,7 @@ max(const T & a, const T & b) template KOKKOS_INLINE_FUNCTION T -min(const T & a, const T & b) +min(T const & a, T const & b) { return a < b ? a : b; } @@ -187,11 +195,7 @@ tau() // // Random number generation. Teuchos [-1,1] // -template -KOKKOS_INLINE_FUNCTION -typename Sacado::ScalarType::type -random() -{ +template typename Sacado::ScalarType::type random() { using S = typename Sacado::ScalarType::type; return Teuchos::ScalarTraits().random(); } @@ -199,11 +203,7 @@ random() // // Uniform [0,1] random number generation. // -template -KOKKOS_INLINE_FUNCTION -typename Sacado::ScalarType::type -random_uniform() -{ +template typename Sacado::ScalarType::type random_uniform() { using S = typename Sacado::ScalarType::type; return static_cast(0.5 * random() + 0.5); } @@ -211,11 +211,7 @@ random_uniform() // // Normal N(0,1) random number generation. // -template -KOKKOS_INLINE_FUNCTION -typename Sacado::ScalarType::type -random_normal() -{ +template typename Sacado::ScalarType::type random_normal() { using S = typename Sacado::ScalarType::type; S const diff --git a/packages/minitensor/src/MiniTensor_Vector.h b/packages/minitensor/src/MiniTensor_Vector.h index 7b2c57ed9aec..9455dc4d46b5 100644 --- a/packages/minitensor/src/MiniTensor_Vector.h +++ b/packages/minitensor/src/MiniTensor_Vector.h @@ -506,7 +506,6 @@ unit(Vector const & u); /// \return v, beta /// template -KOKKOS_INLINE_FUNCTION std::pair, T> house(Vector const & x); diff --git a/packages/minitensor/src/MiniTensor_Vector.i.h b/packages/minitensor/src/MiniTensor_Vector.i.h index 0f90b39c3e5a..6c0ebfac6536 100644 --- a/packages/minitensor/src/MiniTensor_Vector.i.h +++ b/packages/minitensor/src/MiniTensor_Vector.i.h @@ -39,6 +39,7 @@ // ************************************************************************ // @HEADER +#include "Kokkos_ArithTraits.hpp" #if !defined(MiniTensor_Vector_i_h) #define MiniTensor_Vector_i_h @@ -812,16 +813,16 @@ norm_infinity(Vector const & u) default: for (Index i = 0; i < dimension; ++i) { - s = std::max(s, std::abs(u(i))); + s = max(minitensor::abs(u(i)), s); } break; case 3: - s = std::max(std::max(std::abs(u(0)), std::abs(u(1))), std::abs(u(2))); + s = max(max(minitensor::abs(u(0)), minitensor::abs(u(1))), minitensor::abs(u(2))); break; case 2: - s = std::max(std::abs(u(0)), std::abs(u(1))); + s = max(minitensor::abs(u(0)), minitensor::abs(u(1))); break; } @@ -842,11 +843,8 @@ unit(Vector const & u) // // Compute Householder vector // -template -KOKKOS_INLINE_FUNCTION -std::pair, T> -house(Vector const & x) -{ +template +std::pair, T> house(Vector const &x) { Vector v = x; diff --git a/packages/minitensor/test/test_01.cc b/packages/minitensor/test/test_01.cc index 975d36871f3c..fa0ba3f9701d 100644 --- a/packages/minitensor/test/test_01.cc +++ b/packages/minitensor/test/test_01.cc @@ -901,7 +901,7 @@ TEST(MiniTensor, SymmetricEigen) Tensor V(3); Tensor D(3); - boost::tie(V, D) = eig_sym(A); + std::tie(V, D) = eig_sym(A); ASSERT_LE(std::abs(D(0, 0) - 1.1), machine_epsilon()); ASSERT_LE(std::abs(D(1, 1) - 1.0), machine_epsilon()); @@ -921,7 +921,7 @@ TEST(MiniTensor, LeftPolarDecomposition) Tensor V(3); Tensor R(3); - boost::tie(V, R) = polar_left(F); + std::tie(V, R) = polar_left(F); Real const error_x = norm(V - X) / norm(X); @@ -1031,7 +1031,7 @@ TEST(MiniTensor, PolarLeftLog) Tensor V(3), R(3), v(3); - boost::tie(V, R, v) = polar_left_logV(F); + std::tie(V, R, v) = polar_left_logV(F); Real const error = norm(v - x) / norm(x); @@ -1084,7 +1084,7 @@ TEST(MiniTensor, SVD2x2) Tensor U(2), S(2), V(2); - boost::tie(U, S, V) = svd(A); + std::tie(U, S, V) = svd(A); Tensor B = U * S * transpose(V); @@ -1099,7 +1099,7 @@ TEST(MiniTensor, SVD3x3) Tensor U(3), S(3), V(3); - boost::tie(U, S, V) = svd(A); + std::tie(U, S, V) = svd(A); Tensor const B = U * S * transpose(V); @@ -1115,7 +1115,7 @@ TEST(MiniTensor, SVD3x3Fad) Tensor> U(3), S(3), V(3); - boost::tie(U, S, V) = svd(A); + std::tie(U, S, V) = svd(A); Tensor> const B = U * S * transpose(V); @@ -1183,7 +1183,7 @@ TEST(MiniTensor, SymmetricEigen2x2) Tensor V(2), D(2); - boost::tie(V, D) = eig_sym(A); + std::tie(V, D) = eig_sym(A); Tensor const B = V * D * transpose(V); @@ -1198,7 +1198,7 @@ TEST(MiniTensor, SymmetricEigen3x3) Tensor V(3), D(3); - boost::tie(V, D) = eig_sym(A); + std::tie(V, D) = eig_sym(A); Tensor const B = V * D * transpose(V); @@ -1213,11 +1213,11 @@ TEST(MiniTensor, Polar3x3) Tensor R(3), U(3); - boost::tie(R, U) = polar_right(A); + std::tie(R, U) = polar_right(A); Tensor X(3), D(3), Y(3); - boost::tie(X, D, Y) = svd(A); + std::tie(X, D, Y) = svd(A); Tensor const B = R - X * transpose(Y) + U - Y * D * transpose(Y); @@ -1234,7 +1234,7 @@ TEST(MiniTensor, Cholesky) bool is_spd; - boost::tie(G, is_spd) = cholesky(A); + std::tie(G, is_spd) = cholesky(A); Tensor const B(1.0, 0.0, 0.0, 1.0, 2.0, 0.0, 1.0, 1.0, 1.0); diff --git a/packages/ml/src/CMakeLists.txt b/packages/ml/src/CMakeLists.txt index 122c357215dd..4201ff7553ce 100644 --- a/packages/ml/src/CMakeLists.txt +++ b/packages/ml/src/CMakeLists.txt @@ -329,3 +329,7 @@ TRIBITS_ADD_LIBRARY( HEADERS ${HEADERS} SOURCES ${SOURCES} ) + +if (WIN32) + target_link_libraries (ml PUBLIC ws2_32) +endif () diff --git a/packages/ml/src/MLAPI/MLAPI_Workspace.cpp b/packages/ml/src/MLAPI/MLAPI_Workspace.cpp index adea81e4e75d..f58e7dda54da 100644 --- a/packages/ml/src/MLAPI/MLAPI_Workspace.cpp +++ b/packages/ml/src/MLAPI/MLAPI_Workspace.cpp @@ -16,6 +16,10 @@ #ifdef _MSC_VER #include "winprocess.h" #endif +#include +#ifdef _WIN32 +#include +#endif namespace MLAPI { diff --git a/packages/ml/src/Utils/ml_epetra_utils.cpp b/packages/ml/src/Utils/ml_epetra_utils.cpp index 34fab14d69da..70f81f7ef76f 100644 --- a/packages/ml/src/Utils/ml_epetra_utils.cpp +++ b/packages/ml/src/Utils/ml_epetra_utils.cpp @@ -43,6 +43,10 @@ #ifdef _MSC_VER # include "winprocess.h" #endif +#include +#ifdef _WIN32 +#include +#endif #ifdef HAVE_ML_TEUCHOS using namespace Teuchos; @@ -52,7 +56,6 @@ using namespace Teuchos; #include "EpetraExt_MatrixMatrix.h" #endif - // ====================================================================== typedef struct { diff --git a/packages/muelu/src/Graph/MatrixTransformation/MueLu_CoalesceDropFactory_def.hpp b/packages/muelu/src/Graph/MatrixTransformation/MueLu_CoalesceDropFactory_def.hpp index c4b903035125..60b76d6dc427 100644 --- a/packages/muelu/src/Graph/MatrixTransformation/MueLu_CoalesceDropFactory_def.hpp +++ b/packages/muelu/src/Graph/MatrixTransformation/MueLu_CoalesceDropFactory_def.hpp @@ -132,7 +132,8 @@ namespace MueLu { { typedef Teuchos::StringToIntegralParameterEntryValidator validatorType; - validParamList->getEntry("aggregation: drop scheme").setValidator(rcp(new validatorType(Teuchos::tuple("classical", "distance laplacian","signed classical","block diagonal","block diagonal classical","block diagonal distance laplacian","block diagonal signed classical","block diagonal colored signed classical"), "aggregation: drop scheme"))); + // "signed classical" is the Ruge-Stuben style (relative to max off-diagonal), "sign classical sa" is the signed version of the sa criterion (relative to the diagonal values) + validParamList->getEntry("aggregation: drop scheme").setValidator(rcp(new validatorType(Teuchos::tuple("signed classical sa","classical", "distance laplacian","signed classical","block diagonal","block diagonal classical","block diagonal distance laplacian","block diagonal signed classical","block diagonal colored signed classical"), "aggregation: drop scheme"))); } SET_VALID_ENTRY("aggregation: distance laplacian algo"); @@ -163,7 +164,9 @@ namespace MueLu { if (algo == "distance laplacian" || algo == "block diagonal distance laplacian") { Input(currentLevel, "Coordinates"); } - if (algo.find("block diagonal") != std::string::npos || algo.find("signed classical") != std::string::npos) { + if(algo == "signed classical sa") + ; + else if (algo.find("block diagonal") != std::string::npos || algo.find("signed classical") != std::string::npos) { Input(currentLevel, "BlockNumber"); } } @@ -174,7 +177,7 @@ namespace MueLu { void CoalesceDropFactory::Build(Level ¤tLevel) const { FactoryMonitor m(*this, "Build", currentLevel); - + typedef Teuchos::ScalarTraits STS; typedef typename STS::magnitudeType real_type; typedef Xpetra::MultiVector RealValuedMultiVector; @@ -196,7 +199,8 @@ namespace MueLu { bool use_block_algorithm=false; LO interleaved_blocksize = as(pL.get("aggregation: block diagonal: interleaved blocksize")); - bool useSignedClassical = false; + bool useSignedClassicalRS = false; + bool useSignedClassicalSA = false; bool generateColoringGraph = false; // NOTE: If we're doing blockDiagonal, we'll not want to do rowSum twice (we'll do it @@ -210,8 +214,13 @@ namespace MueLu { Coords = Get< RCP >(currentLevel, "Coordinates"); A = realA; } + else if(algo == "signed classical sa") { + useSignedClassicalSA = true; + algo = "classical"; + A = realA; + } else if(algo == "signed classical" || algo == "block diagonal colored signed classical" || algo == "block diagonal signed classical") { - useSignedClassical = true; + useSignedClassicalRS = true; // if(realA->GetFixedBlockSize() > 1) { RCP BlockNumber = Get >(currentLevel, "BlockNumber"); // Ghost the column block numbers if we need to @@ -378,12 +387,14 @@ namespace MueLu { const typename STS::magnitudeType dirichletThreshold = STS::magnitude(as(pL.get("aggregation: Dirichlet threshold"))); - // NOTE: We don't support signed classical with cut drop at present - TEUCHOS_TEST_FOR_EXCEPTION(useSignedClassical && classicalAlgo != defaultAlgo, Exceptions::RuntimeError, "\"aggregation: classical algo\" != default is not supported for scalled classical aggregation"); - + // NOTE: We don't support signed classical RS or SA with cut drop at present + TEUCHOS_TEST_FOR_EXCEPTION(useSignedClassicalRS && classicalAlgo != defaultAlgo, Exceptions::RuntimeError, "\"aggregation: classical algo\" != default is not supported for scalled classical aggregation"); + TEUCHOS_TEST_FOR_EXCEPTION(useSignedClassicalSA && classicalAlgo != defaultAlgo, Exceptions::RuntimeError, "\"aggregation: classical algo\" != default is not supported for scalled classical sa aggregation"); GO numDropped = 0, numTotal = 0; std::string graphType = "unamalgamated"; //for description purposes only + + /************************** RS or SA-style Classical Dropping (and variants) **************************/ if (algo == "classical") { if (predrop_ == null) { // ap: this is a hack: had to declare predrop_ as mutable @@ -404,7 +415,7 @@ namespace MueLu { // At this points we either have // (predrop_ != null) // Therefore, it is sufficient to check only threshold - if (A->GetFixedBlockSize() == 1 && threshold == STS::zero() && !useSignedClassical && A->hasCrsGraph()) { + if (A->GetFixedBlockSize() == 1 && threshold == STS::zero() && !useSignedClassicalRS && !useSignedClassicalSA && A->hasCrsGraph()) { // Case 1: scalar problem, no dropping => just use matrix graph RCP graph = rcp(new Graph(A->getCrsGraph(), "graph of A")); // Detect and record rows that correspond to Dirichlet boundary conditions @@ -431,7 +442,8 @@ namespace MueLu { } else if ( (A->GetFixedBlockSize() == 1 && threshold != STS::zero()) || (A->GetFixedBlockSize() == 1 && threshold == STS::zero() && !A->hasCrsGraph()) || - (A->GetFixedBlockSize() == 1 && useSignedClassical) ) { + (A->GetFixedBlockSize() == 1 && useSignedClassicalRS) || + (A->GetFixedBlockSize() == 1 && useSignedClassicalSA) ) { // Case 2: scalar problem with dropping => record the column indices of undropped entries, but still use original // graph's map information, e.g., whether index is local // OR a matrix without a CrsGraph @@ -444,7 +456,8 @@ namespace MueLu { RCP ghostedDiag; ArrayRCP ghostedDiagVals; ArrayRCP negMaxOffDiagonal; - if(useSignedClassical) { + // RS style needs the max negative off-diagonal, SA style needs the diagonal + if(useSignedClassicalRS) { if(ghostedBlockNumber.is_null()) { negMaxOffDiagonal = MueLu::Utilities::GetMatrixMaxMinusOffDiagonal(*A); if (GetVerbLevel() & Statistics1) @@ -489,12 +502,12 @@ namespace MueLu { //FIXME For now, hardwiring the dropping in here LO rownnz = 0; - if(useSignedClassical) { - // Signed classical + if(useSignedClassicalRS) { + // Signed classical RS style for (LO colID = 0; colID < Teuchos::as(nnz); colID++) { LO col = indices[colID]; MT max_neg_aik = realThreshold * STS::real(negMaxOffDiagonal[row]); - MT neg_aij = - STS::real(vals[colID]); + MT neg_aij = - STS::real(vals[colID]); /* if(row==1326) printf("A(%d,%d) = %6.4e, block = (%d,%d) neg_aij = %6.4e max_neg_aik = %6.4e\n",row,col,vals[colID], g_block_id.is_null() ? -1 : g_block_id[row], g_block_id.is_null() ? -1 : g_block_id[col], @@ -507,6 +520,27 @@ namespace MueLu { } rows[row+1] = realnnz; } + else if(useSignedClassicalSA) { + // Signed classical SA style + for (LO colID = 0; colID < Teuchos::as(nnz); colID++) { + LO col = indices[colID]; + + bool is_nonpositive = STS::real(vals[colID]) <= 0; + MT aiiajj = STS::magnitude(threshold*threshold * ghostedDiagVals[col]*ghostedDiagVals[row]); // eps^2*|a_ii|*|a_jj| + MT aij = is_nonpositive ? STS::magnitude(vals[colID]*vals[colID]) : (-STS::magnitude(vals[colID]*vals[colID])); // + |a_ij|^2, if a_ij < 0, - |a_ij|^2 if a_ij >=0 + /* + if(row==1326) printf("A(%d,%d) = %6.4e, raw_aij = %6.4e aij = %6.4e aiiajj = %6.4e\n",row,col,vals[colID], + vals[colID],aij, aiiajj); + */ + + if ((!rowIsDirichlet && aij > aiiajj) || row == col) { + columns[realnnz++] = col; + rownnz++; + } else + numDropped++; + } + rows[row+1] = realnnz; + } else { // Standard abs classical for (LO colID = 0; colID < Teuchos::as(nnz); colID++) { diff --git a/packages/muelu/src/Transfers/Smoothed-Aggregation/MueLu_SaPFactory_decl.hpp b/packages/muelu/src/Transfers/Smoothed-Aggregation/MueLu_SaPFactory_decl.hpp index 43c926dda95f..eb6dabbad4d9 100644 --- a/packages/muelu/src/Transfers/Smoothed-Aggregation/MueLu_SaPFactory_decl.hpp +++ b/packages/muelu/src/Transfers/Smoothed-Aggregation/MueLu_SaPFactory_decl.hpp @@ -146,6 +146,10 @@ template A, RCP& P) const; + void newSatisfyPConstraints(RCP& P) const; + + bool constrainRow(Scalar *orig, LocalOrdinal nEntries, Scalar leftBound, Scalar rghtBound,Scalar rsumTarget, Scalar *fixedUnsorted, Scalar *scalarData) const; + //@} diff --git a/packages/muelu/src/Transfers/Smoothed-Aggregation/MueLu_SaPFactory_def.hpp b/packages/muelu/src/Transfers/Smoothed-Aggregation/MueLu_SaPFactory_def.hpp index b1480a2f065c..84ff4905b2d4 100644 --- a/packages/muelu/src/Transfers/Smoothed-Aggregation/MueLu_SaPFactory_def.hpp +++ b/packages/muelu/src/Transfers/Smoothed-Aggregation/MueLu_SaPFactory_def.hpp @@ -226,7 +226,10 @@ namespace MueLu { // finalP = Ptent + (I - \omega D^{-1}A) Ptent finalP = Xpetra::IteratorOps::Jacobi(omega, *invDiag, *A, *Ptent, finalP, GetOStream(Statistics2), std::string("MueLu::SaP-")+levelIDs, APparams); - if (enforceConstraints) SatisfyPConstraints( A, finalP); + if (enforceConstraints) { + if (A->GetFixedBlockSize() == 1) newSatisfyPConstraints( finalP); + else SatisfyPConstraints( A, finalP); + } } } else { @@ -377,6 +380,325 @@ namespace MueLu { } // for (size_t i = 0; i < as(P->getRowMap()->getNumNodeElements()); i++) ... } //SatsifyPConstraints() + template + void SaPFactory::newSatisfyPConstraints(RCP& P) const { + + const Scalar zero = Teuchos::ScalarTraits::zero(); + const Scalar one = Teuchos::ScalarTraits::one(); + + LocalOrdinal maxEntriesPerRow = 100; // increased later if needed + Teuchos::ArrayRCP scalarData(3*maxEntriesPerRow); + bool hasFeasible; + + for (size_t i = 0; i < as(P->getRowMap()->getNodeNumElements()); i++) { + + Teuchos::ArrayView indices; + Teuchos::ArrayView vals1; + Teuchos::ArrayView< Scalar> vals; + P->getLocalRowView((LocalOrdinal) i, indices, vals1); + size_t nnz = indices.size(); + if (nnz != 0) { + + vals = ArrayView(const_cast(vals1.getRawPtr()), nnz); + Scalar rsumTarget = zero; + for (size_t j = 0; j < nnz; j++) rsumTarget += vals[j]; + + if (nnz > as(maxEntriesPerRow)) { + maxEntriesPerRow = nnz*3; + scalarData.resize(3*maxEntriesPerRow); + } + hasFeasible = constrainRow(vals.getRawPtr(), as(nnz), zero , one, rsumTarget, vals.getRawPtr(), scalarData.getRawPtr()); + + if (!hasFeasible) { // just set all entries to the same value giving a row sum of 1 + for (size_t j = 0; j < nnz; j++) vals[j] = one/as(nnz); + } + } + + } // for (size_t i = 0; i < as(P->getRowMap()->getNumNodeElements()); i++) ... + } //SatsifyPConstraints() + + template + bool SaPFactory::constrainRow(Scalar *orig, LocalOrdinal nEntries, Scalar leftBound, Scalar rghtBound,Scalar rsumTarget, Scalar *fixedUnsorted, Scalar *scalarData) const { +/* + Input + orig data that should be adjusted to satisfy bound constraints and + row sum constraint. orig is not modified by this function. + + nEntries length or 'orig' + + leftBound, define bound constraints for the results. + rghtBound + + rsumTarget defines an equality constraint for the row sum + + fixedUnsorted on output, if a feasible solutuion exists then + || orig - fixedUnsorted || = min when also + leftBound <= fixedUnsorted[i] <= rghtBound for all i + and sum(fixedUnsorted) = rsumTarget. + + Note: it is possible to use the same pointer for + fixedUnsorted and orig. In this case, orig gets + overwritten with the new constraint satisfying values. + + scalarData a work array that should be 3x nEntries. + + On return constrain() indicates whether or not a feasible solution exists. +*/ + +/* + Given a sequence of numbers o1 ... on, fix these so that they are as + close as possible to the original but satisfy bound constraints and also + have the same row sum as the oi's. If we know who is going to lie on a + bound, then the "best" answer (i.e., || o - f ||_2 = min) perturbs + each element that doesn't lie on a bound by the same amount. + + We can represent the oi's by considering scattered points on a number line + + | | + | | + o o o | o o o o o o |o o + | | + + \____/ \____/ + <---- <---- + delta delta + + Bounds are shown by vertical lines. The fi's must lie within the bounds, so + the 3 leftmost points must be shifted to the right and the 2 rightmost must + be shifted to the left. If these fi points are all shifted to the bounds + while the others remain in the same location, the row sum constraint is + likely not satisfied and so more shifting is necessary. In the figure, the f + rowsum is too large and so there must be more shifting to the left. + + To minimize || o - f ||_2, we basically shift all "interiors" by the same + amount, denoted delta. The only trick is that some points near bounds are + still affected by the bounds and so these points might be shifted more or less + than delta. In the example,t he 3 rightmost points are shifted in the opposite + direction as delta to the bound. The 4th point is shifted by something less + than delta so it does not violate the lower bound. The rightmost point is + shifted to the bound by some amount larger than delta. However, the 2nd point + is shifted by delta (i.e., it lies inside the two bounds). + + If we know delta, we can figure out everything. If we know which points + are special (not shifted by delta), we can also figure out everything. + The problem is these two things (delta and the special points) are + inter-connected. An algorithm for computing follows. + + 1) move exterior points to the bounds and compute how much the row sum is off + (rowSumDeviation). We assume now that the new row sum is high, so interior + points must be shifted left. + + 2) Mark closest point just left of the leftmost bound, closestToLeftBound, + and compute its distance to the leftmost bound. Mark closest point to the + left of the rightmost bound, closestToRghtBound, and compute its distance to + right bound. There are two cases to consider. + + 3) Case 1: closestToLeftBound is closer than closestToRghtBound. + Assume that shifting by delta does not move closestToLeftBound past the + left bound. This means that it will be shifted by delta. However, + closestToRghtBound will be shifted by more than delta. So the total + number of points shifted by delta (|interiorToBounds|) includes + closestToLeftBound up to and including the point just to the left of + closestToRghtBound. So + + delta = rowSumDeviation/ |interiorToBounds| . + + Recall that rowSumDeviation already accounts for the non-delta shift of + of closestToRightBound. Now check whether our assumption is valid. + + If delta <= closestToLeftBoundDist, assumption is true so delta can be + applied to interiorToBounds ... and we are done. + Else assumption is false. Shift closestToLeftBound to the left bound. + Update rowSumDeviation, interiorToBounds, and identify new + closestToLeftBound. Repeat step 3). + + Case 2: closestToRghtBound is closer than closestToLeftBound. + Assume that shifting by delta does not move closestToRghtBound past right + bound. This means that it must be shifted by more than delta to right + bound. So the total number of points shifted by delta again includes + closestToLeftBound up to and including the point just to the left of + closestToRghtBound. So again compute + + delta = rowSumDeviation/ |interiorToBounds| . + + If delta <= closestToRghtBoundDist, assumption is true so delta is + can be applied to interiorToBounds ... and we are done + Else assumption is false. Put closestToRghtBound in the + interiorToBounds set. Remove it's contribution to rowSumDeviation, + identify new closestToRghtBound. Repeat step 3) + + + To implement, sort the oi's so things like closestToLeftBound is just index + into sorted array. Updaing closestToLeftBound or closestToRghtBound requires + increment by 1. |interiorToBounds|= closestToRghtBound - closestToLeftBound + To handle the case when the rowsum is low (requiring right interior shifts), + just flip the signs on data and use the left-shift code (and then flip back + before exiting function. +*/ + bool hasFeasibleSol; + Scalar notFlippedLeftBound, notFlippedRghtBound, aBigNumber, *origSorted; + Scalar rowSumDeviation, temp, *fixedSorted, delta; + Scalar closestToLeftBoundDist, closestToRghtBoundDist; + LocalOrdinal closestToLeftBound, closestToRghtBound; + LocalOrdinal *inds; + bool flipped; + + notFlippedLeftBound = leftBound; + notFlippedRghtBound = rghtBound; + + if ((Teuchos::ScalarTraits::real(rsumTarget) >= Teuchos::ScalarTraits::real(leftBound*as(nEntries))) && + (Teuchos::ScalarTraits::real(rsumTarget) <= Teuchos::ScalarTraits::real(rghtBound*as(nEntries)))) + hasFeasibleSol = true; + else { + hasFeasibleSol=false; + return hasFeasibleSol; + } + flipped = false; + // compute aBigNumber to handle some corner cases where we need + // something large so that an if statement will be false + aBigNumber = Teuchos::ScalarTraits::zero(); + for (LocalOrdinal i = 0; i < nEntries; i++) { + if ( Teuchos::ScalarTraits::magnitude(orig[i]) > Teuchos::ScalarTraits::magnitude(aBigNumber)) + aBigNumber = Teuchos::ScalarTraits::magnitude(orig[i]); + } + aBigNumber = aBigNumber+ (Teuchos::ScalarTraits::magnitude(leftBound) + Teuchos::ScalarTraits::magnitude(rghtBound))*as(100.0); + + origSorted = &scalarData[0]; + fixedSorted = &(scalarData[nEntries]); + inds = (LocalOrdinal *) &(scalarData[2*nEntries]); + + for (LocalOrdinal i = 0; i < nEntries; i++) inds[i] = i; + for (LocalOrdinal i = 0; i < nEntries; i++) origSorted[i] = orig[i]; /* orig no longer used */ + + // sort so that orig[inds] is sorted. + std::sort(inds, inds+nEntries, + [origSorted](LocalOrdinal leftIndex, LocalOrdinal rightIndex) + { return Teuchos::ScalarTraits::real(origSorted[leftIndex]) < Teuchos::ScalarTraits::real(origSorted[rightIndex]);}); + + for (LocalOrdinal i = 0; i < nEntries; i++) origSorted[i] = orig[inds[i]]; + // find entry in origSorted just to the right of the leftBound + closestToLeftBound = 0; + while ((closestToLeftBound < nEntries) && (Teuchos::ScalarTraits::real(origSorted[closestToLeftBound]) <= Teuchos::ScalarTraits::real(leftBound))) closestToLeftBound++; + + // find entry in origSorted just to the right of the rghtBound + closestToRghtBound = closestToLeftBound; + while ((closestToRghtBound < nEntries) && (Teuchos::ScalarTraits::real(origSorted[closestToRghtBound]) <= Teuchos::ScalarTraits::real(rghtBound))) closestToRghtBound++; + + // compute distance between closestToLeftBound and the left bound and the + // distance between closestToRghtBound and the right bound. + + closestToLeftBoundDist = origSorted[closestToLeftBound] - leftBound; + if (closestToRghtBound==nEntries) closestToRghtBoundDist= aBigNumber; + else closestToRghtBoundDist= origSorted[closestToRghtBound] - rghtBound; + + // compute how far the rowSum is off from the target row sum taking into account + // numbers that have been shifted to satisfy bound constraint + + rowSumDeviation = leftBound*as(closestToLeftBound) + as((nEntries-closestToRghtBound))*rghtBound - rsumTarget; + for (LocalOrdinal i=closestToLeftBound; i < closestToRghtBound; i++) rowSumDeviation += origSorted[i]; + + // the code that follow after this if statement assumes that rowSumDeviation is positive. If this + // is not the case, flip the signs of everything so that rowSumDeviation is now positive. + // Later we will flip the data back to its original form. + if (Teuchos::ScalarTraits::real(rowSumDeviation) < Teuchos::ScalarTraits::real(Teuchos::ScalarTraits::zero())) { + flipped = true; + temp = leftBound; leftBound = -rghtBound; rghtBound = temp; + + /* flip sign of origSorted and reverse ordering so that the negative version is sorted */ + + if ((nEntries%2) == 1) origSorted[(nEntries/2) ] = -origSorted[(nEntries/2) ]; + for (LocalOrdinal i=0; i < nEntries/2; i++) { + temp=origSorted[i]; + origSorted[i] = -origSorted[nEntries-1-i]; + origSorted[nEntries-i-1] = -temp; + } + + /* reverse bounds */ + + LocalOrdinal itemp = closestToLeftBound; + closestToLeftBound = nEntries-closestToRghtBound; + closestToRghtBound = nEntries-itemp; + closestToLeftBoundDist = origSorted[closestToLeftBound] - leftBound; + if (closestToRghtBound==nEntries) closestToRghtBoundDist= aBigNumber; + else closestToRghtBoundDist= origSorted[closestToRghtBound] - rghtBound; + + rowSumDeviation = -rowSumDeviation; + } + + // initial fixedSorted so bounds are satisfied and interiors correspond to origSorted + + for (LocalOrdinal i = 0; i < closestToLeftBound; i++) fixedSorted[i] = leftBound; + for (LocalOrdinal i = closestToLeftBound; i < closestToRghtBound; i++) fixedSorted[i] = origSorted[i]; + for (LocalOrdinal i = closestToRghtBound; i < nEntries; i++) fixedSorted[i] = rghtBound; + + while ((Teuchos::ScalarTraits::magnitude(rowSumDeviation) > Teuchos::ScalarTraits::magnitude(as(1.e-10)*rsumTarget))){ // && ( (closestToLeftBound < nEntries ) || (closestToRghtBound < nEntries))) { + if (closestToRghtBound != closestToLeftBound) + delta = rowSumDeviation/ as(closestToRghtBound - closestToLeftBound); + else delta = aBigNumber; + + if (Teuchos::ScalarTraits::magnitude(closestToLeftBoundDist) <= Teuchos::ScalarTraits::magnitude(closestToRghtBoundDist)) { + if (Teuchos::ScalarTraits::magnitude(delta) <= Teuchos::ScalarTraits::magnitude(closestToLeftBoundDist)) { + rowSumDeviation = Teuchos::ScalarTraits::zero(); + for (LocalOrdinal i = closestToLeftBound; i < closestToRghtBound ; i++) fixedSorted[i] = origSorted[i] - delta; + } + else { + rowSumDeviation = rowSumDeviation - closestToLeftBoundDist; + fixedSorted[closestToLeftBound] = leftBound; + closestToLeftBound++; + if (closestToLeftBound < nEntries) closestToLeftBoundDist = origSorted[closestToLeftBound] - leftBound; + else closestToLeftBoundDist = aBigNumber; + } + } + else { + if (Teuchos::ScalarTraits::magnitude(delta) <= Teuchos::ScalarTraits::magnitude(closestToRghtBoundDist)) { + rowSumDeviation = 0; + for (LocalOrdinal i = closestToLeftBound; i < closestToRghtBound ; i++) fixedSorted[i] = origSorted[i] - delta; + } + else { + rowSumDeviation = rowSumDeviation + closestToRghtBoundDist; +// if (closestToRghtBound < nEntries) { + fixedSorted[closestToRghtBound] = origSorted[closestToRghtBound]; + closestToRghtBound++; + // } + if (closestToRghtBound >= nEntries) closestToRghtBoundDist = aBigNumber; + else closestToRghtBoundDist = origSorted[closestToRghtBound] - rghtBound; + } + } + } + + if (flipped) { + /* flip sign of fixedSorted and reverse ordering so that the positve version is sorted */ + + if ((nEntries%2) == 1) fixedSorted[(nEntries/2) ] = -fixedSorted[(nEntries/2) ]; + for (LocalOrdinal i=0; i < nEntries/2; i++) { + temp=fixedSorted[i]; + fixedSorted[i] = -fixedSorted[nEntries-1-i]; + fixedSorted[nEntries-i-1] = -temp; + } + } + for (LocalOrdinal i = 0; i < nEntries; i++) fixedUnsorted[inds[i]] = fixedSorted[i]; + + /* check that no constraints are violated */ + + bool lowerViolation = false; + bool upperViolation = false; + bool sumViolation = false; + temp = Teuchos::ScalarTraits::zero(); + for (LocalOrdinal i = 0; i < nEntries; i++) { + if (Teuchos::ScalarTraits::real(fixedUnsorted[i]) < Teuchos::ScalarTraits::real(notFlippedLeftBound)) lowerViolation = true; + if (Teuchos::ScalarTraits::real(fixedUnsorted[i]) > Teuchos::ScalarTraits::real(notFlippedRghtBound)) upperViolation = true; + temp += fixedUnsorted[i]; + } + if (Teuchos::ScalarTraits::magnitude(temp - rsumTarget) > Teuchos::ScalarTraits::magnitude(as(1.0e-8)*rsumTarget)) sumViolation = true; + + TEUCHOS_TEST_FOR_EXCEPTION(lowerViolation, Exceptions::RuntimeError, "MueLu::SaPFactory::constrainRow: feasible solution but computation resulted in a lower bound violation??? "); + TEUCHOS_TEST_FOR_EXCEPTION(upperViolation, Exceptions::RuntimeError, "MueLu::SaPFactory::constrainRow: feasible solution but computation resulted in an upper bound violation??? "); + TEUCHOS_TEST_FOR_EXCEPTION(sumViolation, Exceptions::RuntimeError, "MueLu::SaPFactory::constrainRow: feasible solution but computation resulted in a row sum violation??? "); + + return hasFeasibleSol; +} + + } //namespace MueLu #endif // MUELU_SAPFACTORY_DEF_HPP diff --git a/packages/muelu/test/unit_tests/CoalesceDropFactory.cpp b/packages/muelu/test/unit_tests/CoalesceDropFactory.cpp index c730e165e812..63883e9521c8 100644 --- a/packages/muelu/test/unit_tests/CoalesceDropFactory.cpp +++ b/packages/muelu/test/unit_tests/CoalesceDropFactory.cpp @@ -1620,6 +1620,47 @@ namespace MueLuTests { + TEUCHOS_UNIT_TEST_TEMPLATE_4_DECL(CoalesceDropFactory, SignedClassicalSA, Scalar, LocalOrdinal, GlobalOrdinal, Node) { +# include + typedef Teuchos::ScalarTraits STS; + typedef typename STS::magnitudeType real_type; + + MUELU_TESTING_SET_OSTREAM; + MUELU_TESTING_LIMIT_SCOPE(Scalar,GlobalOrdinal,Node); + out << "version: " << MueLu::Version() << std::endl; + + RCP > comm = Parameters::getDefaultComm(); + Xpetra::UnderlyingLib lib = TestHelpers::Parameters::getLib(); + + GO nx = 10*comm->getSize(); + Teuchos::ParameterList matrixList; + matrixList.set("nx",nx); + matrixList.set("ny",(GO)10); + matrixList.set("nz",(GO)10); + matrixList.set("matrixType","Laplace3D"); + RCP A =TestHelpers::TestFactory::BuildMatrix(matrixList,lib); + + Level fineLevel; + fineLevel.Set("A", A); + + + RCP amalgFact = rcp(new AmalgamationFactory()); + CoalesceDropFactory coalesceDropFact; + coalesceDropFact.SetFactory("UnAmalgamationInfo",amalgFact); + coalesceDropFact.SetParameter("aggregation: drop tol",Teuchos::ParameterEntry(0.0)); + coalesceDropFact.SetParameter("aggregation: drop scheme",Teuchos::ParameterEntry(std::string("signed classical sa"))); + fineLevel.Request("Graph",&coalesceDropFact); + fineLevel.Request("DofsPerNode", &coalesceDropFact); + + coalesceDropFact.Build(fineLevel); + + RCP graph = fineLevel.Get >("Graph", &coalesceDropFact); + LO myDofsPerNode = fineLevel.Get("DofsPerNode", &coalesceDropFact); + TEST_EQUALITY(Teuchos::as(myDofsPerNode) == 1, true); + } + + + #define MUELU_ETI_GROUP(SC,LO,GO,Node) \ TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(CoalesceDropFactory,Constructor,SC,LO,GO,Node) \ TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(CoalesceDropFactory,Build,SC,LO,GO,Node) \ @@ -1642,7 +1683,8 @@ namespace MueLuTests { TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(CoalesceDropFactory,BlockDiagonalDistanceLaplacian,SC,LO,GO,Node) \ TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(CoalesceDropFactory,BlockDiagonalDistanceLaplacianWeighted,SC,LO,GO,Node) \ TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(CoalesceDropFactory,DistanceLaplacianWeighted,SC,LO,GO,Node) \ - TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(CoalesceDropFactory,AggresiveDroppingIsMarkedAsBoundary,SC,LO,GO,Node) + TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(CoalesceDropFactory,AggresiveDroppingIsMarkedAsBoundary,SC,LO,GO,Node) \ + TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(CoalesceDropFactory,SignedClassicalSA,SC,LO,GO,Node) \ #include diff --git a/packages/panzer/disc-fe/src/Panzer_IntegrationValues2.cpp b/packages/panzer/disc-fe/src/Panzer_IntegrationValues2.cpp index cbe5c45a2d3d..77e14cb368bd 100644 --- a/packages/panzer/disc-fe/src/Panzer_IntegrationValues2.cpp +++ b/packages/panzer/disc-fe/src/Panzer_IntegrationValues2.cpp @@ -1717,6 +1717,7 @@ getCubaturePointsRef(const bool cache, Kokkos::parallel_for("copy values",policy,KOKKOS_LAMBDA (const int cell,const int point, const int dim) { aux(cell,point_offset + point,dim) = side_cub_points(point,dim); }); + PHX::Device::execution_space().fence(); } } else { diff --git a/packages/shylu/shylu_node/basker/src/shylubasker_def.hpp b/packages/shylu/shylu_node/basker/src/shylubasker_def.hpp index b5f973fb1248..8a2da3fd7c5a 100644 --- a/packages/shylu/shylu_node/basker/src/shylubasker_def.hpp +++ b/packages/shylu/shylu_node/basker/src/shylubasker_def.hpp @@ -2202,11 +2202,7 @@ namespace BaskerNS //Next test if Kokkos has that many threads! //This is a common mistake in mpi-based apps #ifdef KOKKOS_ENABLE_OPENMP - #ifdef KOKKOS_ENABLE_DEPRECATED_CODE - int check_value = Kokkos::OpenMP::max_hardware_threads(); - #else int check_value = Kokkos::OpenMP::impl_max_hardware_threads(); - #endif if(nthreads > check_value) { BASKER_ASSERT(0==1, "Basker SetThreads Assert: Number of thread not available"); diff --git a/packages/stk/stk_emend/stk_emend/independent_set/CMakeLists.txt b/packages/stk/stk_emend/stk_emend/independent_set/CMakeLists.txt index 65456ef38091..961a9218c3b9 100644 --- a/packages/stk/stk_emend/stk_emend/independent_set/CMakeLists.txt +++ b/packages/stk/stk_emend/stk_emend/independent_set/CMakeLists.txt @@ -46,8 +46,18 @@ # SET(HEADERS "") +SET(SOURCES "") + +INCLUDE_DIRECTORIES(${${PACKAGE_NAME}_SOURCE_DIR}) FILE(GLOB HEADERS *.hpp) +FILE(GLOB SOURCES *.cpp) + +TRIBITS_ADD_LIBRARY( + stk_emend + HEADERS ${HEADERS} + SOURCES ${SOURCES} + ) INSTALL(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/${${PROJECT_NAME}_INSTALL_INCLUDE_DIR}/stk_emend/independent_set) diff --git a/packages/stk/stk_emend/stk_emend/independent_set/IndependentSetDummy.cpp b/packages/stk/stk_emend/stk_emend/independent_set/IndependentSetDummy.cpp new file mode 100644 index 000000000000..b2ef6c6c65a0 --- /dev/null +++ b/packages/stk/stk_emend/stk_emend/independent_set/IndependentSetDummy.cpp @@ -0,0 +1,3 @@ +namespace independent_set { +void IndependentSetDummy() {} +} diff --git a/packages/teuchos/parameterlist/src/CMakeLists.txt b/packages/teuchos/parameterlist/src/CMakeLists.txt index 2374fe597f79..e5db66fa4013 100644 --- a/packages/teuchos/parameterlist/src/CMakeLists.txt +++ b/packages/teuchos/parameterlist/src/CMakeLists.txt @@ -25,5 +25,5 @@ TRIBITS_ADD_LIBRARY( ) if (WIN32) - target_link_libraries (teuchosparameterlist ws2_32) + target_link_libraries (teuchosparameterlist PUBLIC ws2_32) endif () diff --git a/packages/thyra/core/src/support/operator_vector/adapter_support/Thyra_SpmdVectorDefaultBase_def.hpp b/packages/thyra/core/src/support/operator_vector/adapter_support/Thyra_SpmdVectorDefaultBase_def.hpp index 3213c3a961ec..560f819c4228 100644 --- a/packages/thyra/core/src/support/operator_vector/adapter_support/Thyra_SpmdVectorDefaultBase_def.hpp +++ b/packages/thyra/core/src/support/operator_vector/adapter_support/Thyra_SpmdVectorDefaultBase_def.hpp @@ -57,7 +57,7 @@ #include "Teuchos_Assert.hpp" #include "Teuchos_dyn_cast.hpp" #include "Teuchos_Assert.hpp" - +#include "Teuchos_Range1D.hpp" namespace Thyra { @@ -200,7 +200,7 @@ void SpmdVectorDefaultBase::acquireDetachedVectorViewImpl( #ifdef THYRA_DEBUG TEUCHOS_ASSERT(sub_vec); #endif - if( rng_in == Range1D::Invalid ) { + if( rng_in == Range1D::INVALID ) { // Just return an null view *sub_vec = RTOpPack::ConstSubVectorView(); return; @@ -270,7 +270,7 @@ void SpmdVectorDefaultBase::acquireNonconstDetachedVectorViewImpl( #ifdef THYRA_DEBUG TEUCHOS_ASSERT(sub_vec); #endif - if( rng_in == Range1D::Invalid ) { + if( rng_in == Range1D::INVALID ) { // Just return an null view *sub_vec = RTOpPack::SubVectorView(); return; diff --git a/packages/tpetra/core/example/BlockCrs/Tpetra_TestBlockCrsMeshDatabase.hpp b/packages/tpetra/core/example/BlockCrs/Tpetra_TestBlockCrsMeshDatabase.hpp index 777a0eabe7c9..b722be73a433 100644 --- a/packages/tpetra/core/example/BlockCrs/Tpetra_TestBlockCrsMeshDatabase.hpp +++ b/packages/tpetra/core/example/BlockCrs/Tpetra_TestBlockCrsMeshDatabase.hpp @@ -117,16 +117,18 @@ namespace BlockCrsTest { template KOKKOS_INLINE_FUNCTION static void heapify(T1 *v, T2 n, T2 i) { - T2 largest = i; - T2 l = 2*i + 1; - T2 r = 2*i + 2; - - if (l < n && v[l] > v[largest]) largest = l; - if (r < n && v[r] > v[largest]) largest = r; - if (largest != i) { + while (true) { + T2 largest = i; + T2 l = 2*i + 1; + T2 r = 2*i + 2; + + if (l < n && v[l] > v[largest]) largest = l; + if (r < n && v[r] > v[largest]) largest = r; + if (largest == i) + break; // swap T1 tmp = v[i]; v[i] = v[largest]; v[largest] = tmp; - heapify(v, n, largest); + i = largest; } } diff --git a/packages/tpetra/core/ext/TpetraExt_MatrixMatrix_SYCL.hpp b/packages/tpetra/core/ext/TpetraExt_MatrixMatrix_SYCL.hpp new file mode 100644 index 000000000000..8a76ac813b08 --- /dev/null +++ b/packages/tpetra/core/ext/TpetraExt_MatrixMatrix_SYCL.hpp @@ -0,0 +1,829 @@ +// @HEADER +// *********************************************************************** +// +// Tpetra: Templated Linear Algebra Services Package +// Copyright (2008) Sandia Corporation +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Michael A. Heroux (maherou@sandia.gov) +// +// ************************************************************************ +// @HEADER + + +// This is a verbatim copy of the other TpetraExt_MatrixMatrix_*.hpp files +// replacing the execution/memory space by the ones corresponding to SYCL. +#ifndef TPETRA_MATRIXMATRIX_SYCL_DEF_HPP +#define TPETRA_MATRIXMATRIX_SYCL_DEF_HPP + +#ifdef HAVE_TPETRA_INST_SYCL +namespace Tpetra { +namespace MMdetails { + +/*********************************************************************************************************/ +// MMM KernelWrappers for Partial Specialization to SYCL +template +struct KernelWrappers { + static inline void mult_A_B_newmatrix_kernel_wrapper(CrsMatrixStruct& Aview, + CrsMatrixStruct& Bview, + const LocalOrdinalViewType & Acol2Brow, + const LocalOrdinalViewType & Acol2Irow, + const LocalOrdinalViewType & Bcol2Ccol, + const LocalOrdinalViewType & Icol2Ccol, + CrsMatrix& C, + Teuchos::RCP > Cimport, + const std::string& label = std::string(), + const Teuchos::RCP& params = Teuchos::null); + + + + static inline void mult_A_B_reuse_kernel_wrapper(CrsMatrixStruct& Aview, + CrsMatrixStruct& Bview, + const LocalOrdinalViewType & Acol2Brow, + const LocalOrdinalViewType & Acol2Irow, + const LocalOrdinalViewType & Bcol2Ccol, + const LocalOrdinalViewType & Icol2Ccol, + CrsMatrix& C, + Teuchos::RCP > Cimport, + const std::string& label = std::string(), + const Teuchos::RCP& params = Teuchos::null); + +}; + +// Jacobi KernelWrappers for Partial Specialization to SYCL +template +struct KernelWrappers2 { + static inline void jacobi_A_B_newmatrix_kernel_wrapper(Scalar omega, + const Vector & Dinv, + CrsMatrixStruct& Aview, + CrsMatrixStruct& Bview, + const LocalOrdinalViewType & Acol2Brow, + const LocalOrdinalViewType & Acol2Irow, + const LocalOrdinalViewType & Bcol2Ccol, + const LocalOrdinalViewType & Icol2Ccol, + CrsMatrix& C, + Teuchos::RCP > Cimport, + const std::string& label = std::string(), + const Teuchos::RCP& params = Teuchos::null); + + static inline void jacobi_A_B_reuse_kernel_wrapper(Scalar omega, + const Vector & Dinv, + CrsMatrixStruct& Aview, + CrsMatrixStruct& Bview, + const LocalOrdinalViewType & Acol2Brow, + const LocalOrdinalViewType & Acol2Irow, + const LocalOrdinalViewType & Bcol2Ccol, + const LocalOrdinalViewType & Icol2Ccol, + CrsMatrix& C, + Teuchos::RCP > Cimport, + const std::string& label = std::string(), + const Teuchos::RCP& params = Teuchos::null); + + static inline void jacobi_A_B_newmatrix_KokkosKernels(Scalar omega, + const Vector & Dinv, + CrsMatrixStruct& Aview, + CrsMatrixStruct& Bview, + const LocalOrdinalViewType & Acol2Brow, + const LocalOrdinalViewType & Acol2Irow, + const LocalOrdinalViewType & Bcol2Ccol, + const LocalOrdinalViewType & Icol2Ccol, + CrsMatrix& C, + Teuchos::RCP > Cimport, + const std::string& label = std::string(), + const Teuchos::RCP& params = Teuchos::null); +}; + + +/*********************************************************************************************************/ +// AB NewMatrix Kernel wrappers (KokkosKernels/SYCL Version) +template +void KernelWrappers::mult_A_B_newmatrix_kernel_wrapper(CrsMatrixStruct& Aview, + CrsMatrixStruct& Bview, + const LocalOrdinalViewType & Acol2Brow, + const LocalOrdinalViewType & Acol2Irow, + const LocalOrdinalViewType & Bcol2Ccol, + const LocalOrdinalViewType & Icol2Ccol, + CrsMatrix& C, + Teuchos::RCP > Cimport, + const std::string& label, + const Teuchos::RCP& params) { + + +#ifdef HAVE_TPETRA_MMM_TIMINGS + std::string prefix_mmm = std::string("TpetraExt ") + label + std::string(": "); + using Teuchos::TimeMonitor; + Teuchos::RCP MM = rcp(new TimeMonitor(*(TimeMonitor::getNewTimer(prefix_mmm + std::string("MMM Newmatrix SYCLWrapper"))))); +#endif + // Node-specific code + typedef Kokkos::Compat::KokkosSYCLWrapperNode Node; + std::string nodename("SYCL"); + + // Lots and lots of typedefs + using Teuchos::RCP; + typedef typename Tpetra::CrsMatrix::local_matrix_device_type KCRS; + typedef typename KCRS::device_type device_t; + typedef typename KCRS::StaticCrsGraphType graph_t; + typedef typename graph_t::row_map_type::non_const_type lno_view_t; + typedef typename graph_t::row_map_type::const_type c_lno_view_t; + typedef typename graph_t::entries_type::non_const_type lno_nnz_view_t; + typedef typename KCRS::values_type::non_const_type scalar_view_t; + //typedef typename graph_t::row_map_type::const_type lno_view_t_const; + + // Options + int team_work_size = 16; // Defaults to 16 as per Deveci 12/7/16 - csiefer + std::string myalg("SPGEMM_KK_MEMORY"); + if(!params.is_null()) { + if(params->isParameter("sycl: algorithm")) + myalg = params->get("sycl: algorithm",myalg); + if(params->isParameter("sycl: team work size")) + team_work_size = params->get("sycl: team work size",team_work_size); + } + + // KokkosKernelsHandle + typedef KokkosKernels::Experimental::KokkosKernelsHandle< + typename lno_view_t::const_value_type,typename lno_nnz_view_t::const_value_type, typename scalar_view_t::const_value_type, + typename device_t::execution_space, typename device_t::memory_space,typename device_t::memory_space > KernelHandle; + + // Grab the Kokkos::SparseCrsMatrices + const KCRS & Amat = Aview.origMatrix->getLocalMatrixDevice(); + const KCRS & Bmat = Bview.origMatrix->getLocalMatrixDevice(); + + c_lno_view_t Arowptr = Amat.graph.row_map, + Browptr = Bmat.graph.row_map; + const lno_nnz_view_t Acolind = Amat.graph.entries, + Bcolind = Bmat.graph.entries; + const scalar_view_t Avals = Amat.values, + Bvals = Bmat.values; + + c_lno_view_t Irowptr; + lno_nnz_view_t Icolind; + scalar_view_t Ivals; + if(!Bview.importMatrix.is_null()) { + auto lclB = Bview.importMatrix->getLocalMatrixDevice(); + Irowptr = lclB.graph.row_map; + Icolind = lclB.graph.entries; + Ivals = lclB.values; + } + + + // Get the algorithm mode + std::string alg = nodename+std::string(" algorithm"); + // printf("DEBUG: Using kernel: %s\n",myalg.c_str()); + if(!params.is_null() && params->isParameter(alg)) myalg = params->get(alg,myalg); + KokkosSparse::SPGEMMAlgorithm alg_enum = KokkosSparse::StringToSPGEMMAlgorithm(myalg); + + // Merge the B and Bimport matrices + const KCRS Bmerged = Tpetra::MMdetails::merge_matrices(Aview,Bview,Acol2Brow,Acol2Irow,Bcol2Ccol,Icol2Ccol,C.getColMap()->getNodeNumElements()); + +#ifdef HAVE_TPETRA_MMM_TIMINGS + MM = Teuchos::null; MM = rcp(new TimeMonitor (*TimeMonitor::getNewTimer(prefix_mmm + std::string("MMM Newmatrix SYCLCore")))); +#endif + + // Do the multiply on whatever we've got + typename KernelHandle::nnz_lno_t AnumRows = Amat.numRows(); + typename KernelHandle::nnz_lno_t BnumRows = Bmerged.numRows(); + typename KernelHandle::nnz_lno_t BnumCols = Bmerged.numCols(); + + lno_view_t row_mapC (Kokkos::ViewAllocateWithoutInitializing("non_const_lnow_row"), AnumRows + 1); + lno_nnz_view_t entriesC; + scalar_view_t valuesC; + KernelHandle kh; + kh.create_spgemm_handle(alg_enum); + kh.set_team_work_size(team_work_size); + + KokkosSparse::Experimental::spgemm_symbolic(&kh,AnumRows,BnumRows,BnumCols,Amat.graph.row_map,Amat.graph.entries,false,Bmerged.graph.row_map,Bmerged.graph.entries,false,row_mapC); + + size_t c_nnz_size = kh.get_spgemm_handle()->get_c_nnz(); + if (c_nnz_size){ + entriesC = lno_nnz_view_t (Kokkos::ViewAllocateWithoutInitializing("entriesC"), c_nnz_size); + valuesC = scalar_view_t (Kokkos::ViewAllocateWithoutInitializing("valuesC"), c_nnz_size); + } + KokkosSparse::Experimental::spgemm_numeric(&kh,AnumRows,BnumRows,BnumCols,Amat.graph.row_map,Amat.graph.entries,Amat.values,false,Bmerged.graph.row_map,Bmerged.graph.entries,Bmerged.values,false,row_mapC,entriesC,valuesC); + kh.destroy_spgemm_handle(); + +#ifdef HAVE_TPETRA_MMM_TIMINGS + MM = Teuchos::null; MM = rcp(new TimeMonitor (*TimeMonitor::getNewTimer(prefix_mmm + std::string("MMM Newmatrix SYCLSort")))); +#endif + + // Sort & set values + if (params.is_null() || params->get("sort entries",true)) + Import_Util::sortCrsEntries(row_mapC, entriesC, valuesC); + C.setAllValues(row_mapC,entriesC,valuesC); + +#ifdef HAVE_TPETRA_MMM_TIMINGS + MM = Teuchos::null; MM = rcp(new TimeMonitor (*TimeMonitor::getNewTimer(prefix_mmm + std::string("MMM Newmatrix SYCLESFC")))); +#endif + + // Final Fillcomplete + RCP labelList = rcp(new Teuchos::ParameterList); + labelList->set("Timer Label",label); + if(!params.is_null()) labelList->set("compute global constants",params->get("compute global constants",true)); + RCP > dummyExport; + C.expertStaticFillComplete(Bview.origMatrix->getDomainMap(), Aview.origMatrix->getRangeMap(), Cimport,dummyExport,labelList); +} + + +/*********************************************************************************************************/ +template +void KernelWrappers::mult_A_B_reuse_kernel_wrapper( + CrsMatrixStruct& Aview, + CrsMatrixStruct& Bview, + const LocalOrdinalViewType & targetMapToOrigRow_dev, + const LocalOrdinalViewType & targetMapToImportRow_dev, + const LocalOrdinalViewType & Bcol2Ccol_dev, + const LocalOrdinalViewType & Icol2Ccol_dev, + CrsMatrix& C, + Teuchos::RCP > Cimport, + const std::string& label, + const Teuchos::RCP& params) { + + // FIXME: Right now, this is a cut-and-paste of the serial kernel + typedef Kokkos::Compat::KokkosSYCLWrapperNode Node; + +#ifdef HAVE_TPETRA_MMM_TIMINGS + std::string prefix_mmm = std::string("TpetraExt ") + label + std::string(": "); + using Teuchos::TimeMonitor; + Teuchos::RCP MM = rcp(new TimeMonitor(*TimeMonitor::getNewTimer(prefix_mmm + std::string("MMM Reuse SerialCore")))); + Teuchos::RCP MM2; +#endif + using Teuchos::RCP; + using Teuchos::rcp; + + + // Lots and lots of typedefs + typedef typename Tpetra::CrsMatrix::local_matrix_host_type KCRS; + typedef typename KCRS::StaticCrsGraphType graph_t; + typedef typename graph_t::row_map_type::const_type c_lno_view_t; + typedef typename graph_t::entries_type::non_const_type lno_nnz_view_t; + typedef typename KCRS::values_type::non_const_type scalar_view_t; + + typedef Scalar SC; + typedef LocalOrdinal LO; + typedef GlobalOrdinal GO; + typedef Node NO; + typedef Map map_type; + const size_t ST_INVALID = Teuchos::OrdinalTraits::invalid(); + const LO LO_INVALID = Teuchos::OrdinalTraits::invalid(); + const SC SC_ZERO = Teuchos::ScalarTraits::zero(); + + // Since this is being run on SYCL, we need to fence because the below code will use UVM + // typename graph_t::execution_space().fence(); + + // KDDKDD UVM Without UVM, need to copy targetMap arrays to host. + // KDDKDD UVM Ideally, this function would run on device and use + // KDDKDD UVM KokkosKernels instead of this host implementation. + auto targetMapToOrigRow = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), + targetMapToOrigRow_dev); + auto targetMapToImportRow = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), + targetMapToImportRow_dev); + auto Bcol2Ccol = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), + Bcol2Ccol_dev); + auto Icol2Ccol = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), + Icol2Ccol_dev); + + // Sizes + RCP Ccolmap = C.getColMap(); + size_t m = Aview.origMatrix->getNodeNumRows(); + size_t n = Ccolmap->getNodeNumElements(); + + // Grab the Kokkos::SparseCrsMatrices & inner stuff + const KCRS & Amat = Aview.origMatrix->getLocalMatrixHost(); + const KCRS & Bmat = Bview.origMatrix->getLocalMatrixHost(); + const KCRS & Cmat = C.getLocalMatrixHost(); + + c_lno_view_t Arowptr = Amat.graph.row_map, + Browptr = Bmat.graph.row_map, + Crowptr = Cmat.graph.row_map; + const lno_nnz_view_t Acolind = Amat.graph.entries, + Bcolind = Bmat.graph.entries, + Ccolind = Cmat.graph.entries; + const scalar_view_t Avals = Amat.values, Bvals = Bmat.values; + scalar_view_t Cvals = Cmat.values; + + c_lno_view_t Irowptr; + lno_nnz_view_t Icolind; + scalar_view_t Ivals; + if(!Bview.importMatrix.is_null()) { + auto lclB = Bview.importMatrix->getLocalMatrixHost(); + Irowptr = lclB.graph.row_map; + Icolind = lclB.graph.entries; + Ivals = lclB.values; + } + +#ifdef HAVE_TPETRA_MMM_TIMINGS + MM2 = Teuchos::null; MM2 = rcp(new TimeMonitor(*TimeMonitor::getNewTimer(prefix_mmm + std::string("MMM Newmatrix SerialCore - Compare")))); +#endif + + // Classic csr assembly (low memory edition) + // mfh 27 Sep 2016: The c_status array is an implementation detail + // of the local sparse matrix-matrix multiply routine. + + // The status array will contain the index into colind where this entry was last deposited. + // c_status[i] < CSR_ip - not in the row yet + // c_status[i] >= CSR_ip - this is the entry where you can find the data + // We start with this filled with INVALID's indicating that there are no entries yet. + // Sadly, this complicates the code due to the fact that size_t's are unsigned. + std::vector c_status(n, ST_INVALID); + + // For each row of A/C + size_t CSR_ip = 0, OLD_ip = 0; + for (size_t i = 0; i < m; i++) { + // First fill the c_status array w/ locations where we're allowed to + // generate nonzeros for this row + OLD_ip = Crowptr[i]; + CSR_ip = Crowptr[i+1]; + for (size_t k = OLD_ip; k < CSR_ip; k++) { + c_status[Ccolind[k]] = k; + + // Reset values in the row of C + Cvals[k] = SC_ZERO; + } + + for (size_t k = Arowptr[i]; k < Arowptr[i+1]; k++) { + LO Aik = Acolind[k]; + const SC Aval = Avals[k]; + if (Aval == SC_ZERO) + continue; + + if (targetMapToOrigRow[Aik] != LO_INVALID) { + // Local matrix + size_t Bk = Teuchos::as(targetMapToOrigRow[Aik]); + + for (size_t j = Browptr[Bk]; j < Browptr[Bk+1]; ++j) { + LO Bkj = Bcolind[j]; + LO Cij = Bcol2Ccol[Bkj]; + + TEUCHOS_TEST_FOR_EXCEPTION(c_status[Cij] < OLD_ip || c_status[Cij] >= CSR_ip, + std::runtime_error, "Trying to insert a new entry (" << i << "," << Cij << ") into a static graph " << + "(c_status = " << c_status[Cij] << " of [" << OLD_ip << "," << CSR_ip << "))"); + + Cvals[c_status[Cij]] += Aval * Bvals[j]; + } + + } else { + // Remote matrix + size_t Ik = Teuchos::as(targetMapToImportRow[Aik]); + for (size_t j = Irowptr[Ik]; j < Irowptr[Ik+1]; ++j) { + LO Ikj = Icolind[j]; + LO Cij = Icol2Ccol[Ikj]; + + TEUCHOS_TEST_FOR_EXCEPTION(c_status[Cij] < OLD_ip || c_status[Cij] >= CSR_ip, + std::runtime_error, "Trying to insert a new entry (" << i << "," << Cij << ") into a static graph " << + "(c_status = " << c_status[Cij] << " of [" << OLD_ip << "," << CSR_ip << "))"); + + Cvals[c_status[Cij]] += Aval * Ivals[j]; + } + } + } + } + + C.fillComplete(C.getDomainMap(), C.getRangeMap()); +} + +/*********************************************************************************************************/ +template +void KernelWrappers2::jacobi_A_B_newmatrix_kernel_wrapper(Scalar omega, + const Vector & Dinv, + CrsMatrixStruct& Aview, + CrsMatrixStruct& Bview, + const LocalOrdinalViewType & Acol2Brow, + const LocalOrdinalViewType & Acol2Irow, + const LocalOrdinalViewType & Bcol2Ccol, + const LocalOrdinalViewType & Icol2Ccol, + CrsMatrix& C, + Teuchos::RCP > Cimport, + const std::string& label, + const Teuchos::RCP& params) { + +#ifdef HAVE_TPETRA_MMM_TIMINGS + std::string prefix_mmm = std::string("TpetraExt ") + label + std::string(": "); + using Teuchos::TimeMonitor; + Teuchos::RCP MM; +#endif + + // Node-specific code + using Teuchos::RCP; + + // Options + //int team_work_size = 16; // Defaults to 16 as per Deveci 12/7/16 - csiefer // unreferenced + std::string myalg("KK"); + if(!params.is_null()) { + if(params->isParameter("sycl: jacobi algorithm")) + myalg = params->get("sycl: jacobi algorithm",myalg); + } + + if(myalg == "MSAK") { + ::Tpetra::MatrixMatrix::ExtraKernels::jacobi_A_B_newmatrix_MultiplyScaleAddKernel(omega,Dinv,Aview,Bview,Acol2Brow,Acol2Irow,Bcol2Ccol,Icol2Ccol,C,Cimport,label,params); + } + else if(myalg == "KK") { + jacobi_A_B_newmatrix_KokkosKernels(omega,Dinv,Aview,Bview,Acol2Brow,Acol2Irow,Bcol2Ccol,Icol2Ccol,C,Cimport,label,params); + } + else { + throw std::runtime_error("Tpetra::MatrixMatrix::Jacobi newmatrix unknown kernel"); + } + +#ifdef HAVE_TPETRA_MMM_TIMINGS + MM = Teuchos::null; MM = rcp(new TimeMonitor (*TimeMonitor::getNewTimer(prefix_mmm + std::string("Jacobi Newmatrix SYCLESFC")))); +#endif + + // Final Fillcomplete + RCP labelList = rcp(new Teuchos::ParameterList); + labelList->set("Timer Label",label); + if(!params.is_null()) labelList->set("compute global constants",params->get("compute global constants",true)); + + // NOTE: MSAK already fillCompletes, so we have to check here + if(!C.isFillComplete()) { + RCP > dummyExport; + C.expertStaticFillComplete(Bview.origMatrix->getDomainMap(), Aview.origMatrix->getRangeMap(), Cimport,dummyExport,labelList); + } + +} + + + +/*********************************************************************************************************/ +template +void KernelWrappers2::jacobi_A_B_reuse_kernel_wrapper(Scalar omega, + const Vector & Dinv, + CrsMatrixStruct& Aview, + CrsMatrixStruct& Bview, + const LocalOrdinalViewType & targetMapToOrigRow_dev, + const LocalOrdinalViewType & targetMapToImportRow_dev, + const LocalOrdinalViewType & Bcol2Ccol_dev, + const LocalOrdinalViewType & Icol2Ccol_dev, + CrsMatrix& C, + Teuchos::RCP > Cimport, + const std::string& label, + const Teuchos::RCP& params) { + + // FIXME: Right now, this is a cut-and-paste of the serial kernel + typedef Kokkos::Compat::KokkosSYCLWrapperNode Node; + +#ifdef HAVE_TPETRA_MMM_TIMINGS + std::string prefix_mmm = std::string("TpetraExt ") + label + std::string(": "); + using Teuchos::TimeMonitor; + Teuchos::RCP MM = rcp(new TimeMonitor(*TimeMonitor::getNewTimer(prefix_mmm + std::string("Jacobi Reuse SYCLCore")))); + Teuchos::RCP MM2; +#endif + using Teuchos::RCP; + using Teuchos::rcp; + + // Lots and lots of typedefs + typedef typename Tpetra::CrsMatrix::local_matrix_host_type KCRS; + typedef typename KCRS::StaticCrsGraphType graph_t; + typedef typename graph_t::row_map_type::const_type c_lno_view_t; + typedef typename graph_t::entries_type::non_const_type lno_nnz_view_t; + typedef typename KCRS::values_type::non_const_type scalar_view_t; + typedef typename scalar_view_t::memory_space scalar_memory_space; + + typedef Scalar SC; + typedef LocalOrdinal LO; + typedef GlobalOrdinal GO; + typedef Node NO; + typedef Map map_type; + const size_t ST_INVALID = Teuchos::OrdinalTraits::invalid(); + const LO LO_INVALID = Teuchos::OrdinalTraits::invalid(); + const SC SC_ZERO = Teuchos::ScalarTraits::zero(); + + // Since this is being run on SYCL, we need to fence because the below host code will use UVM + // KDDKDD typename graph_t::execution_space().fence(); + + // KDDKDD UVM Without UVM, need to copy targetMap arrays to host. + // KDDKDD UVM Ideally, this function would run on device and use + // KDDKDD UVM KokkosKernels instead of this host implementation. + auto targetMapToOrigRow = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), + targetMapToOrigRow_dev); + auto targetMapToImportRow = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), + targetMapToImportRow_dev); + auto Bcol2Ccol = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), + Bcol2Ccol_dev); + auto Icol2Ccol = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), + Icol2Ccol_dev); + + + // Sizes + RCP Ccolmap = C.getColMap(); + size_t m = Aview.origMatrix->getNodeNumRows(); + size_t n = Ccolmap->getNodeNumElements(); + + // Grab the Kokkos::SparseCrsMatrices & inner stuff + const KCRS & Amat = Aview.origMatrix->getLocalMatrixHost(); + const KCRS & Bmat = Bview.origMatrix->getLocalMatrixHost(); + const KCRS & Cmat = C.getLocalMatrixHost(); + + c_lno_view_t Arowptr = Amat.graph.row_map, Browptr = Bmat.graph.row_map, Crowptr = Cmat.graph.row_map; + const lno_nnz_view_t Acolind = Amat.graph.entries, Bcolind = Bmat.graph.entries, Ccolind = Cmat.graph.entries; + const scalar_view_t Avals = Amat.values, Bvals = Bmat.values; + scalar_view_t Cvals = Cmat.values; + + c_lno_view_t Irowptr; + lno_nnz_view_t Icolind; + scalar_view_t Ivals; + if(!Bview.importMatrix.is_null()) { + auto lclB = Bview.importMatrix->getLocalMatrixHost(); + Irowptr = lclB.graph.row_map; + Icolind = lclB.graph.entries; + Ivals = lclB.values; + } + + // Jacobi-specific inner stuff + auto Dvals = + Dinv.template getLocalView(Access::ReadOnly); + +#ifdef HAVE_TPETRA_MMM_TIMINGS + MM2 = Teuchos::null; MM2 = rcp(new TimeMonitor(*TimeMonitor::getNewTimer(prefix_mmm + std::string("Jacobi Reuse SYCLCore - Compare")))); +#endif + + // The status array will contain the index into colind where this entry was last deposited. + // c_status[i] < CSR_ip - not in the row yet + // c_status[i] >= CSR_ip - this is the entry where you can find the data + // We start with this filled with INVALID's indicating that there are no entries yet. + // Sadly, this complicates the code due to the fact that size_t's are unsigned. + std::vector c_status(n, ST_INVALID); + + // For each row of A/C + size_t CSR_ip = 0, OLD_ip = 0; + for (size_t i = 0; i < m; i++) { + + // First fill the c_status array w/ locations where we're allowed to + // generate nonzeros for this row + OLD_ip = Crowptr[i]; + CSR_ip = Crowptr[i+1]; + for (size_t k = OLD_ip; k < CSR_ip; k++) { + c_status[Ccolind[k]] = k; + + // Reset values in the row of C + Cvals[k] = SC_ZERO; + } + + SC minusOmegaDval = -omega*Dvals(i,0); + + // Entries of B + for (size_t j = Browptr[i]; j < Browptr[i+1]; j++) { + Scalar Bval = Bvals[j]; + if (Bval == SC_ZERO) + continue; + LO Bij = Bcolind[j]; + LO Cij = Bcol2Ccol[Bij]; + + TEUCHOS_TEST_FOR_EXCEPTION(c_status[Cij] < OLD_ip || c_status[Cij] >= CSR_ip, + std::runtime_error, "Trying to insert a new entry into a static graph"); + + Cvals[c_status[Cij]] = Bvals[j]; + } + + // Entries of -omega * Dinv * A * B + for (size_t k = Arowptr[i]; k < Arowptr[i+1]; k++) { + LO Aik = Acolind[k]; + const SC Aval = Avals[k]; + if (Aval == SC_ZERO) + continue; + + if (targetMapToOrigRow[Aik] != LO_INVALID) { + // Local matrix + size_t Bk = Teuchos::as(targetMapToOrigRow[Aik]); + + for (size_t j = Browptr[Bk]; j < Browptr[Bk+1]; ++j) { + LO Bkj = Bcolind[j]; + LO Cij = Bcol2Ccol[Bkj]; + + TEUCHOS_TEST_FOR_EXCEPTION(c_status[Cij] < OLD_ip || c_status[Cij] >= CSR_ip, + std::runtime_error, "Trying to insert a new entry into a static graph"); + + Cvals[c_status[Cij]] += minusOmegaDval * Aval * Bvals[j]; + } + + } else { + // Remote matrix + size_t Ik = Teuchos::as(targetMapToImportRow[Aik]); + for (size_t j = Irowptr[Ik]; j < Irowptr[Ik+1]; ++j) { + LO Ikj = Icolind[j]; + LO Cij = Icol2Ccol[Ikj]; + + TEUCHOS_TEST_FOR_EXCEPTION(c_status[Cij] < OLD_ip || c_status[Cij] >= CSR_ip, + std::runtime_error, "Trying to insert a new entry into a static graph"); + + Cvals[c_status[Cij]] += minusOmegaDval * Aval * Ivals[j]; + } + } + } + } + +#ifdef HAVE_TPETRA_MMM_TIMINGS + MM2= Teuchos::null; + MM = Teuchos::null; MM = rcp(new TimeMonitor (*TimeMonitor::getNewTimer(prefix_mmm + std::string("Jacobi Reuse ESFC")))); +#endif + + C.fillComplete(C.getDomainMap(), C.getRangeMap()); + +} + +/*********************************************************************************************************/ +template +void KernelWrappers2::jacobi_A_B_newmatrix_KokkosKernels(Scalar omega, + const Vector & Dinv, + CrsMatrixStruct& Aview, + CrsMatrixStruct& Bview, + const LocalOrdinalViewType & Acol2Brow, + const LocalOrdinalViewType & Acol2Irow, + const LocalOrdinalViewType & Bcol2Ccol, + const LocalOrdinalViewType & Icol2Ccol, + CrsMatrix& C, + Teuchos::RCP > Cimport, + const std::string& label, + const Teuchos::RCP& params) { + +#ifdef HAVE_TPETRA_MMM_TIMINGS + std::string prefix_mmm = std::string("TpetraExt ") + label + std::string(": "); + using Teuchos::TimeMonitor; + Teuchos::RCP MM; +#endif + + // Check if the diagonal entries exist in debug mode + const bool debug = Tpetra::Details::Behavior::debug(); + if(debug) { + + auto rowMap = Aview.origMatrix->getRowMap(); + Tpetra::Vector diags(rowMap); + Aview.origMatrix->getLocalDiagCopy(diags); + size_t diagLength = rowMap->getNodeNumElements(); + Teuchos::Array diagonal(diagLength); + diags.get1dCopy(diagonal()); + + for(size_t i = 0; i < diagLength; ++i) { + TEUCHOS_TEST_FOR_EXCEPTION(diagonal[i] == Teuchos::ScalarTraits::zero(), + std::runtime_error, + "Matrix A has a zero/missing diagonal: " << diagonal[i] << std::endl << + "KokkosKernels Jacobi-fused SpGEMM requires nonzero diagonal entries in A" << std::endl); + } + } + + // Usings + using device_t = typename Kokkos::Compat::KokkosSYCLWrapperNode::device_type; + using matrix_t = typename Tpetra::CrsMatrix::local_matrix_device_type; + using graph_t = typename matrix_t::StaticCrsGraphType; + using lno_view_t = typename graph_t::row_map_type::non_const_type; + using c_lno_view_t = typename graph_t::row_map_type::const_type; + using lno_nnz_view_t = typename graph_t::entries_type::non_const_type; + using scalar_view_t = typename matrix_t::values_type::non_const_type; + + // KokkosKernels handle + using handle_t = typename KokkosKernels::Experimental::KokkosKernelsHandle< + typename lno_view_t::const_value_type,typename lno_nnz_view_t::const_value_type, typename scalar_view_t::const_value_type, + typename device_t::execution_space, typename device_t::memory_space,typename device_t::memory_space >; + + // Get the rowPtr, colInd and vals of importMatrix + c_lno_view_t Irowptr; + lno_nnz_view_t Icolind; + scalar_view_t Ivals; + if(!Bview.importMatrix.is_null()) { + auto lclB = Bview.importMatrix->getLocalMatrixDevice(); + Irowptr = lclB.graph.row_map; + Icolind = lclB.graph.entries; + Ivals = lclB.values; + } + + // Merge the B and Bimport matrices + const matrix_t Bmerged = Tpetra::MMdetails::merge_matrices(Aview,Bview,Acol2Brow,Acol2Irow,Bcol2Ccol,Icol2Ccol,C.getColMap()->getNodeNumElements()); + + // Get the properties and arrays of input matrices + const matrix_t & Amat = Aview.origMatrix->getLocalMatrixDevice(); + const matrix_t & Bmat = Bview.origMatrix->getLocalMatrixDevice(); + + typename handle_t::nnz_lno_t AnumRows = Amat.numRows(); + typename handle_t::nnz_lno_t BnumRows = Bmerged.numRows(); + typename handle_t::nnz_lno_t BnumCols = Bmerged.numCols(); + + c_lno_view_t Arowptr = Amat.graph.row_map, Browptr = Bmerged.graph.row_map; + const lno_nnz_view_t Acolind = Amat.graph.entries, Bcolind = Bmerged.graph.entries; + const scalar_view_t Avals = Amat.values, Bvals = Bmerged.values; + + // Arrays of the output matrix + lno_view_t row_mapC (Kokkos::ViewAllocateWithoutInitializing("non_const_lnow_row"), AnumRows + 1); + lno_nnz_view_t entriesC; + scalar_view_t valuesC; + + // Options + int team_work_size = 16; + std::string myalg("SPGEMM_KK_MEMORY"); + if(!params.is_null()) { + if(params->isParameter("sycl: algorithm")) + myalg = params->get("sycl: algorithm",myalg); + if(params->isParameter("sycl: team work size")) + team_work_size = params->get("sycl: team work size",team_work_size); + } + + // Get the algorithm mode + std::string nodename("SYCL"); + std::string alg = nodename + std::string(" algorithm"); + if(!params.is_null() && params->isParameter(alg)) myalg = params->get(alg,myalg); + KokkosSparse::SPGEMMAlgorithm alg_enum = KokkosSparse::StringToSPGEMMAlgorithm(myalg); + + + // KokkosKernels call + handle_t kh; + kh.create_spgemm_handle(alg_enum); + kh.set_team_work_size(team_work_size); + + KokkosSparse::Experimental::spgemm_symbolic(&kh, AnumRows, BnumRows, BnumCols, + Arowptr, Acolind, false, + Browptr, Bcolind, false, + row_mapC); + + size_t c_nnz_size = kh.get_spgemm_handle()->get_c_nnz(); + if (c_nnz_size){ + entriesC = lno_nnz_view_t (Kokkos::ViewAllocateWithoutInitializing("entriesC"), c_nnz_size); + valuesC = scalar_view_t (Kokkos::ViewAllocateWithoutInitializing("valuesC"), c_nnz_size); + } + + KokkosSparse::Experimental::spgemm_jacobi(&kh, AnumRows, BnumRows, BnumCols, + Arowptr, Acolind, Avals, false, + Browptr, Bcolind, Bvals, false, + row_mapC, entriesC, valuesC, + omega, Dinv.getLocalViewDevice(Access::ReadOnly)); + kh.destroy_spgemm_handle(); + +#ifdef HAVE_TPETRA_MMM_TIMINGS + MM = Teuchos::null; MM = rcp(new TimeMonitor (*TimeMonitor::getNewTimer(prefix_mmm + std::string("Jacobi Newmatrix SYCLSort")))); +#endif + + // Sort & set values + if (params.is_null() || params->get("sort entries",true)) + Import_Util::sortCrsEntries(row_mapC, entriesC, valuesC); + C.setAllValues(row_mapC,entriesC,valuesC); + +#ifdef HAVE_TPETRA_MMM_TIMINGS + MM = Teuchos::null; MM = rcp(new TimeMonitor (*TimeMonitor::getNewTimer(prefix_mmm + std::string("Jacobi Newmatrix SYCLESFC")))); +#endif + + // Final Fillcomplete + Teuchos::RCP labelList = rcp(new Teuchos::ParameterList); + labelList->set("Timer Label",label); + if(!params.is_null()) labelList->set("compute global constants",params->get("compute global constants",true)); + Teuchos::RCP > dummyExport; + C.expertStaticFillComplete(Bview.origMatrix->getDomainMap(), Aview.origMatrix->getRangeMap(), Cimport,dummyExport,labelList); +} + + }//MMdetails +}//Tpetra + +#endif//SYCL + +#endif diff --git a/packages/tpetra/core/ext/TpetraExt_MatrixMatrix_def.hpp b/packages/tpetra/core/ext/TpetraExt_MatrixMatrix_def.hpp index a11686002906..feedd3504b42 100644 --- a/packages/tpetra/core/ext/TpetraExt_MatrixMatrix_def.hpp +++ b/packages/tpetra/core/ext/TpetraExt_MatrixMatrix_def.hpp @@ -82,6 +82,7 @@ #include "TpetraExt_MatrixMatrix_OpenMP.hpp" #include "TpetraExt_MatrixMatrix_Cuda.hpp" #include "TpetraExt_MatrixMatrix_HIP.hpp" +#include "TpetraExt_MatrixMatrix_SYCL.hpp" namespace Tpetra { diff --git a/packages/tpetra/core/ext/TpetraExt_TripleMatrixMultiply_def.hpp b/packages/tpetra/core/ext/TpetraExt_TripleMatrixMultiply_def.hpp index 2a2c082d739f..5df1da47daed 100644 --- a/packages/tpetra/core/ext/TpetraExt_TripleMatrixMultiply_def.hpp +++ b/packages/tpetra/core/ext/TpetraExt_TripleMatrixMultiply_def.hpp @@ -74,6 +74,7 @@ #include "TpetraExt_MatrixMatrix_OpenMP.hpp" #include "TpetraExt_MatrixMatrix_Cuda.hpp" #include "TpetraExt_MatrixMatrix_HIP.hpp" +#include "TpetraExt_MatrixMatrix_SYCL.hpp" namespace Tpetra { diff --git a/packages/tpetra/core/src/Tpetra_MultiVector_def.hpp b/packages/tpetra/core/src/Tpetra_MultiVector_def.hpp index deb9a3d2617b..f13ca22e662d 100644 --- a/packages/tpetra/core/src/Tpetra_MultiVector_def.hpp +++ b/packages/tpetra/core/src/Tpetra_MultiVector_def.hpp @@ -322,7 +322,9 @@ namespace { // (anonymous) { // FIXME (mfh 15 Mar 2019) DualView doesn't have a stride // method yet, but its Views do. - size_t strides[WrappedOrNotDualViewType::t_dev::Rank]; + // NOTE: dv.stride() returns a vector of length one + // more than its rank + size_t strides[WrappedOrNotDualViewType::t_dev::Rank+1]; dv.stride(strides); const size_t LDA = strides[1]; const size_t numRows = dv.extent (0); diff --git a/packages/tpetra/core/test/ImportExport2/ImportExport2_UnitTests.cpp b/packages/tpetra/core/test/ImportExport2/ImportExport2_UnitTests.cpp index 95ec17d71174..17fbbc4e1f89 100644 --- a/packages/tpetra/core/test/ImportExport2/ImportExport2_UnitTests.cpp +++ b/packages/tpetra/core/test/ImportExport2/ImportExport2_UnitTests.cpp @@ -3035,12 +3035,24 @@ TEUCHOS_UNIT_TEST_TEMPLATE_2_DECL( Import_Util,GetTwoTransferOwnershipVector, LO TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( Import_Util, GetPids, LO, GO ) \ TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( Import_Util, GetTwoTransferOwnershipVector, LO, GO ) -#define UNIT_TEST_GROUP_SC_LO_GO( SC, LO, GO ) \ +// These are the tests associated to UNIT_TEST_GROUP_SC_LO_GO that work for all backends. +#define UNIT_TEST_GROUP_SC_LO_GO_COMMON( SC, LO, GO ) \ TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT( CrsMatrixImportExport, doImport, LO, GO, SC ) \ TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT( MultiVectorImport, doImport, LO, GO, SC ) \ TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT( FusedImportExport, doImport, LO, GO, SC ) \ - TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT( Import_Util, UnpackAndCombineWithOwningPIDs, LO, GO, SC ) \ - TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT( FusedImportExport, MueLuStyle, LO, GO, SC ) + TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT( Import_Util, UnpackAndCombineWithOwningPIDs, LO, GO, SC ) + +// FIXME_SYCL requires querying free device memory in KokkosKernels, see +// https://github.com/kokkos/kokkos-kernels/issues/1062. +// The SYCL specifications don't allow asking for that. +#ifdef HAVE_TPETRA_SYCL + #define UNIT_TEST_GROUP_SC_LO_GO( SC, LO, GO ) \ + UNIT_TEST_GROUP_SC_LO_GO_COMMON( SC, LO, GO ) +#else + #define UNIT_TEST_GROUP_SC_LO_GO( SC, LO, GO ) \ + UNIT_TEST_GROUP_SC_LO_GO_COMMON( SC, LO, GO ) \ + TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT( FusedImportExport, MueLuStyle, LO, GO, SC ) +#endif // Note: This test fails. Should fix later. // TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( ReverseImportExport, doImport, ORDINAL, SCALAR ) diff --git a/packages/tpetra/core/test/MatrixMatrix/MatrixMatrix_UnitTests.cpp b/packages/tpetra/core/test/MatrixMatrix/MatrixMatrix_UnitTests.cpp index 0256eafe3f78..63b13fd6a208 100644 --- a/packages/tpetra/core/test/MatrixMatrix/MatrixMatrix_UnitTests.cpp +++ b/packages/tpetra/core/test/MatrixMatrix/MatrixMatrix_UnitTests.cpp @@ -2209,8 +2209,8 @@ TEUCHOS_UNIT_TEST_TEMPLATE_4_DECL(Tpetra_MatMatAdd, locally_unsorted, SC, LO, GO }*/ -#define UNIT_TEST_GROUP_SC_LO_GO_NO( SC, LO, GO, NT ) \ - TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(Tpetra_MatMat, operations_test,SC, LO, GO, NT) \ +// These are the tests associated to UNIT_TEST_GROUP_SC_LO_GO_NO that work for all backends. +#define UNIT_TEST_GROUP_SC_LO_GO_NO_COMMON( SC, LO, GO, NT ) \ TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(Tpetra_MatMat, range_row_test, SC, LO, GO, NT) \ TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(Tpetra_MatMat, ATI_range_row_test, SC, LO, GO, NT) \ TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(Tpetra_MatMat, threaded_add_sorted, SC, LO, GO, NT) \ @@ -2222,6 +2222,18 @@ TEUCHOS_UNIT_TEST_TEMPLATE_4_DECL(Tpetra_MatMatAdd, locally_unsorted, SC, LO, GO TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(Tpetra_MatMatAdd, different_col_maps, SC, LO, GO, NT) \ TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(Tpetra_MatMatAdd, different_index_base, SC, LO, GO, NT) +// FIXME_SYCL requires querying free device memory in KokkosKernels, see +// https://github.com/kokkos/kokkos-kernels/issues/1062. +// The SYCL specifications don't allow asking for that. +#ifdef HAVE_TPETRA_SYCL + #define UNIT_TEST_GROUP_SC_LO_GO_NO( SC, LO, GO, NT ) \ + UNIT_TEST_GROUP_SC_LO_GO_NO_COMMON( SC, LO, GO, NT ) +#else + #define UNIT_TEST_GROUP_SC_LO_GO_NO( SC, LO, GO, NT ) \ + TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(Tpetra_MatMat, operations_test,SC, LO, GO, NT) \ + UNIT_TEST_GROUP_SC_LO_GO_NO_COMMON( SC, LO, GO, NT ) +#endif + TPETRA_ETI_MANGLING_TYPEDEFS() TPETRA_INSTANTIATE_SLGN_NO_ORDINAL_SCALAR( UNIT_TEST_GROUP_SC_LO_GO_NO ) diff --git a/packages/trilinoscouplings/examples/scaling/CMakeLists.txt b/packages/trilinoscouplings/examples/scaling/CMakeLists.txt index 0145d4f9df78..d579fca201ad 100644 --- a/packages/trilinoscouplings/examples/scaling/CMakeLists.txt +++ b/packages/trilinoscouplings/examples/scaling/CMakeLists.txt @@ -172,6 +172,7 @@ IF(${PACKAGE_NAME}_ENABLE_Epetra AND ${PACKAGE_NAME}_ENABLE_EpetraExt AND Example_Poisson2D_pn_tpetra SOURCES example_Poisson2D_pn_tpetra.cpp TrilinosCouplings_Pamgen_Utils.cpp + TrilinosCouplings_IntrepidPoissonExampleHelpers.cpp COMM mpi ) diff --git a/packages/trilinoscouplings/examples/scaling/TrilinosCouplings_IntrepidPoissonExampleHelpers.cpp b/packages/trilinoscouplings/examples/scaling/TrilinosCouplings_IntrepidPoissonExampleHelpers.cpp index 7cec51bec895..dcab796e5b84 100644 --- a/packages/trilinoscouplings/examples/scaling/TrilinosCouplings_IntrepidPoissonExampleHelpers.cpp +++ b/packages/trilinoscouplings/examples/scaling/TrilinosCouplings_IntrepidPoissonExampleHelpers.cpp @@ -113,6 +113,8 @@ double materialTensorOffDiagonalValue_; Matrix3 rotation_, strength_,diff_total_; +std::vector matrix2D_; + bool useDiffusionMatrix() { return use_diffusion_; } @@ -136,6 +138,20 @@ const std::vector& getDiffusionMatrix() { +const std::vector& getDiffusionMatrix2D() { + // Gets the x/y sub-matrix + if(!use_diffusion_) + throw std::runtime_error("setDiffusionRotationStrength has not been called"); + matrix2D_.resize(4); + matrix2D_[0] = diff_total_(0,0); + matrix2D_[1] = diff_total_(0,1); + matrix2D_[2] = diff_total_(1,0); + matrix2D_[3] = diff_total_(1,1); + + return matrix2D_; +} + + void setDiffusionRotationAndStrength(const std::vector& theta, const std::vector& diagonal) { rotation_ = Z_Rotation(theta[2]*M_PI/180.0) * Y_Rotation(theta[1]*M_PI/180.0) * X_Rotation(theta[0]*M_PI/180.0); diff --git a/packages/trilinoscouplings/examples/scaling/TrilinosCouplings_IntrepidPoissonExampleHelpers.hpp b/packages/trilinoscouplings/examples/scaling/TrilinosCouplings_IntrepidPoissonExampleHelpers.hpp index 35f7e680cab8..5daf4cda0bd5 100644 --- a/packages/trilinoscouplings/examples/scaling/TrilinosCouplings_IntrepidPoissonExampleHelpers.hpp +++ b/packages/trilinoscouplings/examples/scaling/TrilinosCouplings_IntrepidPoissonExampleHelpers.hpp @@ -110,6 +110,11 @@ void setDiffusionRotationAndStrength(const std::vector& diff_rotation_an /// \brief Gets the diffusion Tensor const std::vector & getDiffusionMatrix(); + +/// \brief Gets the 2D diffusion Tensor (for 2D problems) +const std::vector & getDiffusionMatrix2D(); + + /// \brief Use the diffusion tensor rather than the off-diagonal values bool useDiffusionMatrix(); diff --git a/packages/trilinoscouplings/examples/scaling/example_Poisson2D_pn_tpetra.cpp b/packages/trilinoscouplings/examples/scaling/example_Poisson2D_pn_tpetra.cpp index 3eefdc36f7ca..caea27002639 100644 --- a/packages/trilinoscouplings/examples/scaling/example_Poisson2D_pn_tpetra.cpp +++ b/packages/trilinoscouplings/examples/scaling/example_Poisson2D_pn_tpetra.cpp @@ -87,6 +87,8 @@ // TrilinosCouplings includes #include "TrilinosCouplings_config.h" #include "TrilinosCouplings_Pamgen_Utils.hpp" +#include "TrilinosCouplings_IntrepidPoissonExampleHelpers.hpp" + // Intrepid includes #include "Intrepid_FunctionSpaceTools.hpp" @@ -469,6 +471,25 @@ int main(int argc, char *argv[]) { clp.setOption ("seed", &randomSeed, "Random Seed."); + // Material diffusion strength and rotation (in 3D) + // We'll get the 2D version of this later + std::vector diff_rotation_angle {0.0, 0.0, 0.0}; + std::vector diff_strength {1.0, 1.0, 1.0}; + for(int i=0; i<3; i++) { + char letter[4] = "xyz"; + char str1[80], str2[80]; + // Rotation + sprintf(str1,"rot_%c_angle",letter[i]); + sprintf(str2,"Rotation around %c axis, in degrees",letter[i]); + clp.setOption(str1,&diff_rotation_angle[i],str2); + + // Strength + sprintf(str1,"strength_%c",letter[i]); + sprintf(str2,"Strength of pre-rotation %c-diffusion",letter[i]); + clp.setOption(str1,&diff_strength[i],str2); + } + + switch (clp.parse(argc, argv)) { case Teuchos::CommandLineProcessor::PARSE_HELP_PRINTED: return EXIT_SUCCESS; case Teuchos::CommandLineProcessor::PARSE_ERROR: @@ -499,6 +520,15 @@ int main(int argc, char *argv[]) { } + // Diffusion Tensor + ::TrilinosCouplings::IntrepidPoissonExample::setDiffusionRotationAndStrength(diff_rotation_angle, diff_strength); + if(MyPID == 0) { + const std::vector & A = ::TrilinosCouplings::IntrepidPoissonExample::getDiffusionMatrix2D(); + std::cout<<"[ "< diffusion_tensor; + template void materialTensor(Scalar material[][2], const Scalar& x, const Scalar& y) { - - material[0][0] = 1.; - material[0][1] = 0.; + if(diffusion_tensor.size() == 0) + diffusion_tensor = ::TrilinosCouplings::IntrepidPoissonExample::getDiffusionMatrix2D(); + material[0][0] = (Scalar)diffusion_tensor[0]; + material[0][1] = (Scalar)diffusion_tensor[1]; // - material[1][0] = 0.; - material[1][1] = 1.; + material[1][0] = (Scalar)diffusion_tensor[2]; + material[1][1] = (Scalar)diffusion_tensor[3]; } /**********************************************************************************/ @@ -2072,9 +2106,10 @@ int TestMultiLevelPreconditionerLaplace(char ProblemType[], ParameterList belosList; belosList.set("Maximum Iterations", maxIts); // Maximum number of iterations allowed belosList.set("Convergence Tolerance", tol); // Relative convergence tolerance requested - belosList.set("Verbosity", Belos::Errors + Belos::Warnings + Belos::StatusTestDetails); + // belosList.set("Verbosity", Belos::Errors + Belos::Warnings + Belos::StatusTestDetails); belosList.set("Output Frequency", 1); - belosList.set("Output Style", Belos::Brief); + belosList.set("Output Style", 1); + belosList.set ("Verbosity", 33); bool scaleResidualHist = true; if (!scaleResidualHist) belosList.set("Implicit Residual Scaling", "None"); diff --git a/packages/zoltan/config/config.guess b/packages/zoltan/config/config.guess index 49ba16f15c66..e81d3ae7c210 100755 --- a/packages/zoltan/config/config.guess +++ b/packages/zoltan/config/config.guess @@ -1,14 +1,14 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012 Free Software Foundation, Inc. +# Copyright 1992-2021 Free Software Foundation, Inc. -timestamp='2012-01-01' +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2021-06-03' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or +# the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but @@ -17,26 +17,30 @@ timestamp='2012-01-01' # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA -# 02110-1301, USA. +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - - -# Originally written by Per Bothner. Please send patches (context -# diff format) to and include a ChangeLog -# entry. +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). # -# This script attempts to guess a canonical system name similar to -# config.sub. If it succeeds, it prints the system name on stdout, and -# exits with 0. Otherwise, it exits with 1. +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess +# +# Please send patches to . + + +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + me=`echo "$0" | sed -e 's,.*/,,'` @@ -45,7 +49,7 @@ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -56,9 +60,7 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, -2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 -Free Software Foundation, Inc. +Copyright 1992-2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -92,7 +94,8 @@ if test $# != 0; then exit 1 fi -trap 'exit 1' 1 2 15 +# Just in case it came from the environment. +GUESS= # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires @@ -104,45 +107,90 @@ trap 'exit 1' 1 2 15 # Portable tmp directory creation inspired by the Autoconf team. -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; - for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 + +set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039,SC3028 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c89 c99 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD=$driver + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then +if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown +case $UNAME_SYSTEM in +Linux|GNU|GNU/*) + LIBC=unknown + + set_cc_for_build + cat <<-EOF > "$dummy.c" + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #elif defined(__GLIBC__) + LIBC=gnu + #else + #include + /* First heuristic to detect musl libc. */ + #ifdef __DEFINED_va_list + LIBC=musl + #endif + #endif + EOF + cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + eval "$cc_set_libc" + + # Second heuristic to detect musl libc. + if [ "$LIBC" = unknown ] && + command -v ldd >/dev/null && + ldd --version 2>&1 | grep -q ^musl; then + LIBC=musl + fi + + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + if [ "$LIBC" = unknown ]; then + LIBC=gnu + fi + ;; +esac + # Note: order is significant - the case branches are not exclusive. -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in +case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, @@ -154,22 +202,32 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || echo unknown)` - case "${UNAME_MACHINE_ARCH}" in + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + echo unknown)` + case $UNAME_MACHINE_ARCH in + aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + earmv*) + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` + machine=${arch}${endian}-unknown + ;; + *) machine=$UNAME_MACHINE_ARCH-unknown ;; esac # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. - case "${UNAME_MACHINE_ARCH}" in + # to ELF recently (or will in the future) and ABI. + case $UNAME_MACHINE_ARCH in + earm*) + os=netbsdelf + ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build + set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then @@ -184,41 +242,80 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in os=netbsd ;; esac + # Determine ABI tags. + case $UNAME_MACHINE_ARCH in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` + ;; + esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in + case $UNAME_VERSION in Debian*) release='-gnu' ;; *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" - exit ;; + GUESS=$machine-${os}${release}${abi-} + ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE + ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE + ;; + *:SecBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE + ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE + ;; + *:MidnightBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE + ;; *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE + ;; *:SolidBSD:*:*) - echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE + ;; + *:OS108:*:*) + GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE + ;; macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd${UNAME_RELEASE} - exit ;; + GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE + ;; *:MirBSD:*:*) - echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE + ;; + *:Sortix:*:*) + GUESS=$UNAME_MACHINE-unknown-sortix + ;; + *:Twizzler:*:*) + GUESS=$UNAME_MACHINE-unknown-twizzler + ;; + *:Redox:*:*) + GUESS=$UNAME_MACHINE-unknown-redox + ;; + mips:OSF1:*.*) + GUESS=mips-dec-osf1 + ;; alpha:OSF1:*:*) + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + trap '' 0 case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` @@ -232,163 +329,158 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in + case $ALPHA_CPU_TYPE in "EV4 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; + UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; + UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; + UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; + UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; + UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; + UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; + UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; + UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; + UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - # Reset EXIT trap before exiting to avoid spurious non-zero exit code. - exitcode=$? - trap '' 0 - exit $exitcode ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit ;; + OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + GUESS=$UNAME_MACHINE-dec-osf$OSF_REL + ;; Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit ;; + GUESS=m68k-unknown-sysv4 + ;; *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos - exit ;; + GUESS=$UNAME_MACHINE-unknown-amigaos + ;; *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos - exit ;; + GUESS=$UNAME_MACHINE-unknown-morphos + ;; *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; + GUESS=i370-ibm-openedition + ;; *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; + GUESS=s390-ibm-zvmoe + ;; *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; + GUESS=powerpc-ibm-os400 + ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} - exit ;; - arm:riscos:*:*|arm:RISCOS:*:*) - echo arm-unknown-riscos - exit ;; + GUESS=arm-acorn-riscix$UNAME_RELEASE + ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + GUESS=arm-unknown-riscos + ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; + GUESS=hppa1.1-hitachi-hiuxmpp + ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; + case `(/bin/universe) 2>/dev/null` in + att) GUESS=pyramid-pyramid-sysv3 ;; + *) GUESS=pyramid-pyramid-bsd ;; + esac + ;; NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; + GUESS=pyramid-pyramid-svr4 + ;; DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; + GUESS=sparc-icl-nx6 + ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; + sparc) GUESS=sparc-icl-nx7 ;; + esac + ;; s390x:SunOS:*:*) - echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL + ;; sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-hal-solaris2$SUN_REL + ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris2$SUN_REL + ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux${UNAME_RELEASE} - exit ;; + GUESS=i386-pc-auroraux$UNAME_RELEASE + ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval $set_cc_for_build - SUN_ARCH="i386" + set_cc_for_build + SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then - SUN_ARCH="x86_64" + SUN_ARCH=x86_64 fi fi - echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$SUN_ARCH-pc-solaris2$SUN_REL + ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris3$SUN_REL + ;; sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in + case `/usr/bin/arch -k` in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` + GUESS=sparc-sun-sunos$SUN_REL + ;; sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} - exit ;; + GUESS=m68k-sun-sunos$UNAME_RELEASE + ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 - case "`/bin/arch`" in + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 + case `/bin/arch` in sun3) - echo m68k-sun-sunos${UNAME_RELEASE} + GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun4) - echo sparc-sun-sunos${UNAME_RELEASE} + GUESS=sparc-sun-sunos$UNAME_RELEASE ;; esac - exit ;; + ;; aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} - exit ;; + GUESS=sparc-auspex-sunos$UNAME_RELEASE + ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor @@ -398,44 +490,44 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-milan-mint$UNAME_RELEASE + ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-hades-mint$UNAME_RELEASE + ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-unknown-mint$UNAME_RELEASE + ;; m68k:machten:*:*) - echo m68k-apple-machten${UNAME_RELEASE} - exit ;; + GUESS=m68k-apple-machten$UNAME_RELEASE + ;; powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} - exit ;; + GUESS=powerpc-apple-machten$UNAME_RELEASE + ;; RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; + GUESS=mips-dec-mach_bsd4.3 + ;; RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} - exit ;; + GUESS=mips-dec-ultrix$UNAME_RELEASE + ;; VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} - exit ;; + GUESS=vax-dec-ultrix$UNAME_RELEASE + ;; 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} - exit ;; + GUESS=clipper-intergraph-clix$UNAME_RELEASE + ;; mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { @@ -444,95 +536,96 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && - dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`$dummy $dummyarg` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos${UNAME_RELEASE} - exit ;; + GUESS=mips-mips-riscos$UNAME_RELEASE + ;; Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; + GUESS=powerpc-motorola-powermax + ;; Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; + GUESS=powerpc-harris-powermax + ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; + GUESS=powerpc-harris-powermax + ;; Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; + GUESS=powerpc-harris-powerunix + ;; m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; + GUESS=m88k-harris-cxux7 + ;; m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; + GUESS=m88k-motorola-sysv4 + ;; m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; + GUESS=m88k-motorola-sysv3 + ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] + if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ + test "$TARGET_BINARY_INTERFACE"x = x then - echo m88k-dg-dgux${UNAME_RELEASE} + GUESS=m88k-dg-dgux$UNAME_RELEASE else - echo m88k-dg-dguxbcs${UNAME_RELEASE} + GUESS=m88k-dg-dguxbcs$UNAME_RELEASE fi else - echo i586-dg-dgux${UNAME_RELEASE} + GUESS=i586-dg-dgux$UNAME_RELEASE fi - exit ;; + ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; + GUESS=m88k-dolphin-sysv3 + ;; M88*:*:R3*:*) # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; + GUESS=m88k-motorola-sysv3 + ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; + GUESS=m88k-tektronix-sysv3 + ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; + GUESS=m68k-tektronix-bsd + ;; *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` - exit ;; + IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` + GUESS=mips-sgi-irix$IRIX_REL + ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id + ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; + GUESS=i386-ibm-aix + ;; ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then + if test -x /usr/bin/oslevel ; then IBM_REV=`/usr/bin/oslevel` else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} - exit ;; + GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV + ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #include main() @@ -543,76 +636,77 @@ EOF exit(0); } EOF - if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then - echo "$SYSTEM_NAME" + GUESS=$SYSTEM_NAME else - echo rs6000-ibm-aix3.2.5 + GUESS=rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 + GUESS=rs6000-ibm-aix3.2.4 else - echo rs6000-ibm-aix3.2 + GUESS=rs6000-ibm-aix3.2 fi - exit ;; + ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` + if test -x /usr/bin/lslpp ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} - exit ;; + GUESS=$IBM_ARCH-ibm-aix$IBM_REV + ;; *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) - echo romp-ibm-bsd4.4 - exit ;; + GUESS=rs6000-ibm-aix + ;; + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) + GUESS=romp-ibm-bsd4.4 + ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 + GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to + ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; + GUESS=rs6000-bull-bosx + ;; DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; + GUESS=m68k-bull-sysv3 + ;; 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; + GUESS=m68k-hp-bsd + ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; + GUESS=m68k-hp-bsd4.4 + ;; 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + case $UNAME_MACHINE in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then + if test -x /usr/bin/getconf; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + case $sc_cpu_version in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + case $sc_kernel_bits in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + if test "$HP_ARCH" = ""; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include @@ -645,13 +739,13 @@ EOF exit (0); } EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if [ ${HP_ARCH} = "hppa2.0w" ] + if test "$HP_ARCH" = hppa2.0w then - eval $set_cc_for_build + set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler @@ -662,23 +756,23 @@ EOF # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then - HP_ARCH="hppa2.0w" + HP_ARCH=hppa2.0w else - HP_ARCH="hppa64" + HP_ARCH=hppa64 fi fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} - exit ;; + GUESS=$HP_ARCH-hp-hpux$HPUX_REV + ;; ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} - exit ;; + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + GUESS=ia64-hp-hpux$HPUX_REV + ;; 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #include int main () @@ -703,38 +797,38 @@ EOF exit (0); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) - echo hppa1.1-hp-bsd - exit ;; + GUESS=unknown-hitachi-hiuxwe2 + ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) + GUESS=hppa1.1-hp-bsd + ;; 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; + GUESS=hppa1.0-hp-bsd + ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) - echo hppa1.1-hp-osf - exit ;; + GUESS=hppa1.0-hp-mpeix + ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) + GUESS=hppa1.1-hp-osf + ;; hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; + GUESS=hppa1.0-hp-osf + ;; i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk + if test -x /usr/sbin/sysversion ; then + GUESS=$UNAME_MACHINE-unknown-osf1mk else - echo ${UNAME_MACHINE}-unknown-osf1 + GUESS=$UNAME_MACHINE-unknown-osf1 fi - exit ;; + ;; parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; + GUESS=hppa1.1-hp-lites + ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; + GUESS=c1-convex-bsd + ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd @@ -742,129 +836,145 @@ EOF fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; + GUESS=c34-convex-bsd + ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; + GUESS=c38-convex-bsd + ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; + GUESS=c4-convex-bsd + ;; CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=ymp-cray-unicos$CRAY_REL + ;; CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=t90-cray-unicos$CRAY_REL + ;; CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=alphaev5-cray-unicosmk$CRAY_REL + ;; CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=sv1-cray-unicos$CRAY_REL + ;; *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=craynv-cray-unicosmp$CRAY_REL + ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` + GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE + ;; sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} - exit ;; + GUESS=sparc-unknown-bsdi$UNAME_RELEASE + ;; *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE + ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi + else + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf + fi + ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` - case ${UNAME_PROCESSOR} in + case $UNAME_PROCESSOR in amd64) - echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - *) - echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; esac - exit ;; + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL + ;; i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin - exit ;; + GUESS=$UNAME_MACHINE-pc-cygwin + ;; + *:MINGW64*:*) + GUESS=$UNAME_MACHINE-pc-mingw64 + ;; *:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 - exit ;; - i*:MSYS*:*) - echo ${UNAME_MACHINE}-pc-msys - exit ;; - i*:windows32*:*) - # uname -m includes "-pc" on this system. - echo ${UNAME_MACHINE}-mingw32 - exit ;; + GUESS=$UNAME_MACHINE-pc-mingw32 + ;; + *:MSYS*:*) + GUESS=$UNAME_MACHINE-pc-msys + ;; i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 - exit ;; + GUESS=$UNAME_MACHINE-pc-pw32 + ;; *:Interix*:*) - case ${UNAME_MACHINE} in + case $UNAME_MACHINE in x86) - echo i586-pc-interix${UNAME_RELEASE} - exit ;; + GUESS=i586-pc-interix$UNAME_RELEASE + ;; authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix${UNAME_RELEASE} - exit ;; + GUESS=x86_64-unknown-interix$UNAME_RELEASE + ;; IA64) - echo ia64-unknown-interix${UNAME_RELEASE} - exit ;; + GUESS=ia64-unknown-interix$UNAME_RELEASE + ;; esac ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit ;; - 8664:Windows_NT:*) - echo x86_64-pc-mks - exit ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit ;; i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin - exit ;; + GUESS=$UNAME_MACHINE-pc-uwin + ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-unknown-cygwin - exit ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit ;; + GUESS=x86_64-pc-cygwin + ;; prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=powerpcle-unknown-solaris2$SUN_REL + ;; *:GNU:*:*) # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` - exit ;; + GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` + GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL + ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu - exit ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix - exit ;; + GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC + ;; + *:Minix:*:*) + GUESS=$UNAME_MACHINE-unknown-minix + ;; + aarch64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; @@ -874,168 +984,226 @@ EOF EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} - exit ;; + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; arm*:Linux:*:*) - eval $set_cc_for_build + set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then - echo ${UNAME_MACHINE}-unknown-linux-gnu + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then - echo ${UNAME_MACHINE}-unknown-linux-gnueabi + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi else - echo ${UNAME_MACHINE}-unknown-linux-gnueabihf + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf fi fi - exit ;; + ;; avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; cris:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; crisv32:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; + e2k:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; frv:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; hexagon:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; i*86:Linux:*:*) - LIBC=gnu - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #ifdef __dietlibc__ - LIBC=dietlibc - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` - echo "${UNAME_MACHINE}-pc-linux-${LIBC}" - exit ;; + GUESS=$UNAME_MACHINE-pc-linux-$LIBC + ;; ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + k1om:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; mips:Linux:*:* | mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + IS_GLIBC=0 + test x"${LIBC}" = xgnu && IS_GLIBC=1 + sed 's/^ //' << EOF > "$dummy.c" #undef CPU - #undef ${UNAME_MACHINE} - #undef ${UNAME_MACHINE}el + #undef mips + #undef mipsel + #undef mips64 + #undef mips64el + #if ${IS_GLIBC} && defined(_ABI64) + LIBCABI=gnuabi64 + #else + #if ${IS_GLIBC} && defined(_ABIN32) + LIBCABI=gnuabin32 + #else + LIBCABI=${LIBC} + #endif + #endif + + #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa64r6 + #else + #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa32r6 + #else + #if defined(__mips64) + CPU=mips64 + #else + CPU=mips + #endif + #endif + #endif + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=${UNAME_MACHINE}el + MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=${UNAME_MACHINE} + MIPS_ENDIAN= #else - CPU= + MIPS_ENDIAN= #endif #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` + eval "$cc_set_vars" + test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } + ;; + mips64el:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + openrisc*:Linux:*:*) + GUESS=or1k-unknown-linux-$LIBC + ;; + or32:Linux:*:* | or1k*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; - or32:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; padre:Linux:*:*) - echo sparc-unknown-linux-gnu - exit ;; + GUESS=sparc-unknown-linux-$LIBC + ;; parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu - exit ;; + GUESS=hppa64-unknown-linux-$LIBC + ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-gnu ;; - PA8*) echo hppa2.0-unknown-linux-gnu ;; - *) echo hppa-unknown-linux-gnu ;; + PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; + PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; + *) GUESS=hppa-unknown-linux-$LIBC ;; esac - exit ;; + ;; ppc64:Linux:*:*) - echo powerpc64-unknown-linux-gnu - exit ;; + GUESS=powerpc64-unknown-linux-$LIBC + ;; ppc:Linux:*:*) - echo powerpc-unknown-linux-gnu - exit ;; + GUESS=powerpc-unknown-linux-$LIBC + ;; + ppc64le:Linux:*:*) + GUESS=powerpc64le-unknown-linux-$LIBC + ;; + ppcle:Linux:*:*) + GUESS=powerpcle-unknown-linux-$LIBC + ;; + riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux - exit ;; + GUESS=$UNAME_MACHINE-ibm-linux-$LIBC + ;; sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; tile*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-dec-linux-$LIBC + ;; x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + set_cc_for_build + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_X32 >/dev/null + then + LIBCABI=${LIBC}x32 + fi + fi + GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI + ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; + GUESS=i386-sequent-sysv4 + ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} - exit ;; + GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION + ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx - exit ;; + GUESS=$UNAME_MACHINE-pc-os2-emx + ;; i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop - exit ;; + GUESS=$UNAME_MACHINE-unknown-stop + ;; i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos - exit ;; + GUESS=$UNAME_MACHINE-unknown-atheos + ;; i*86:syllable:*:*) - echo ${UNAME_MACHINE}-pc-syllable - exit ;; + GUESS=$UNAME_MACHINE-pc-syllable + ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=i386-unknown-lynxos$UNAME_RELEASE + ;; i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp - exit ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + GUESS=$UNAME_MACHINE-pc-msdosdjgpp + ;; + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL fi - exit ;; + ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in @@ -1043,12 +1211,12 @@ EOF *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} - exit ;; + GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 @@ -1058,43 +1226,43 @@ EOF && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL else - echo ${UNAME_MACHINE}-pc-sysv32 + GUESS=$UNAME_MACHINE-pc-sysv32 fi - exit ;; + ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configury will decide that + # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. - echo i586-pc-msdosdjgpp - exit ;; + GUESS=i586-pc-msdosdjgpp + ;; Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; + GUESS=i386-pc-mach3 + ;; paragon:*:*:*) - echo i860-intel-osf1 - exit ;; + GUESS=i860-intel-osf1 + ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 fi - exit ;; + ;; mini*:CTIX:SYS*5:*) # "miniframe" - echo m68010-convergent-sysv - exit ;; + GUESS=m68010-convergent-sysv + ;; mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; + GUESS=m68k-convergent-sysv + ;; M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; + GUESS=m68k-diab-dnix + ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) @@ -1102,9 +1270,9 @@ EOF test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; @@ -1113,223 +1281,281 @@ EOF test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=m68k-unknown-lynxos$UNAME_RELEASE + ;; mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; + GUESS=m68k-atari-sysv4 + ;; TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=sparc-unknown-lynxos$UNAME_RELEASE + ;; rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=rs6000-unknown-lynxos$UNAME_RELEASE + ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=powerpc-unknown-lynxos$UNAME_RELEASE + ;; SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} - exit ;; + GUESS=mips-dde-sysv$UNAME_RELEASE + ;; RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; + GUESS=mips-sni-sysv4 + ;; RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; + GUESS=mips-sni-sysv4 + ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 + GUESS=$UNAME_MACHINE-sni-sysv4 else - echo ns32k-sni-sysv + GUESS=ns32k-sni-sysv fi - exit ;; + ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says - echo i586-unisys-sysv4 - exit ;; + GUESS=i586-unisys-sysv4 + ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; + GUESS=hppa1.1-stratus-sysv4 + ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; + GUESS=i860-stratus-sysv4 + ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. - echo ${UNAME_MACHINE}-stratus-vos - exit ;; + GUESS=$UNAME_MACHINE-stratus-vos + ;; *:VOS:*:*) # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; + GUESS=hppa1.1-stratus-vos + ;; mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} - exit ;; + GUESS=m68k-apple-aux$UNAME_RELEASE + ;; news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; + GUESS=mips-sony-newsos6 + ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} + if test -d /usr/nec; then + GUESS=mips-nec-sysv$UNAME_RELEASE else - echo mips-unknown-sysv${UNAME_RELEASE} + GUESS=mips-unknown-sysv$UNAME_RELEASE fi - exit ;; + ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; + GUESS=powerpc-be-beos + ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; + GUESS=powerpc-apple-beos + ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; + GUESS=i586-pc-beos + ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. - echo i586-pc-haiku - exit ;; + GUESS=i586-pc-haiku + ;; + x86_64:Haiku:*:*) + GUESS=x86_64-unknown-haiku + ;; SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx4-nec-superux$UNAME_RELEASE + ;; SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx5-nec-superux$UNAME_RELEASE + ;; SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx6-nec-superux$UNAME_RELEASE + ;; SX-7:SUPER-UX:*:*) - echo sx7-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx7-nec-superux$UNAME_RELEASE + ;; SX-8:SUPER-UX:*:*) - echo sx8-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx8-nec-superux$UNAME_RELEASE + ;; SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx8r-nec-superux$UNAME_RELEASE + ;; + SX-ACE:SUPER-UX:*:*) + GUESS=sxace-nec-superux$UNAME_RELEASE + ;; Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} - exit ;; + GUESS=powerpc-apple-rhapsody$UNAME_RELEASE + ;; *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE + ;; + arm64:Darwin:*:*) + GUESS=aarch64-apple-darwin$UNAME_RELEASE + ;; *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in - i386) - eval $set_cc_for_build - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - UNAME_PROCESSOR="x86_64" - fi - fi ;; unknown) UNAME_PROCESSOR=powerpc ;; esac - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} - exit ;; + if command -v xcode-select > /dev/null 2> /dev/null && \ + ! xcode-select --print-path > /dev/null 2> /dev/null ; then + # Avoid executing cc if there is no toolchain installed as + # cc will be a stub that puts up a graphical alert + # prompting the user to install developer tools. + CC_FOR_BUILD=no_compiler_found + else + set_cc_for_build + fi + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # uname -m returns i386 or x86_64 + UNAME_PROCESSOR=$UNAME_MACHINE + fi + GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE + ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then + if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE + ;; *:QNX:*:4*) - echo i386-pc-qnx - exit ;; - NEO-?:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk${UNAME_RELEASE} - exit ;; - NSE-?:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk${UNAME_RELEASE} - exit ;; - NSR-?:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} - exit ;; + GUESS=i386-pc-qnx + ;; + NEO-*:NONSTOP_KERNEL:*:*) + GUESS=neo-tandem-nsk$UNAME_RELEASE + ;; + NSE-*:NONSTOP_KERNEL:*:*) + GUESS=nse-tandem-nsk$UNAME_RELEASE + ;; + NSR-*:NONSTOP_KERNEL:*:*) + GUESS=nsr-tandem-nsk$UNAME_RELEASE + ;; + NSV-*:NONSTOP_KERNEL:*:*) + GUESS=nsv-tandem-nsk$UNAME_RELEASE + ;; + NSX-*:NONSTOP_KERNEL:*:*) + GUESS=nsx-tandem-nsk$UNAME_RELEASE + ;; *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; + GUESS=mips-compaq-nonstopux + ;; BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; + GUESS=bs2000-siemens-sysv + ;; DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE + ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. - if test "$cputype" = "386"; then + if test "${cputype-}" = 386; then UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" + elif test "x${cputype-}" != x; then + UNAME_MACHINE=$cputype fi - echo ${UNAME_MACHINE}-unknown-plan9 - exit ;; + GUESS=$UNAME_MACHINE-unknown-plan9 + ;; *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit ;; + GUESS=pdp10-unknown-tops10 + ;; *:TENEX:*:*) - echo pdp10-unknown-tenex - exit ;; + GUESS=pdp10-unknown-tenex + ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; + GUESS=pdp10-dec-tops20 + ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; + GUESS=pdp10-xkl-tops20 + ;; *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit ;; + GUESS=pdp10-unknown-tops20 + ;; *:ITS:*:*) - echo pdp10-unknown-its - exit ;; + GUESS=pdp10-unknown-its + ;; SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} - exit ;; + GUESS=mips-sei-seiux$UNAME_RELEASE + ;; *:DragonFly:*:*) - echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit ;; + DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL + ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "${UNAME_MACHINE}" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; + case $UNAME_MACHINE in + A*) GUESS=alpha-dec-vms ;; + I*) GUESS=ia64-dec-vms ;; + V*) GUESS=vax-dec-vms ;; esac ;; *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; + GUESS=i386-pc-xenix + ;; i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' - exit ;; + SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` + GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL + ;; i*86:rdos:*:*) - echo ${UNAME_MACHINE}-pc-rdos - exit ;; - i*86:AROS:*:*) - echo ${UNAME_MACHINE}-pc-aros - exit ;; + GUESS=$UNAME_MACHINE-pc-rdos + ;; + *:AROS:*:*) + GUESS=$UNAME_MACHINE-unknown-aros + ;; + x86_64:VMkernel:*:*) + GUESS=$UNAME_MACHINE-unknown-esx + ;; + amd64:Isilon\ OneFS:*:*) + GUESS=x86_64-unknown-onefs + ;; + *:Unleashed:*:*) + GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE + ;; esac -#echo '(No uname command or uname output not recognized.)' 1>&2 -#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 +# Do we have a guess based on uname results? +if test "x$GUESS" != x; then + echo "$GUESS" + exit +fi -eval $set_cc_for_build -cat >$dummy.c < "$dummy.c" < -# include +#include +#include +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#include +#if defined(_SIZE_T_) || defined(SIGLOST) +#include +#endif +#endif #endif main () { @@ -1342,20 +1568,12 @@ main () #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 - "4" + "4" #else - "" -#endif - ); exit (0); -#endif + "" #endif - -#if defined (__arm) && defined (__acorn) && defined (__unix) - printf ("arm-acorn-riscix\n"); exit (0); + ); exit (0); #endif - -#if defined (hp300) && !defined (hpux) - printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) @@ -1397,39 +1615,54 @@ main () #endif #if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); + struct utsname un; + uname(&un); + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) -# if !defined (ultrix) -# include -# if defined (BSD) -# if BSD == 43 - printf ("vax-dec-bsd4.3\n"); exit (0); -# else -# if BSD == 199006 - printf ("vax-dec-bsd4.3reno\n"); exit (0); -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# endif -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# else - printf ("vax-dec-ultrix\n"); exit (0); -# endif +#if !defined (ultrix) +#include +#if defined (BSD) +#if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +#else +#if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#endif +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#else +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname un; + uname (&un); + printf ("vax-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname *un; + uname (&un); + printf ("mips-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("mips-dec-ultrix\n"); exit (0); +#endif +#endif #endif #if defined (alliant) && defined (i860) @@ -1440,54 +1673,46 @@ main () } EOF -$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. +test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } -test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } +echo "$0: unable to guess system type" >&2 -# Convex versions that predate uname can use getsysinfo(1) +case $UNAME_MACHINE:$UNAME_SYSTEM in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <&2 < in order to provide the needed -information to handle your system. +our_year=`echo $timestamp | sed 's,-.*,,'` +thisyear=`date +%Y` +# shellcheck disable=SC2003 +script_age=`expr "$thisyear" - "$our_year"` +if test "$script_age" -lt 3 ; then + cat >&2 </dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" EOF +fi exit 1 # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" diff --git a/packages/zoltan/config/config.sub b/packages/zoltan/config/config.sub index d6b6b3c768ff..d74fb6deac94 100755 --- a/packages/zoltan/config/config.sub +++ b/packages/zoltan/config/config.sub @@ -1,38 +1,33 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012 Free Software Foundation, Inc. +# Copyright 1992-2021 Free Software Foundation, Inc. -timestamp='2012-01-01' +# shellcheck disable=SC2006,SC2268 # see below for rationale -# This file is (in principle) common to ALL GNU software. -# The presence of a machine in this file suggests that SOME GNU software -# can handle that machine. It does not imply ALL GNU software can. -# -# This file is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or +timestamp='2021-08-14' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA -# 02110-1301, USA. +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). -# Please send patches to . Submit a context -# diff and a properly formatted GNU ChangeLog entry. +# Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. @@ -40,7 +35,7 @@ timestamp='2012-01-01' # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD +# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases @@ -57,15 +52,21 @@ timestamp='2012-01-01' # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + me=`echo "$0" | sed -e 's,.*/,,'` usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -75,9 +76,7 @@ Report bugs and patches to ." version="\ GNU config.sub ($timestamp) -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, -2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 -Free Software Foundation, Inc. +Copyright 1992-2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -99,12 +98,12 @@ while test $# -gt 0 ; do - ) # Use stdin as input. break ;; -* ) - echo "$me: invalid option $1$help" + echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) # First pass through any local machine types. - echo $1 + echo "$1" exit ;; * ) @@ -120,1181 +119,1181 @@ case $# in exit 1;; esac -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ - linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | \ - kopensolaris*-gnu* | \ - storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac +# Split fields of configuration type +# shellcheck disable=SC2162 +saved_IFS=$IFS +IFS="-" read field1 field2 field3 field4 <&2 + exit 1 ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + *-*-*-*) + basic_machine=$field1-$field2 + basic_os=$field3-$field4 ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + nto-qnx* | linux-* | uclinux-uclibc* \ + | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ + | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ + | storm-chaos* | os2-emx* | rtmk-nova*) + basic_machine=$field1 + basic_os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + basic_os=linux-android + ;; + *) + basic_machine=$field1-$field2 + basic_os=$field3 + ;; + esac ;; - -psos*) - os=-psos + *-*) + # A lone config we happen to match not fitting any pattern + case $field1-$field2 in + decstation-3100) + basic_machine=mips-dec + basic_os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Prevent following clause from handling this valid os + sun*os*) + basic_machine=$field1 + basic_os=$field2 + ;; + zephyr*) + basic_machine=$field1-unknown + basic_os=$field2 + ;; + # Manufacturers + dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ + | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ + | unicom* | ibm* | next | hp | isi* | apollo | altos* \ + | convergent* | ncr* | news | 32* | 3600* | 3100* \ + | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ + | ultra | tti* | harris | dolphin | highlevel | gould \ + | cbm | ns | masscomp | apple | axis | knuth | cray \ + | microblaze* | sim | cisco \ + | oki | wec | wrs | winbond) + basic_machine=$field1-$field2 + basic_os= + ;; + *) + basic_machine=$field1 + basic_os=$field2 + ;; + esac + ;; + esac ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + basic_os=bsd + ;; + a29khif) + basic_machine=a29k-amd + basic_os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + basic_os=scout + ;; + alliant) + basic_machine=fx80-alliant + basic_os= + ;; + altos | altos3068) + basic_machine=m68k-altos + basic_os= + ;; + am29k) + basic_machine=a29k-none + basic_os=bsd + ;; + amdahl) + basic_machine=580-amdahl + basic_os=sysv + ;; + amiga) + basic_machine=m68k-unknown + basic_os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + basic_os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + basic_os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + basic_os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + basic_os=bsd + ;; + aros) + basic_machine=i386-pc + basic_os=aros + ;; + aux) + basic_machine=m68k-apple + basic_os=aux + ;; + balance) + basic_machine=ns32k-sequent + basic_os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + basic_os=linux + ;; + cegcc) + basic_machine=arm-unknown + basic_os=cegcc + ;; + convex-c1) + basic_machine=c1-convex + basic_os=bsd + ;; + convex-c2) + basic_machine=c2-convex + basic_os=bsd + ;; + convex-c32) + basic_machine=c32-convex + basic_os=bsd + ;; + convex-c34) + basic_machine=c34-convex + basic_os=bsd + ;; + convex-c38) + basic_machine=c38-convex + basic_os=bsd + ;; + cray) + basic_machine=j90-cray + basic_os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + basic_os= + ;; + da30) + basic_machine=m68k-da30 + basic_os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + basic_os= + ;; + delta88) + basic_machine=m88k-motorola + basic_os=sysv3 + ;; + dicos) + basic_machine=i686-pc + basic_os=dicos + ;; + djgpp) + basic_machine=i586-pc + basic_os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + basic_os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + basic_os=ose + ;; + gmicro) + basic_machine=tron-gmicro + basic_os=sysv + ;; + go32) + basic_machine=i386-pc + basic_os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + basic_os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + basic_os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + basic_os=hms + ;; + harris) + basic_machine=m88k-harris + basic_os=sysv3 + ;; + hp300 | hp300hpux) + basic_machine=m68k-hp + basic_os=hpux + ;; + hp300bsd) + basic_machine=m68k-hp + basic_os=bsd + ;; + hppaosf) + basic_machine=hppa1.1-hp + basic_os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + basic_os=proelf + ;; + i386mach) + basic_machine=i386-mach + basic_os=mach + ;; + isi68 | isi) + basic_machine=m68k-isi + basic_os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + basic_os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + basic_os=sysv + ;; + merlin) + basic_machine=ns32k-utek + basic_os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + basic_os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + basic_os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + basic_os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + basic_os=coff + ;; + morphos) + basic_machine=powerpc-unknown + basic_os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + basic_os=moxiebox + ;; + msdos) + basic_machine=i386-pc + basic_os=msdos + ;; + msys) + basic_machine=i686-pc + basic_os=msys + ;; + mvs) + basic_machine=i370-ibm + basic_os=mvs + ;; + nacl) + basic_machine=le32-unknown + basic_os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + basic_os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + basic_os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + basic_os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + basic_os=newsos + ;; + news1000) + basic_machine=m68030-sony + basic_os=newsos + ;; + necv70) + basic_machine=v70-nec + basic_os=sysv + ;; + nh3000) + basic_machine=m68k-harris + basic_os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + basic_os=cxux + ;; + nindy960) + basic_machine=i960-intel + basic_os=nindy + ;; + mon960) + basic_machine=i960-intel + basic_os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + basic_os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + basic_os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + basic_os=ose + ;; + os68k) + basic_machine=m68k-none + basic_os=os68k + ;; + paragon) + basic_machine=i860-intel + basic_os=osf + ;; + parisc) + basic_machine=hppa-unknown + basic_os=linux + ;; + psp) + basic_machine=mipsallegrexel-sony + basic_os=psp + ;; + pw32) + basic_machine=i586-unknown + basic_os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + basic_os=rdos + ;; + rdos32) + basic_machine=i386-pc + basic_os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + basic_os=coff + ;; + sa29200) + basic_machine=a29k-amd + basic_os=udi + ;; + sei) + basic_machine=mips-sei + basic_os=seiux + ;; + sequent) + basic_machine=i386-sequent + basic_os= + ;; + sps7) + basic_machine=m68k-bull + basic_os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + basic_os= + ;; + stratus) + basic_machine=i860-stratus + basic_os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + basic_os= + ;; + sun2os3) + basic_machine=m68000-sun + basic_os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + basic_os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + basic_os= + ;; + sun3os3) + basic_machine=m68k-sun + basic_os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + basic_os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + basic_os= + ;; + sun4os3) + basic_machine=sparc-sun + basic_os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + basic_os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + basic_os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + basic_os= + ;; + sv1) + basic_machine=sv1-cray + basic_os=unicos + ;; + symmetry) + basic_machine=i386-sequent + basic_os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + basic_os=unicos + ;; + t90) + basic_machine=t90-cray + basic_os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + basic_os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + basic_os=tpf + ;; + udi29k) + basic_machine=a29k-amd + basic_os=udi + ;; + ultra3) + basic_machine=a29k-nyu + basic_os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + basic_os=none + ;; + vaxv) + basic_machine=vax-dec + basic_os=sysv + ;; + vms) + basic_machine=vax-dec + basic_os=vms + ;; + vsta) + basic_machine=i386-pc + basic_os=vsta + ;; + vxworks960) + basic_machine=i960-wrs + basic_os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + basic_os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + basic_os=vxworks + ;; + xbox) + basic_machine=i686-pc + basic_os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + basic_os=unicos + ;; + *) + basic_machine=$1 + basic_os= + ;; + esac ;; esac -# Decode aliases for certain CPU-COMPANY combinations. +# Decode 1-component or ad-hoc basic machines case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | am33_2.0 \ - | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ - | be32 | be64 \ - | bfin \ - | c4x | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | epiphany \ - | fido | fr30 | frv \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | hexagon \ - | i370 | i860 | i960 | ia64 \ - | ip2k | iq2000 \ - | le32 | le64 \ - | lm32 \ - | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | mcore | mep | metag \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64octeon | mips64octeonel \ - | mips64orion | mips64orionel \ - | mips64r5900 | mips64r5900el \ - | mips64vr | mips64vrel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | moxie \ - | mt \ - | msp430 \ - | nds32 | nds32le | nds32be \ - | nios | nios2 \ - | ns16k | ns32k \ - | open8 \ - | or32 \ - | pdp10 | pdp11 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle \ - | pyramid \ - | rl78 | rx \ - | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ - | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ - | spu \ - | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ - | ubicom32 \ - | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ - | we32k \ - | x86 | xc16x | xstormy16 | xtensa \ - | z8k | z80) - basic_machine=$basic_machine-unknown - ;; - c54x) - basic_machine=tic54x-unknown - ;; - c55x) - basic_machine=tic55x-unknown - ;; - c6x) - basic_machine=tic6x-unknown - ;; - m6811 | m68hc11 | m6812 | m68hc12 | picochip) - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) - ;; - ms1) - basic_machine=mt-unknown + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond ;; - - strongarm | thumb | xscale) - basic_machine=arm-unknown + op50n) + cpu=hppa1.1 + vendor=oki ;; - - xscaleeb) - basic_machine=armeb-unknown + op60c) + cpu=hppa1.1 + vendor=oki ;; - - xscaleel) - basic_machine=armel-unknown + ibm*) + cpu=i370 + vendor=ibm ;; - - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 + orion105) + cpu=clipper + vendor=highlevel ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ - | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* | avr32-* \ - | be32-* | be64-* \ - | bfin-* | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | clipper-* | craynv-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | elxsi-* \ - | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | hexagon-* \ - | i*86-* | i860-* | i960-* | ia64-* \ - | ip2k-* | iq2000-* \ - | le32-* | le64-* \ - | lm32-* \ - | m32c-* | m32r-* | m32rle-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64octeon-* | mips64octeonel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64r5900-* | mips64r5900el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mips64vr5900-* | mips64vr5900el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipstx39-* | mipstx39el-* \ - | mmix-* \ - | mt-* \ - | msp430-* \ - | nds32-* | nds32le-* | nds32be-* \ - | nios-* | nios2-* \ - | none-* | np1-* | ns16k-* | ns32k-* \ - | open8-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ - | pyramid-* \ - | rl78-* | romp-* | rs6000-* | rx-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ - | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ - | tahoe-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tile*-* \ - | tron-* \ - | ubicom32-* \ - | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ - | vax-* \ - | we32k-* \ - | x86-* | x86_64-* | xc16x-* | xps100-* \ - | xstormy16-* | xtensa*-* \ - | ymp-* \ - | z8k-* | z80-*) - ;; - # Recognize the basic CPU types without company name, with glob match. - xtensa*) - basic_machine=$basic_machine-unknown + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple + ;; + pmac | pmac-mpw) + cpu=powerpc + vendor=apple ;; + # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att + cpu=m68000 + vendor=att ;; 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - abacus) - basic_machine=abacus-unknown - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amd64-*) - basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aros) - basic_machine=i386-pc - os=-aros - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - blackfin) - basic_machine=bfin-unknown - os=-linux - ;; - blackfin-*) - basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux + cpu=we32k + vendor=att ;; bluegene*) - basic_machine=powerpc-ibm - os=-cnk - ;; - c54x-*) - basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c55x-*) - basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c6x-*) - basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - cegcc) - basic_machine=arm-unknown - os=-cegcc - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp - ;; - cr16 | cr16-*) - basic_machine=cr16-unknown - os=-elf - ;; - crds | unos) - basic_machine=m68k-crds - ;; - crisv32 | crisv32-* | etraxfs*) - basic_machine=crisv32-axis - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - crx) - basic_machine=crx-unknown - os=-elf - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec + cpu=powerpc + vendor=ibm + basic_os=cnk ;; decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 + cpu=pdp10 + vendor=dec + basic_os=tops10 ;; decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 + cpu=pdp10 + vendor=dec + basic_os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dicos) - basic_machine=i686-pc - os=-dicos - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon + cpu=m68k + vendor=motorola ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd + dpx2*) + cpu=m68k + vendor=bull + basic_os=sysv3 ;; encore | umax | mmax) - basic_machine=ns32k-encore + cpu=ns32k + vendor=encore ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose + elxsi) + cpu=elxsi + vendor=elxsi + basic_os=${basic_os:-bsd} ;; fx2800) - basic_machine=i860-alliant + cpu=i860 + vendor=alliant ;; genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 + cpu=ns32k + vendor=ns ;; h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp + cpu=hppa1.0 + vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp + cpu=m68000 + vendor=hp ;; hp9k3[2-9][0-9]) - basic_machine=m68k-hp + cpu=m68k + vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp + cpu=hppa1.0 + vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm + cpu=hppa1.0 + vendor=hp ;; i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv32 ;; i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv4 ;; i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv ;; i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=solaris2 ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta + j90 | j90-cray) + cpu=j90 + vendor=cray + basic_os=${basic_os:-unicos} ;; iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) + cpu=mips + vendor=sgi + case $basic_os in + irix*) ;; *) - os=-irix4 + basic_os=irix4 ;; esac ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - m68knommu) - basic_machine=m68k-unknown - os=-linux - ;; - m68knommu-*) - basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - microblaze) - basic_machine=microblaze-xilinx - ;; - mingw32) - basic_machine=i386-pc - os=-mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - os=-mingw32ce - ;; miniframe) - basic_machine=m68000-convergent + cpu=m68000 + vendor=convergent ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - ms1-*) - basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` - ;; - msys) - basic_machine=i386-pc - os=-msys - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - nacl) - basic_machine=le32-unknown - os=-nacl - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + basic_os=mint ;; news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) + cpu=mips + vendor=sony + basic_os=newsos + ;; + next | m*-next) + cpu=m68k + vendor=next + case $basic_os in + openstep*) + ;; + nextstep*) ;; - -ns2*) - os=-nextstep2 + ns2*) + basic_os=nextstep2 ;; *) - os=-nextstep3 + basic_os=nextstep3 ;; esac ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; np1) - basic_machine=np1-gould - ;; - neo-tandem) - basic_machine=neo-tandem - ;; - nse-tandem) - basic_machine=nse-tandem - ;; - nsr-tandem) - basic_machine=nsr-tandem + cpu=np1 + vendor=gould ;; op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - openrisc | openrisc-*) - basic_machine=or32-unknown - ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k + cpu=hppa1.1 + vendor=oki + basic_os=proelf ;; pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - parisc) - basic_machine=hppa-unknown - os=-linux - ;; - parisc-*) - basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 ;; pbd) - basic_machine=sparc-tti + cpu=sparc + vendor=tti ;; pbb) - basic_machine=m68k-tti - ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 - ;; - pc98) - basic_machine=i386-pc - ;; - pc98-*) - basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + cpu=m68k + vendor=tti ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc - ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc - ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc - ;; - pentium4) - basic_machine=i786-pc - ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + pc532) + cpu=ns32k + vendor=pc532 ;; pn) - basic_machine=pn-gould - ;; - power) basic_machine=power-ibm - ;; - ppc | ppcbe) basic_machine=powerpc-unknown - ;; - ppc-* | ppcbe-*) - basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + cpu=pn + vendor=gould ;; - ppcle | powerpclittle | ppc-le | powerpc-little) - basic_machine=powerpcle-unknown - ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64) basic_machine=powerpc64-unknown - ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) - basic_machine=powerpc64le-unknown - ;; - ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + power) + cpu=power + vendor=ibm ;; ps2) - basic_machine=i386-ibm - ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rdos) - basic_machine=i386-pc - os=-rdos - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff + cpu=i386 + vendor=ibm ;; rm[46]00) - basic_machine=mips-siemens + cpu=mips + vendor=siemens ;; rtpc | rtpc-*) - basic_machine=romp-ibm - ;; - s390 | s390-*) - basic_machine=s390-ibm + cpu=romp + vendor=ibm ;; - s390x | s390x-*) - basic_machine=s390x-ibm - ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; - sb1) - basic_machine=mipsisa64sb1-unknown + sde) + cpu=mipsisa32 + vendor=sde + basic_os=${basic_os:-elf} ;; - sb1el) - basic_machine=mipsisa64sb1el-unknown + simso-wrs) + cpu=sparclite + vendor=wrs + basic_os=vxworks ;; - sde) - basic_machine=mipsisa32-sde - os=-elf + tower | tower-32) + cpu=m68k + vendor=ncr ;; - sei) - basic_machine=mips-sei - os=-seiux + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu ;; - sequent) - basic_machine=i386-sequent + w65) + cpu=w65 + vendor=wdc ;; - sh) - basic_machine=sh-hitachi - os=-hms + w89k-*) + cpu=hppa1.1 + vendor=winbond + basic_os=proelf ;; - sh5el) - basic_machine=sh5le-unknown + none) + cpu=none + vendor=none ;; - sh64) - basic_machine=sh64-unknown + leon|leon[3-9]) + cpu=sparc + vendor=$basic_machine ;; - sparclite-wrs | simso-wrs) - basic_machine=sparclite-wrs - os=-vxworks + leon-*|leon[3-9]-*) + cpu=sparc + vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 + + *-*) + # shellcheck disable=SC2162 + saved_IFS=$IFS + IFS="-" read cpu vendor <&2 - exit 1 + # Recognize the canonical CPU types that are allowed with any + # company name. + case $cpu in + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | abacus \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \ + | alphapca5[67] | alpha64pca5[67] \ + | am33_2.0 \ + | amdgcn \ + | arc | arceb | arc32 | arc64 \ + | arm | arm[lb]e | arme[lb] | armv* \ + | avr | avr32 \ + | asmjs \ + | ba \ + | be32 | be64 \ + | bfin | bpf | bs2000 \ + | c[123]* | c30 | [cjt]90 | c4x \ + | c8051 | clipper | craynv | csky | cydra \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | elxsi | epiphany \ + | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \ + | h8300 | h8500 \ + | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i*86 | i860 | i960 | ia16 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | loongarch32 | loongarch64 | loongarchx32 \ + | m32c | m32r | m32rle \ + | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \ + | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \ + | m88110 | m88k | maxq | mb | mcore | mep | metag \ + | microblaze | microblazeel \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64eb | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r3 | mipsisa32r3el \ + | mipsisa32r5 | mipsisa32r5el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r3 | mipsisa64r3el \ + | mipsisa64r5 | mipsisa64r5el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mmix \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nfp \ + | nios | nios2 | nios2eb | nios2el \ + | none | np1 | ns16k | ns32k | nvptx \ + | open8 \ + | or1k* \ + | or32 \ + | orion \ + | picochip \ + | pdp10 | pdp11 | pj | pjl | pn | power \ + | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \ + | pru \ + | pyramid \ + | riscv | riscv32 | riscv32be | riscv64 | riscv64be \ + | rl78 | romp | rs6000 | rx \ + | s390 | s390x \ + | score \ + | sh | shl \ + | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \ + | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \ + | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \ + | spu \ + | tahoe \ + | thumbv7* \ + | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \ + | tron \ + | ubicom32 \ + | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \ + | vax \ + | visium \ + | w65 \ + | wasm32 | wasm64 \ + | we32k \ + | x86 | x86_64 | xc16x | xgate | xps100 \ + | xstormy16 | xtensa* \ + | ymp \ + | z8k | z80) + ;; + + *) + echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2 + exit 1 + ;; + esac ;; esac # Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` +case $vendor in + digital*) + vendor=dec ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + commodore*) + vendor=cbm ;; *) ;; @@ -1302,203 +1301,215 @@ esac # Decode manufacturer-specific aliases for certain operating systems. -if [ x"$os" != x"" ] +if test x$basic_os != x then -case $os in - # First match some system type aliases - # that might get confused with valid system types. - # -solaris* is a basic system type, with this one exception. - -auroraux) - os=-auroraux + +# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just +# set os. +case $basic_os in + gnu/linux*) + kernel=linux + os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` + ;; + os2-emx) + kernel=os2 + os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` + ;; + nto-qnx*) + kernel=nto + os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` + ;; + *-*) + # shellcheck disable=SC2162 + saved_IFS=$IFS + IFS="-" read kernel os <&2 - exit 1 + # No normalization, but not necessarily accepted, that comes below. ;; esac + else # Here we handle the default operating systems that come with various machines. @@ -1511,255 +1522,362 @@ else # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. -case $basic_machine in +kernel= +case $cpu-$vendor in score-*) - os=-elf + os=elf ;; spu-*) - os=-elf + os=elf ;; *-acorn) - os=-riscix1.2 + os=riscix1.2 ;; arm*-rebel) - os=-linux + kernel=linux + os=gnu ;; arm*-semi) - os=-aout + os=aout ;; c4x-* | tic4x-*) - os=-coff + os=coff + ;; + c8051-*) + os=elf + ;; + clipper-intergraph) + os=clix + ;; + hexagon-*) + os=elf ;; tic54x-*) - os=-coff + os=coff ;; tic55x-*) - os=-coff + os=coff ;; tic6x-*) - os=-coff + os=coff ;; # This must come before the *-dec entry. pdp10-*) - os=-tops20 + os=tops20 ;; pdp11-*) - os=-none + os=none ;; *-dec | vax-*) - os=-ultrix4.2 + os=ultrix4.2 ;; m68*-apollo) - os=-domain + os=domain ;; i386-sun) - os=-sunos4.0.2 + os=sunos4.0.2 ;; m68000-sun) - os=-sunos3 + os=sunos3 ;; m68*-cisco) - os=-aout + os=aout ;; mep-*) - os=-elf + os=elf ;; mips*-cisco) - os=-elf + os=elf ;; mips*-*) - os=-elf + os=elf ;; or32-*) - os=-coff + os=coff ;; *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 + os=sysv3 ;; sparc-* | *-sun) - os=-sunos4.1.1 + os=sunos4.1.1 ;; - *-be) - os=-beos + pru-*) + os=elf ;; - *-haiku) - os=-haiku + *-be) + os=beos ;; *-ibm) - os=-aix + os=aix ;; *-knuth) - os=-mmixware + os=mmixware ;; *-wec) - os=-proelf + os=proelf ;; *-winbond) - os=-proelf + os=proelf ;; *-oki) - os=-proelf + os=proelf ;; *-hp) - os=-hpux + os=hpux ;; *-hitachi) - os=-hiux + os=hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv + os=sysv ;; *-cbm) - os=-amigaos + os=amigaos ;; *-dg) - os=-dgux + os=dgux ;; *-dolphin) - os=-sysv3 + os=sysv3 ;; m68k-ccur) - os=-rtu + os=rtu ;; m88k-omron*) - os=-luna + os=luna ;; - *-next ) - os=-nextstep + *-next) + os=nextstep ;; *-sequent) - os=-ptx + os=ptx ;; *-crds) - os=-unos + os=unos ;; *-ns) - os=-genix + os=genix ;; i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 + os=mvs ;; *-gould) - os=-sysv + os=sysv ;; *-highlevel) - os=-bsd + os=bsd ;; *-encore) - os=-bsd + os=bsd ;; *-sgi) - os=-irix + os=irix ;; *-siemens) - os=-sysv4 + os=sysv4 ;; *-masscomp) - os=-rtu + os=rtu ;; f30[01]-fujitsu | f700-fujitsu) - os=-uxpv + os=uxpv ;; *-rom68k) - os=-coff + os=coff ;; *-*bug) - os=-coff + os=coff ;; *-apple) - os=-macos + os=macos ;; *-atari*) - os=-mint + os=mint + ;; + *-wrs) + os=vxworks ;; *) - os=-none + os=none ;; esac + fi +# Now, validate our (potentially fixed-up) OS. +case $os in + # Sometimes we do "kernel-libc", so those need to count as OSes. + musl* | newlib* | relibc* | uclibc*) + ;; + # Likewise for "kernel-abi" + eabi* | gnueabi*) + ;; + # VxWorks passes extra cpu info in the 4th filed. + simlinux | simwindows | spe) + ;; + # Now accept the basic system types. + # The portable systems comes first. + # Each alternative MUST end in a * to match a version number. + gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ + | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \ + | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ + | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ + | hiux* | abug | nacl* | netware* | windows* \ + | os9* | macos* | osx* | ios* \ + | mpw* | magic* | mmixware* | mon960* | lnews* \ + | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ + | aos* | aros* | cloudabi* | sortix* | twizzler* \ + | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ + | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ + | mirbsd* | netbsd* | dicos* | openedition* | ose* \ + | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \ + | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \ + | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ + | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ + | udi* | lites* | ieee* | go32* | aux* | hcos* \ + | chorusrdb* | cegcc* | glidix* | serenity* \ + | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ + | midipix* | mingw32* | mingw64* | mint* \ + | uxpv* | beos* | mpeix* | udk* | moxiebox* \ + | interix* | uwin* | mks* | rhapsody* | darwin* \ + | openstep* | oskit* | conix* | pw32* | nonstopux* \ + | storm-chaos* | tops10* | tenex* | tops20* | its* \ + | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \ + | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \ + | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ + | skyos* | haiku* | rdos* | toppers* | drops* | es* \ + | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ + | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ + | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr*) + ;; + # This one is extra strict with allowed versions + sco3.2v2 | sco3.2v[4-9]* | sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + ;; + none) + ;; + *) + echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2 + exit 1 + ;; +esac + +# As a final step for OS-related things, validate the OS-kernel combination +# (given a valid OS), if there is a kernel. +case $kernel-$os in + linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ + | linux-musl* | linux-relibc* | linux-uclibc* ) + ;; + uclinux-uclibc* ) + ;; + -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* ) + # These are just libc implementations, not actual OSes, and thus + # require a kernel. + echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 + exit 1 + ;; + kfreebsd*-gnu* | kopensolaris*-gnu*) + ;; + vxworks-simlinux | vxworks-simwindows | vxworks-spe) + ;; + nto-qnx*) + ;; + os2-emx) + ;; + *-eabi* | *-gnueabi*) + ;; + -*) + # Blank kernel with real OS is always fine. + ;; + *-*) + echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 + exit 1 + ;; +esac + # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) +case $vendor in + unknown) + case $cpu-$os in + *-riscix*) vendor=acorn ;; - -sunos*) + *-sunos*) vendor=sun ;; - -cnk*|-aix*) + *-cnk* | *-aix*) vendor=ibm ;; - -beos*) + *-beos*) vendor=be ;; - -hpux*) + *-hpux*) vendor=hp ;; - -mpeix*) + *-mpeix*) vendor=hp ;; - -hiux*) + *-hiux*) vendor=hitachi ;; - -unos*) + *-unos*) vendor=crds ;; - -dgux*) + *-dgux*) vendor=dg ;; - -luna*) + *-luna*) vendor=omron ;; - -genix*) + *-genix*) vendor=ns ;; - -mvs* | -opened*) + *-clix*) + vendor=intergraph + ;; + *-mvs* | *-opened*) + vendor=ibm + ;; + *-os400*) vendor=ibm ;; - -os400*) + s390-* | s390x-*) vendor=ibm ;; - -ptx*) + *-ptx*) vendor=sequent ;; - -tpf*) + *-tpf*) vendor=ibm ;; - -vxsim* | -vxworks* | -windiss*) + *-vxsim* | *-vxworks* | *-windiss*) vendor=wrs ;; - -aux*) + *-aux*) vendor=apple ;; - -hms*) + *-hms*) vendor=hitachi ;; - -mpw* | -macos*) + *-mpw* | *-macos*) vendor=apple ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) vendor=atari ;; - -vos*) + *-vos*) vendor=stratus ;; esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac -echo $basic_machine$os +echo "$cpu-$vendor-${kernel:+$kernel-}$os" exit # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'"