Compare commits

..
120 Commits
Author SHA1 Message Date
bensworth d8501138bc Some mods for compiling 2021-08-18 09:42:36 -06:00
bensworth c679c99b35 minor mods for MFEM 2021-08-18 09:08:24 -06:00
bensworth 3f9a3658a6 Added framework for IMEX BDF and RK; not included in MFEM config/setup yet 2021-08-18 09:03:18 -06:00
Tzanio Kolev 6e149b75e5 Merge pull request #2456 from mfem/const-mesh-ctor-dev
Const parameter for mesh copy assignment
2021-08-15 18:16:37 -07:00
Tzanio Kolev 90d6afb814 Merge pull request #2445 from mfem/jacobf/2021-08-02/fix-cusparse-deprecated-alg
Improve CUSPARSE SPMV alg selection
2021-08-15 18:16:01 -07:00
Tzanio 8f25a46d5c Merge branch 'bug-fix-tmop' 2021-08-10 18:09:33 -07:00
Veselin Dobrev 1682ade22e Add constructors for the classes Vector and (Par)GridFunction that
use the data from another Vector or sub-Vector.

In class DiscreteAdaptTC, remove calls to tspec.HostReadWrite() that
are no longer needed when constructing (Par)GrindFunction from data in
a Vector.

Add some MFEM_ASSERT statements in Memory::MakeAlias.
2021-08-10 13:39:41 -07:00
Ketan Mittal be9fc95d6f enforce support for hr on cpus only 2021-08-10 11:46:30 -07:00
Tzanio Kolev 5dd706208a Merge pull request #2425 from mfem/build/bernede1/update-spack-package
Update spack package [build/bernede1/update-spack-package]
2021-08-10 10:23:18 -07:00
Ketan Mittal 7735de95d2 fix parallel version also for tspec pointer 2021-08-09 11:59:56 -07:00
Ketan Mittal 2f023c6e53 get host pointer for tspec 2021-08-09 11:48:54 -07:00
Ketan Mittal 4cf47eca13 minor fix to coef_zeta for final energy calculation 2021-08-09 09:27:52 -07:00
Tzanio 883a5843cd Merge branch 'master' into build/bernede1/update-spack-package 2021-08-09 08:39:26 -07:00
Tzanio Kolev ef5dbd0bf6 Merge pull request #1922 from mfem/tmop-amr-dev
hr-adaptivity with TMOP-based error estimators
2021-08-08 16:31:14 -07:00
Tzanio Kolev b02eaf34a1 Merge pull request #2447 from mfem/artv3/bugfix/trueAddMult
Fix MFEM_VERIFY in TrueAddMult
2021-08-08 16:29:45 -07:00
Tzanio Kolev f26765afa1 Merge pull request #2357 from mfem/yohann/mem/copy-checks
Verify that sizes in `Memory<T>` copy methods are compatible.
2021-08-08 16:29:27 -07:00
Tzanio Kolev f81a681203 Merge pull request #2399 from mfem/bugfix-face-restriction
Face neighbor data bugfix [bugfix-face-restriction]
2021-08-06 09:30:49 -07:00
Tzanio Kolev 5705de1507 Merge pull request #2441 from mfem/GetElementSize_fix
fix GetElementSize for surface meshes
2021-08-05 12:17:03 -07:00
Tzanio Kolev 1958f8de19 Merge pull request #2422 from trevilo/master
Bug fix in FindPointsGSLIB::Interpolate when using L2_FECollection in parallel
2021-08-05 12:16:32 -07:00
Josh Essman aacc159390 fix: const param for mesh copy assignment 2021-08-05 09:30:26 -05:00
Arturo Vargas 3b26d08793 revert bilinearform_ext 2021-08-02 21:27:02 -07:00
Arturo Vargas 13892eed2b remove interior face integrators from bilinearforms where they do not exist; 2021-08-02 14:30:22 -07:00
Arturo Vargas 50907081fd fix trueAddMult 2021-08-02 14:08:40 -07:00
Jamie A. Bramwell 9409b7ab3b Merge branch 'master' of github.com:mfem/mfem into bugfix-face-restriction 2021-08-02 10:53:02 -07:00
Jacob Faibussowitsch 86747dd076 check for cusparse version rather than cuda version when selecting cusparse algorithm 2021-08-02 06:37:55 -07:00
Tzanio 9d6a7fe30b Small changes 2021-08-01 14:33:36 -07:00
Tzanio e717f619e7 Moved CHANGELOG entry to the right place 2021-08-01 14:25:36 -07:00
Tzanio 2f2534be10 Merge branch 'master' into tmop-amr-dev 2021-08-01 14:24:05 -07:00
Tzanio 3b8c4323fb Merge branch 'master' into yohann/mem/copy-checks 2021-08-01 14:18:29 -07:00
Keith e786ad65eb fixe GetElementSize for surface meshes 2021-07-30 16:28:24 -07:00
Ketan Mittal ecacadacf2 update changelog 2021-07-23 14:51:45 -07:00
Ketan Mittal 98a6ae7baa merge and resolve conflicts 2021-07-23 14:28:53 -07:00
Ketan Mittal 8f28244cab Merge branch 'tmop-amr-dev' of https://github.com/mfem/mfem into tmop-amr-dev 2021-07-23 14:23:05 -07:00
Ketan Mittal 8dd6b3d2b7 minor 2021-07-23 14:22:55 -07:00
Vladimir Z Tomov ee5efb4e36 Minor. 2021-07-23 14:09:38 -07:00
Vladimir Z Tomov 26e67ee06c Minor. 2021-07-23 13:43:36 -07:00
Vladimir Z Tomov f05ef4e164 Minor. 2021-07-23 09:23:52 -07:00
Adrien M. Bernede f39cf60559 Merge branch 'master' into build/bernede1/update-spack-package 2021-07-23 09:14:45 -07:00
Adrien M. Bernede 7b26fe1dc8 Update mfem-uberenv with merge commit 2021-07-23 06:30:25 -07:00
Adrien M. Bernede 81b7c820bf Update netlib for Hypre 2021-07-23 02:59:53 -07:00
Vladimir Z Tomov a7cba43e12 Minor edits in tmop_amr.hpp/cpp. 2021-07-22 16:39:05 -07:00
Ketan Mittal 9e077ffe59 address reviewer comments 2021-07-22 11:06:41 -07:00
Vladimir Z Tomov 2667d3935d Minor. 2021-07-22 07:46:48 -07:00
Todd A. Oliver 9148a1d942 Bug fix in FindPointsGSLIB::Interpolate with L2_FECollection in parallel
Previously if a given MPI task did not find any local points with
gsl_code==1 (indicating a boundary), that task would exit
FindPointsGSLIB::Interpolate before tasks that found boundary points.
This could lead to the program hanging due to subsequent communication
calls.  To avoid this behavior, we only return early if no MPI task
found any boundary points.
2021-07-22 09:21:51 -05:00
Todd A. Oliver 7859c93931 Add command line options --search-on-r0 to pfindpts miniapp
This option forces only MPI task 0 to own any points to search for, as
opposed to the default behavior where all tasks search for the same
points.  The purpose of this is to test the situation where some MPI
tasks find boundary points, meaning those tasks have points with
gsl_code=1, but other tasks do not.
2021-07-22 09:15:13 -05:00
Todd A. Oliver 55573cbe4e Minor fix s.t. schwarz_ex1p builds 2021-07-22 09:13:00 -05:00
Vladimir Z Tomov 0c68ab8e06 Minor. 2021-07-21 14:55:42 -07:00
Vladimir Z Tomov 18ce336b68 Simplified the hr input options. 2021-07-21 14:37:48 -07:00
Adrien M. BERNEDE 08941bcbe3 Update uberenv 2021-07-20 03:27:23 -07:00
Adrien M. Bernede ec845d006c Update cuda arch to new naming 2021-07-15 07:19:48 -07:00
Adrien M. Bernede ea8be0b691 Oversight 2021-07-14 14:07:23 -07:00
Adrien M. Bernede 0e4dff7f58 Update MFEM Uberenv 2021-07-13 16:51:39 -07:00
Jamie BramwellandWill Pazner 6d83ac94c9 style: Move the dynamic cast into the if statement
Co-authored-by: Will Pazner <11493037+pazner@users.noreply.github.com>
2021-07-09 16:32:37 -07:00
Jamie A. Bramwell 3297173552 fix: Ensure that face neighbor data exists when building FaceRestriction operators in parallel 2021-07-08 16:57:14 -07:00
Ketan Mittal ccb0feb4a4 remove unused variable 2021-07-02 13:08:07 -07:00
Ketan Mittal b877eee42a minor 2021-07-02 11:37:14 -07:00
Ketan Mittal b2b8407f50 add documentation and improve readability 2021-07-02 11:28:52 -07:00
Ketan Mittal d19734afbd removing user input gridfunction ptr 2021-07-01 16:23:26 -07:00
Ketan Mittal 7756b62a35 Merge branch 'tmop-amr-dev' of https://github.com/mfem/mfem into tmop-amr-dev 2021-06-30 18:27:30 -07:00
Ketan Mittal 58a304ebda reviewer comments plus fix discrete aspect-ratio component in target construction 2021-06-30 18:27:14 -07:00
Vladimir Z Tomov a3d9280b26 Minor. 2021-06-30 17:40:03 -07:00
Vladimir Z Tomov e74104e4de Merge branch 'master' into tmop-amr-dev 2021-06-30 16:23:39 -07:00
Yohann Dudouit b356865de2 Add MFEM_VERIFY to CopyTo. 2021-06-28 16:50:30 -07:00
Yohann Dudouit 81366c926c Readd the documentation. 2021-06-25 11:20:01 -07:00
Yohann Dudouit d4a6ac6507 Verify that sizes in Copy are compatible. 2021-06-24 16:59:47 -07:00
Ketan Mittal ac9e93485c update copyright statement 2021-06-01 16:38:20 -07:00
Ketan Mittal 7809f1b5a1 update mesh constructor 2021-06-01 16:34:59 -07:00
Ketan Mittal e08a9971fb comment formatting 2021-06-01 16:30:48 -07:00
Ketan Mittal 6ab84c49ea merge with master 2021-06-01 16:26:27 -07:00
Ketan Mittal 0e6e19c2cf address reviewer comments and remove some redundance objects 2021-06-01 16:08:47 -07:00
Ketan Mittal 4a0fd1525e Merge branch 'master' of https://github.com/mfem/mfem into tmop-amr-dev 2021-01-12 10:44:34 -08:00
Ketan Mittal 72b58b0ad7 bug related to serial run with parallel build 2021-01-12 10:20:19 -08:00
Ketan Mittal d36cf7858f minor changes to ncmesh/pncmesh 2021-01-12 09:11:15 -08:00
Ketan Mittal f162e36168 refactoring code and changing dtc update 2021-01-11 15:41:56 -08:00
Ketan Mittal 9afefcdf52 minor 2021-01-07 12:52:39 -08:00
Ketan Mittal d4b7c3ca11 merging tmopamr and tmopamrsolver 2021-01-07 12:27:45 -08:00
Ketan Mittal e974f12ddb Merge branch 'tmop-amr-dev' of https://github.com/mfem/mfem into tmop-amr-dev 2021-01-06 08:54:59 -08:00
Ketan Mittal b836e0ca6b make style 2021-01-05 17:49:01 -08:00
Ketan Mittal 6543546dc0 minor 2021-01-05 14:43:52 -08:00
Ketan Mittal addb6529ce modifying parallel miniapp 2021-01-05 14:43:37 -08:00
Ketan Mittal d57eeb99b0 moved hr-adaptivity classes to tmop_amr.hpp/cpp 2021-01-05 10:06:14 -08:00
Ketan Mittal 6a12b440bf merge with master and resolve conflicts 2020-12-25 06:53:09 +05:30
Ketan Mittal 7b2633066f modifying sample runs 2020-12-14 11:22:12 -08:00
Ketan Mittal 3115e2cfa1 Merge branch 'master' of https://github.com/mfem/mfem into tmop-amr-dev 2020-12-14 08:14:24 -08:00
Ketan Mittal 01a0e88422 add sample runs 2020-12-14 08:14:10 -08:00
Ketan Mittal a588dd33e8 clean up 2020-12-13 18:02:53 -08:00
Ketan Mittal 38ada18a0e style 2020-12-08 11:35:40 -08:00
Ketan Mittal 4e49041d72 pedantic 2020-12-08 10:47:20 -08:00
Ketan Mittal a3235c4029 missed tmoptools 2020-12-08 10:00:35 -08:00
Ketan Mittal 95b1bf73f9 clean up 2020-12-08 09:38:33 -08:00
Ketan Mittal 2d5818daa0 clean up miniapps 2020-12-07 16:16:24 -08:00
Ketan Mittal ea2f3933da merge with master 2020-12-07 15:07:55 -08:00
Ketan Mittal 7ac7fcf8b5 minor 2020-12-07 15:04:31 -08:00
Ketan Mittal 898964a6b7 misc minor changes.. stopping criterion updated, and changed derefinement interface 2020-09-24 11:30:19 -07:00
Ketan Mittal 8a76a96dd7 minor 2020-09-03 12:53:35 -07:00
Ketan Mittal 6665a69afb minor updates 2020-09-03 11:26:40 -07:00
Ketan Mittal 30ea12a346 Merge branch 'master' of https://github.com/mfem/mfem into tmop-amr-dev 2020-09-03 11:12:23 -07:00
Ketan Mittal d2763f0d56 add support for mixed meshes 2020-08-24 10:23:35 -07:00
Ketan Mittal 05e6ad9369 added spatial weight and threshold capability 2020-08-20 08:57:10 -07:00
Ketan Mittal a3c1a26fd7 added spatial weight and threshold capability 2020-08-20 08:56:44 -07:00
Ketan Mittal d189391502 minor changes 2020-08-17 12:19:33 -07:00
Ketan Mittal 23d3d45547 3D support and other minor changes 2020-08-12 13:50:20 -07:00
Ketan Mittal 3a497d4698 update interface 2020-08-12 11:42:52 -07:00
Ketan Mittal 46c7045d79 checkpointing new local approach.. still need to streamline 2020-08-11 17:59:11 -07:00
Ketan Mittal 6708ff1b73 work in parallel 2020-08-04 08:58:09 -07:00
Ketan Mittal a7f602fb8f adding derefinement and streamlined interface 2020-07-22 10:40:06 -07:00
Ketan Mittal b381b41c65 another approach- ref and dref 2020-05-14 06:48:10 -07:00
Ketan Mittal 2d25fda0fa checkpoint 2020-05-07 08:48:17 -07:00
Ketan Mittal eb2fa948b5 checkpoint method2 2020-05-07 08:47:37 -07:00
Ketan Mittal 376424035e checkpoint 2020-04-29 18:59:37 -07:00
Ketan Mittal 5bd2da8d65 fix aspect-ratio calculation 2020-04-14 10:02:35 -07:00
Ketan Mittal b9fc33f98c updated iso/aniso requirements 2020-04-06 10:33:37 -07:00
Ketan Mittal 0a1c1fe415 misc 2020-04-01 09:55:21 -07:00
Ketan Mittal c5a6f654db Merge branch 'master' of https://github.com/mfem/mfem into tmop-amr-dev 2020-02-19 09:16:18 -08:00
Ketan Mittal dd6f7dcad6 working in serial and parallel on analytic and discrete functions 2020-02-19 09:15:27 -08:00
Ketan Mittal fd5742e3f0 Newton and AMR in a loop 2020-02-13 12:57:08 -08:00
Ketan Mittal a1ba70e6a8 minor 2020-02-11 09:25:51 -08:00
Ketan Mittal 4da7b73965 Merge branch 'master' of https://github.com/mfem/mfem into tmop-amr-dev 2020-01-24 10:14:49 -08:00
Ketan Mittal 01bf7d6e9e updating to work for analytic adaptivity 2020-01-24 10:14:24 -08:00
Ketan Mittal 0c06ad86fd initializing branch 2020-01-23 11:46:53 -08:00
56 changed files with 3612 additions and 1827 deletions
+1 -1
View File
@@ -30,5 +30,5 @@
opt_mpi_cuda_xl_16_1_1_8:
variables:
SPEC: "%xl@16.1.1.8 +mpi +cuda cuda_arch=sm_70"
SPEC: "%xl@16.1.1.8 +mpi +cuda cuda_arch=70"
extends: .build_and_test_on_lassen
+1
View File
@@ -10,6 +10,7 @@
Version 4.3.1 (development)
===========================
- Added support for hr-adaptivity using TMOP-based error estimator.
Version 4.3, released on July 29, 2021
+1 -10
View File
@@ -267,15 +267,6 @@ if (MFEM_USE_SUNDIALS)
find_package(SUNDIALS REQUIRED ${SUNDIALS_COMPONENTS})
endif()
# EPIC
if (MFEM_USE_EPIC)
if (NOT (MFEM_USE_MPI AND MFEM_USE_SUNDIALS AND MFEM_USE_LAPACK) )
message(FATAL_ERROR " *** EPIC requires that MPI, SUNDIALS and LAPACK be enabled.")
else()
find_package(EPIC REQUIRED SUNDIALS NVector_Serial NVector_Parallel BLAS LAPACK)
endif()
endif()
# Mesquite
if (MFEM_USE_MESQUITE)
find_package(Mesquite REQUIRED)
@@ -437,7 +428,7 @@ endif()
# With newer versions of SuiteSparse which include METIS header using 64-bit
# integers, the METIS header (with 32-bit indices, as used by mfem) needs to
# be before SuiteSparse.
set(MFEM_TPLS MPI_CXX OPENMP HYPRE BLAS LAPACK SuperLUDist METIS SuiteSparse SUNDIALS EPIC PETSC
set(MFEM_TPLS MPI_CXX OPENMP HYPRE BLAS LAPACK SuperLUDist METIS SuiteSparse SUNDIALS PETSC
SLEPC MESQUITE MUMPS STRUMPACK AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB NETCDF
MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE ADIOS2
CUSPARSE MKL_CPARDISO AMGX CALIPER)
-1
View File
@@ -29,7 +29,6 @@ set(MFEM_USE_LEGACY_OPENMP @MFEM_USE_LEGACY_OPENMP@)
set(MFEM_USE_MEMALLOC @MFEM_USE_MEMALLOC@)
set(MFEM_TIMER_TYPE @MFEM_TIMER_TYPE@)
set(MFEM_USE_SUNDIALS @MFEM_USE_SUNDIALS@)
set(MFEM_USE_EPIC @MFEM_USE_EPIC@)
set(MFEM_USE_MESQUITE @MFEM_USE_MESQUITE@)
set(MFEM_USE_SUITESPARSE @MFEM_USE_SUITESPARSE@)
set(MFEM_USE_SUPERLU @MFEM_USE_SUPERLU@)
-3
View File
@@ -165,9 +165,6 @@
// Enable MFEM functionality based on the SUNDIALS libraries.
#cmakedefine MFEM_USE_SUNDIALS
// Enable MFEM functionality based on the EPIC libraries.
#cmakedefine MFEM_USE_EPIC
// Version of HYPRE used for building MFEM.
#cmakedefine MFEM_HYPRE_VERSION @MFEM_HYPRE_VERSION@
-21
View File
@@ -1,21 +0,0 @@
# Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Defines the following variables:
# - EPIC_FOUND
# - EPIC_LIBRARIES
# - EPIC_INCLUDE_DIRS
include(MfemCmakeUtilities)
mfem_find_package(EPIC EPIC EPIC_DIR
"include" Epic.h "lib" epic1.0.0
"Paths to headers required by EPIC." "Libraries required by EPIC.")
@@ -759,7 +759,7 @@ function(mfem_export_mk_files)
set(CONFIG_MK_BOOL_VARS MFEM_USE_MPI MFEM_USE_METIS MFEM_USE_METIS_5
MFEM_DEBUG MFEM_USE_EXCEPTIONS MFEM_USE_ZLIB MFEM_USE_LIBUNWIND
MFEM_USE_LAPACK MFEM_THREAD_SAFE MFEM_USE_OPENMP MFEM_USE_LEGACY_OPENMP
MFEM_USE_MEMALLOC MFEM_USE_SUNDIALS MFEM_USE_EPIC MFEM_USE_MESQUITE MFEM_USE_SUITESPARSE
MFEM_USE_MEMALLOC MFEM_USE_SUNDIALS MFEM_USE_MESQUITE MFEM_USE_SUITESPARSE
MFEM_USE_SUPERLU MFEM_USE_STRUMPACK MFEM_USE_GINKGO MFEM_USE_AMGX
MFEM_USE_GNUTLS MFEM_USE_GSLIB MFEM_USE_NETCDF MFEM_USE_PETSC
MFEM_USE_SLEPC MFEM_USE_MPFR MFEM_USE_SIDRE MFEM_USE_CONDUIT MFEM_USE_PUMI
-3
View File
@@ -85,9 +85,6 @@
// Enable MFEM functionality based on the SUNDIALS libraries.
// #define MFEM_USE_SUNDIALS
// Enable MFEM functionality based on the EPIC libraries.
// #define MFEM_USE_EPIC
// Enable MFEM functionality based on the Mesquite library.
// #define MFEM_USE_MESQUITE
-1
View File
@@ -29,7 +29,6 @@ MFEM_USE_OPENMP = @MFEM_USE_OPENMP@
MFEM_USE_MEMALLOC = @MFEM_USE_MEMALLOC@
MFEM_TIMER_TYPE = @MFEM_TIMER_TYPE@
MFEM_USE_SUNDIALS = @MFEM_USE_SUNDIALS@
MFEM_USE_EPIC = @MFEM_USE_EPIC@
MFEM_USE_MESQUITE = @MFEM_USE_MESQUITE@
MFEM_USE_SUITESPARSE = @MFEM_USE_SUITESPARSE@
MFEM_USE_SUPERLU = @MFEM_USE_SUPERLU@
-4
View File
@@ -30,7 +30,6 @@ option(MFEM_USE_OPENMP "Enable the OpenMP backend" OFF)
option(MFEM_USE_LEGACY_OPENMP "Enable legacy OpenMP usage" OFF)
option(MFEM_USE_MEMALLOC "Enable the internal MEMALLOC option." ON)
option(MFEM_USE_SUNDIALS "Enable SUNDIALS usage" OFF)
option(MFEM_USE_EPIC "Enable EPIC usage" OFF)
option(MFEM_USE_MESQUITE "Enable MESQUITE usage" OFF)
option(MFEM_USE_SUITESPARSE "Enable SuiteSparse usage" OFF)
option(MFEM_USE_SUPERLU "Enable SuperLU_DIST usage" OFF)
@@ -116,9 +115,6 @@ set(SUNDIALS_DIR "${MFEM_DIR}/../sundials-5.0.0/instdir" CACHE PATH
# set(SUNDIALS_REQUIRED_PACKAGES "SuiteSparse/KLU/AMD/BTF/COLAMD/config"
# CACHE STRING "Additional packages required by SUNDIALS.")
set(EPIC_DIR "${MFEM_DIR}/../epic-cpp/instdir" CACHE PATH
"Path to the EPIC library.")
set(MESQUITE_DIR "${MFEM_DIR}/../mesquite-2.99" CACHE PATH
"Path to the Mesquite library.")
-6
View File
@@ -122,7 +122,6 @@ MFEM_USE_LEGACY_OPENMP = NO
MFEM_USE_MEMALLOC = YES
MFEM_TIMER_TYPE = $(if $(NOTMAC),2,4)
MFEM_USE_SUNDIALS = NO
MFEM_USE_EPIC = NO
MFEM_USE_MESQUITE = NO
MFEM_USE_SUITESPARSE = NO
MFEM_USE_SUPERLU = NO
@@ -232,11 +231,6 @@ endif
# If SUNDIALS was built with KLU:
# MFEM_USE_SUITESPARSE = YES
# EPIC library configuration
MESQUITE_DIR = @MFEM_DIR@/../epic-cpp/instdir
MESQUITE_OPT = -I$(EPIC_DIR)/include
MESQUITE_LIB = -L$(EPIC_DIR)/lib -lepic1.0.0
# MESQUITE library configuration
MESQUITE_DIR = @MFEM_DIR@/../mesquite-2.99
MESQUITE_OPT = -I$(MESQUITE_DIR)/include
-5
View File
@@ -159,11 +159,6 @@ if (MFEM_USE_AMGX)
add_subdirectory(amgx)
endif()
# Include the examples/epic directory if EPIC is enabled.
if (MFEM_USE_EPIC)
add_subdirectory(epic)
endif()
# Include the examples/ginkgo directory if GINKGO is enabled.
if (MFEM_USE_GINKGO)
add_subdirectory(ginkgo)
-64
View File
@@ -1,64 +0,0 @@
# Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
set(EPIC_EXAMPLES_SRCS)
list(APPEND EPIC_EXAMPLES_SRCS
ex16.cpp
)
if (MFEM_USE_MPI)
list(APPEND EPIC_EXAMPLES_SRCS
ex16p.cpp
)
endif()
# Include the source directory where mfem.hpp and mfem-performance.hpp are.
include_directories(BEFORE ${PROJECT_BINARY_DIR})
# Add "test_epic" target, see below.
add_custom_target(test_epic
${CMAKE_CTEST_COMMAND} -R epic USES_TERMINAL)
# Add one executable per cpp file, adding "epic_" as prefix. Sets
# "test_epic" as a target that depends on the given examples.
set(PFX epic_)
add_mfem_examples(EPIC_EXAMPLES_SRCS ${PFX} "" test_epic)
# Testing.
# The EPIC tests can be run separately using the target "test_epic"
# which builds the examples and runs:
# ctest -R epic
# Example 16: use the default options
# Add the tests: one test per source file.
foreach(SRC_FILE ${EPIC_EXAMPLES_SRCS})
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
set(TEST_NAME ${PFX}${TEST_NAME})
set(THIS_TEST_OPTIONS "-no-vis")
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
if (NOT (${TEST_NAME} MATCHES ".*p$"))
add_test(NAME ${TEST_NAME}_ser
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
else()
add_test(NAME ${TEST_NAME}_np=4
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
${MPIEXEC_POSTFLAGS})
endif()
endforeach()
-17
View File
@@ -1,17 +0,0 @@
Finite Element Discretization Library
__
_ __ ___ / _| ___ _ __ ___
| '_ ` _ \ | |_ / _ \| '_ ` _ \
| | | | | || _|| __/| | | | | |
|_| |_| |_||_| \___||_| |_| |_|
http://mfem.org
This directory contains modifications of the example codes that illustrate the
use of MFEM features based on the EPIC suite of time integration.
To build these examples, make sure that MFEM is configured with the option
"MFEM_USE_EPIC = YES".
We recommend comparing the original example codes with the corresponding files
in the current directory.
-610
View File
@@ -1,610 +0,0 @@
// MFEM Example 16
// EPIC Modification
//
// Compile with: make ex16
//
// Sample runs: ex16
// ex16 -m ../../data/inline-tri.mesh
// ex16 -m ../../data/disc-nurbs.mesh -tf 2
// ex16 -s 8 -a 1.0 -k 0.0 -dt 1e-4 -tf 5e-2 -vs 25
// ex16 -m ../../data/fichera-q2.mesh
// ex16 -m ../../data/escher.mesh
// ex16 -m ../../data/beam-tet.mesh -tf 10 -dt 0.1
// ex16 -m ../../data/amr-quad.mesh -o 4 -r 0
// ex16 -m ../../data/amr-hex.mesh -o 2 -r 0
//
// Description: This example solves a time dependent nonlinear heat equation
// problem of the form du/dt = C(u), with a non-linear diffusion
// operator C(u) = \nabla \cdot (\kappa + \alpha u) \nabla u.
//
// We recommend viewing examples 2, 9 and 10 before viewing this
// example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
class ImplicitSolveOperator;
class JacobianOperator;
/** After spatial discretization, the conduction model can be written as:
*
* du/dt = M^{-1}(-K(u) u)
*
* where u is the vector representing the temperature, M is the mass matrix,
* and K is the diffusion operator with diffusivity depending on u:
* (\kappa + \alpha u).
*
* Class ConductionOperator represents the right-hand side of the above ODE.
*/
class ConductionOperator : public TimeDependentOperator
{
protected:
FiniteElementSpace &fespace;
Array<int> ess_tdof_list; // this list remains empty for pure Neumann b.c.
BilinearForm *M;
mutable BilinearForm *K;
mutable BilinearForm *dK;
mutable BilinearForm *J_K;
SparseMatrix Mmat;
mutable SparseMatrix J_K_mat;
mutable CGSolver M_solver; // Krylov solver for inverting the mass matrix M
DSmoother M_prec; // Preconditioner for the mass matrix M
CGSolver Jg_solver; // Krylov solver for inverting the Jacobian in the nonlinear solve
DSmoother Jg_prec; // Preconditioner for the Jacobian Jg
NewtonSolver newton_solver;
mutable JacobianOperator *jac;
double alpha, kappa;
mutable Vector z; // auxiliary vector
mutable int nRhsMult, nSetJac, nJacMult, nImpSolve, nImpIter, nImpMult, nImpSet;
public:
Vector u0;
ConductionOperator(FiniteElementSpace &f, double alpha, double kappa, const Vector &u);
void UpdateStats();
void PrintStats(ostream& out);
void ExtractJacobians(const Vector& x, std::ostream &out, std::ostream &out2);
BilinearForm& GetKLambda(const Vector& u) const;
BilinearForm& GetdKLambda(const Vector& u) const;
virtual void Mult(const Vector &u, Vector &du_dt) const;
virtual Operator& GetGradient(const Vector &k) const;
virtual void ImplicitSolve(const double dt, const Vector &x, Vector &k);
virtual ~ConductionOperator();
};
class ImplicitSolveOperator : public Operator
{
private:
double dt;
const Vector* x;
ConductionOperator* oper;
const SparseMatrix* M;
mutable SparseMatrix* Jg;
mutable Vector u, z;
mutable int nMult, nSet;
public:
ImplicitSolveOperator(ConductionOperator* oper, const SparseMatrix* M, double dt, const Vector* x);
int GetnMult() { return nMult; }
int GetnSet() { return nSet; }
virtual void Mult(const Vector &k, Vector &gk) const;
virtual Operator &GetGradient(const Vector &k) const;
};
class JacobianOperator : public Operator
{
private:
Operator* J;
Operator* M_solver;
mutable int nMult;
mutable Vector z;
public:
JacobianOperator(Operator* J, Operator* M_solver);
int GetnMult() { return nMult; }
void ExtractJacobian(const Vector& x, std::ostream &out);
virtual void Mult(const Vector &k, Vector &gk) const;
};
double InitialTemperature(const Vector &x);
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../../data/star.mesh";
int ref_levels = 2;
int order = 2;
int ode_solver_type = 8; // Exponential Euler
double t_final = 0.5;
double dt = 1.0e-2;
double alpha = 1.0e-2;
double kappa = 0.5;
bool visualization = true;
bool visit = false;
int vis_steps = 5;
int precision = 8;
cout.precision(precision);
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&ref_levels, "-r", "--refine",
"Number of times to refine the mesh uniformly.");
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver:\n\t"
"1 - Forward Euler,\n\t"
"2 - RK2,\n\t"
"3 - RK3 SSP,\n\t"
"4 - RK4,\n\t"
"5 - Backward Euler,\n\t"
"6 - SDIRK 2,\n\t"
"7 - SDIRK 3,\n\t"
"8 - EPIC (exponential euler)\n\t");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
"Time step.");
args.AddOption(&alpha, "-a", "--alpha",
"Alpha coefficient.");
args.AddOption(&kappa, "-k", "--kappa",
"Kappa coefficient offset.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
"--no-visit-datafiles",
"Save data files for VisIt (visit.llnl.gov) visualization.");
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
"Visualize every n-th timestep.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
if (ode_solver_type < 1 || ode_solver_type > 9)
{
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
return 3;
}
args.PrintOptions(cout);
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral and hexahedral meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 3. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
// command-line parameter.
for (int lev = 0; lev < ref_levels; lev++)
{
mesh->UniformRefinement();
}
// 4. Define the vector finite element space representing the current and the
// initial temperature, u_ref.
H1_FECollection fe_coll(order, dim);
FiniteElementSpace fespace(mesh, &fe_coll);
int fe_size = fespace.GetTrueVSize();
cout << "Number of temperature unknowns: " << fe_size << endl;
GridFunction u_gf(&fespace);
// 5. Set the initial conditions for u. All boundaries are considered
// natural.
FunctionCoefficient u_0(InitialTemperature);
u_gf.ProjectCoefficient(u_0);
Vector u;
u_gf.GetTrueDofs(u);
// 6. Initialize the conduction operator and the visualization.
ConductionOperator oper(fespace, alpha, kappa, u);
u_gf.SetFromTrueDofs(u);
{
ofstream omesh("ex16.mesh");
omesh.precision(precision);
mesh->Print(omesh);
ofstream osol("ex16-init.gf");
osol.precision(precision);
u_gf.Save(osol);
}
VisItDataCollection visit_dc("Example16", mesh);
visit_dc.RegisterField("temperature", &u_gf);
if (visit)
{
visit_dc.SetCycle(0);
visit_dc.SetTime(0.0);
visit_dc.Save();
}
socketstream sout;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
sout.open(vishost, visport);
if (!sout)
{
cout << "Unable to connect to GLVis server at "
<< vishost << ':' << visport << endl;
visualization = false;
cout << "GLVis visualization disabled.\n";
}
else
{
sout.precision(precision);
sout << "solution\n" << *mesh << u_gf;
sout << "pause\n";
sout << flush;
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
}
}
// 7. Define the ODE solver used for time integration.
double t = 0.0;
ODESolver *ode_solver = NULL;
switch (ode_solver_type)
{
// MFEM explicit methods
case 1: ode_solver = new ForwardEulerSolver; break;
case 2: ode_solver = new RK2Solver(0.5); break; // midpoint method
case 3: ode_solver = new RK3SSPSolver; break;
case 4: ode_solver = new RK4Solver; break;
// MFEM implicit L-stable methods
case 5: ode_solver = new BackwardEulerSolver; break;
case 6: ode_solver = new SDIRK23Solver(2); break;
case 7: ode_solver = new SDIRK33Solver; break;
// EPIC
case 8: ode_solver = new EPI2();break;
case 9: ode_solver = new EPIRK4(); break;
}
// Initialize integrators
ode_solver->Init(oper);
// 8. Perform time-integration (looping over the time iterations, ti, with a
// time-step dt).
cout << "Integrating the ODE ..." << endl;
tic_toc.Clear();
tic_toc.Start();
/*ofstream out_jac_an("jacobian_an.txt");
ofstream out_jac_fd("jacobian_fd.txt");
oper.ExtractJacobians(u, out_jac_fd, out_jac_an);*/
bool last_step = false;
int ti;
for (ti = 1; !last_step; ti++)
{
double dt_real = min(dt, t_final - t);
// Note that since we are using the "one-step" mode of the SUNDIALS
// solvers, they will, generally, step over the final time and will not
// explicitly perform the interpolation to t_final as they do in the
// "normal" step mode.
ode_solver->Step(u, t, dt_real);
oper.UpdateStats();
last_step = (t >= t_final - 1e-8*dt);
if (last_step || (ti % vis_steps) == 0) {
cout << "step " << ti << ", t = " << t << endl;
u_gf.SetFromTrueDofs(u);
if (visualization) {
sout << "solution\n" << *mesh << u_gf << flush;
}
if (visit) {
visit_dc.SetCycle(ti);
visit_dc.SetTime(t);
visit_dc.Save();
}
}
}
tic_toc.Stop();
double comp_time = tic_toc.RealTime();
cout << "Done, " << comp_time << "s." << endl;
// 9. Save the final solution. This output can be viewed later using GLVis:
// "glvis -m ex16.mesh -g ex16-final.gf".
{
ofstream osol("ex16-final.gf");
osol.precision(precision);
u_gf.Save(osol);
ofstream ostats("ex16-stats.txt");
ostats << "time " << comp_time << endl;
oper.PrintStats(ostats);
}
// 10. Free the used memory.
delete ode_solver;
delete mesh;
return 0;
}
ConductionOperator::ConductionOperator(FiniteElementSpace &f, double al, double kap, const Vector &u)
: TimeDependentOperator(f.GetTrueVSize(), 0.0), fespace(f), M(NULL), K(NULL), dK(NULL), J_K(NULL), jac(NULL), z(height), u0(height),
nRhsMult(0), nSetJac(0), nJacMult(0), nImpSolve(0), nImpIter(0), nImpMult(0), nImpSet(0)
{
const double rel_tol = 1e-8;
M = new BilinearForm(&fespace);
M->AddDomainIntegrator(new MassIntegrator());
M->Assemble();
M->FormSystemMatrix(ess_tdof_list, Mmat);
M_solver.iterative_mode = false;
M_solver.SetRelTol(rel_tol);
M_solver.SetAbsTol(0.0);
M_solver.SetMaxIter(50);
M_solver.SetPrintLevel(0);
M_solver.SetPreconditioner(M_prec);
M_solver.SetOperator(Mmat);
Jg_solver.SetRelTol(rel_tol);
Jg_solver.SetAbsTol(0.0);
Jg_solver.SetMaxIter(50);
Jg_solver.SetPrintLevel(0);
Jg_solver.SetPreconditioner(Jg_prec);
newton_solver.SetMaxIter(10);
newton_solver.SetRelTol(rel_tol);
newton_solver.SetPrintLevel(-1);
newton_solver.SetSolver(Jg_solver);
newton_solver.SetMaxIter(100);
newton_solver.iterative_mode = false;
alpha = al;
kappa = kap;
}
void ConductionOperator::UpdateStats()
{
if (jac)
{
nJacMult += jac->GetnMult();
}
}
void ConductionOperator::PrintStats(ostream &out)
{
out << "nRhsMult " << nRhsMult << endl
<< "nSetJac " << nSetJac << endl
<< "nJacMult " << nJacMult << endl
<< "nImplicitSolve " << nImpSolve << endl
<< "nImplicitIter " << nImpIter << endl
<< "nImplicitMult " << nImpMult << endl
<< "nImplicitSet " << nImpSet << endl;
}
BilinearForm& ConductionOperator::GetKLambda(const Vector &u) const
{
GridFunction conductivity_gf(&fespace);
conductivity_gf.SetFromTrueDofs(u);
for (int i = 0; i < conductivity_gf.Size(); i++)
{
conductivity_gf(i) = kappa + alpha*conductivity_gf(i);
}
GridFunctionCoefficient conductivity_coeff(&conductivity_gf);
delete K;
K = new BilinearForm(&fespace);
K->AddDomainIntegrator(new DiffusionIntegrator(conductivity_coeff));
K->Assemble();
return *K;
}
BilinearForm& ConductionOperator::GetdKLambda(const Vector &u) const
{
GridFunction conductivity_gf(&fespace);
conductivity_gf.SetFromTrueDofs(u);
for (int i = 0; i < conductivity_gf.Size(); i++)
{
conductivity_gf(i) = kappa + alpha*conductivity_gf(i);
}
// Define diffusion form with conductivity = kappa(u0)
GridFunctionCoefficient conductivity_coeff(&conductivity_gf);
// Define advection form with velocity = grad kappa(u0)
GridFunction neg_cond_gf(conductivity_gf);
neg_cond_gf.Neg();
GradientGridFunctionCoefficient velocity_coeff(&neg_cond_gf);
delete dK;
dK = new BilinearForm(&fespace);
dK->AddDomainIntegrator(new DiffusionIntegrator(conductivity_coeff));
dK->AddDomainIntegrator(new MixedScalarWeakDivergenceIntegrator(velocity_coeff));
dK->Assemble();
return *dK;
}
void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
{
// Compute:
// du_dt = M^{-1}*-K(u)
// for du_dt
GetKLambda(u);
K->Mult(u, z);
z.Neg(); // z = -z
M_solver.Mult(z, du_dt);
nRhsMult++;
}
void ConductionOperator::ImplicitSolve(const double dt, const Vector &x, Vector &k)
{
ImplicitSolveOperator imp_oper(this, &this->Mmat, dt, &x);
newton_solver.SetOperator(imp_oper);
Vector zero; // empty vector is interpreted as zero r.h.s. by NewtonSolver
newton_solver.Mult(zero, k);
MFEM_VERIFY(newton_solver.GetConverged(), "Newton solver did not converge.");
nImpSolve++;
nImpMult += imp_oper.GetnMult();
nImpSet += imp_oper.GetnSet();
nImpIter += newton_solver.GetNumIterations();
}
Operator &ConductionOperator::GetGradient(const Vector &u) const
{
delete jac;
GetdKLambda(u);
jac = new JacobianOperator(dK, &M_solver);
nSetJac++;
return *jac;
}
ConductionOperator::~ConductionOperator()
{
delete M;
delete K;
delete dK;
delete J_K;
delete jac;
}
ImplicitSolveOperator::ImplicitSolveOperator(ConductionOperator *oper_, const SparseMatrix* M_, double dt_, const Vector* x_):
Operator(oper_->Height()), oper(oper_), M(M_), dt(dt_), x(x_), u(height), z(height), Jg(NULL), nMult(0), nSet(0)
{ }
void ImplicitSolveOperator::Mult(const Vector& y, Vector& gy) const
{
// Compute gy = g(y) = My + dt K(lambda(u)) u
// with u = x + dt y
add(*x, dt, y, u);
BilinearForm& K = oper->GetKLambda(u);
K.Mult(u, gy);
M->AddMult(y, gy);
nMult++;
}
Operator& ImplicitSolveOperator::GetGradient(const Vector &k) const
{
add(*x, dt, k, u);
BilinearForm& dK = oper->GetdKLambda(u);
Array<int> ess_tdof_list;
SparseMatrix dK_mat;
dK.FormSystemMatrix(ess_tdof_list, dK_mat);
delete Jg;
Jg = Add(1.0, *M, dt, dK_mat);
nSet++;
return *Jg;
}
JacobianOperator::JacobianOperator(Operator* J_, Operator* M_solver_):
Operator(M_solver_->Height()), J(J_), M_solver(M_solver_), z(height), nMult(0)
{ }
void JacobianOperator::Mult(const Vector &v, Vector &Jv) const
{
Vector temp(v);
J->Mult(v, z);
z.Neg(); // z = -z
M_solver->Mult(z, Jv);
nMult++;
}
void ConductionOperator::ExtractJacobians(const Vector& x, std::ostream &out, std::ostream &out2)
{
int n = x.Size();
Vector e(n);
e = 0.0;
double eps = 1e-8;
Vector fx(n), fx_eps(n), x_eps(n);
Mult(x, fx);
DenseMatrix J(n);
for (int i = 0; i < n; i++)
{
e[i] = 1.0;
add(x, eps, e, x_eps);
Mult(x_eps, fx_eps);
fx_eps -= fx;
fx_eps /= eps;
J.SetCol(i, fx_eps);
e[i] = 0.0;
}
J.PrintMatlab(out);
GetGradient(x);
jac->ExtractJacobian(x, out2);
}
void JacobianOperator::ExtractJacobian(const Vector& x, std::ostream &out)
{
int n = z.Size();
Vector e(n);
e= 0.0;
Vector J_i(n);
DenseMatrix J(n);
for (int i = 0; i < n; i++)
{
e[i] = 1.0;
Mult(e, J_i);
J.SetCol(i, J_i);
e[i] = 0.0;
}
J.PrintMatlab(out);
}
double InitialTemperature(const Vector &x)
{
if (x.Norml2() < 0.5) { return 2.0; }
else { return 1.0; }
}
-494
View File
@@ -1,494 +0,0 @@
// MFEM Example 16 - Parallel Version
// SUNDIALS Modification
//
// Compile with: make ex16p
//
// Sample runs:
// mpirun -np 4 ex16p
// mpirun -np 4 ex16p -m ../../data/inline-tri.mesh
// mpirun -np 4 ex16p -m ../../data/disc-nurbs.mesh -tf 2
// mpirun -np 4 ex16p -s 12 -a 0.0 -k 1.0
// mpirun -np 4 ex16p -s 8 -a 1.0 -k 0.0 -dt 4e-6 -tf 2e-2 -vs 50
// mpirun -np 8 ex16p -s 9 -a 0.5 -k 0.5 -o 4 -dt 8e-6 -tf 2e-2 -vs 50
// mpirun -np 4 ex16p -s 10 -dt 2.0e-4 -tf 4.0e-2
// mpirun -np 16 ex16p -m ../../data/fichera-q2.mesh
// mpirun -np 16 ex16p -m ../../data/escher-p2.mesh
// mpirun -np 8 ex16p -m ../../data/beam-tet.mesh -tf 10 -dt 0.1
// mpirun -np 4 ex16p -m ../../data/amr-quad.mesh -o 4 -rs 0 -rp 0
// mpirun -np 4 ex16p -m ../../data/amr-hex.mesh -o 2 -rs 0 -rp 0
//
// Description: This example solves a time dependent nonlinear heat equation
// problem of the form du/dt = C(u), with a non-linear diffusion
// operator C(u) = \nabla \cdot (\kappa + \alpha u) \nabla u.
//
// We recommend viewing examples 2, 9 and 10 before viewing this
// example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
/** After spatial discretization, the conduction model can be written as:
*
* du/dt = M^{-1}(-Ku)
*
* where u is the vector representing the temperature, M is the mass matrix,
* and K is the diffusion operator with diffusivity depending on u:
* (\kappa + \alpha u).
*
* Class ConductionOperator represents the right-hand side of the above ODE.
*/
class ConductionOperator : public TimeDependentOperator
{
protected:
ParFiniteElementSpace &fespace;
Array<int> ess_tdof_list; // this list remains empty for pure Neumann b.c.
ParBilinearForm *M;
ParBilinearForm *K;
HypreParMatrix Mmat;
HypreParMatrix Kmat;
HypreParMatrix *T; // T = M + dt K
double current_dt;
CGSolver M_solver; // Krylov solver for inverting the mass matrix M
HypreSmoother M_prec; // Preconditioner for the mass matrix M
CGSolver T_solver; // Implicit solver for T = M + dt K
HypreSmoother T_prec; // Preconditioner for the implicit solver
double alpha, kappa;
mutable Vector z; // auxiliary vector
public:
ConductionOperator(ParFiniteElementSpace &f, double alpha, double kappa,
const Vector &u);
virtual void Mult(const Vector &u, Vector &du_dt) const;
/** Solve the Backward-Euler equation: k = f(u + dt*k, t), for the unknown k.
This is the only requirement for high-order SDIRK implicit integration.*/
virtual void ImplicitSolve(const double dt, const Vector &u, Vector &k);
/** Setup the system (M + dt K) x = M b. This method is used by the implicit
SUNDIALS solvers. */
virtual int SUNImplicitSetup(const Vector &x, const Vector &fx,
int jok, int *jcur, double gamma);
/** Solve the system (M + dt K) x = M b. This method is used by the implicit
SUNDIALS solvers. */
virtual int SUNImplicitSolve(const Vector &b, Vector &x, double tol);
/// Update the diffusion BilinearForm K using the given true-dof vector `u`.
void SetParameters(const Vector &u);
virtual ~ConductionOperator();
};
double InitialTemperature(const Vector &x);
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
const char *mesh_file = "../../data/star.mesh";
int ser_ref_levels = 2;
int par_ref_levels = 1;
int order = 2;
int ode_solver_type = 8; // Exponential Euler
double t_final = 0.5;
double dt = 1.0e-2;
double alpha = 1.0e-2;
double kappa = 0.5;
bool visualization = true;
bool visit = false;
int vis_steps = 5;
int precision = 8;
cout.precision(precision);
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
"Number of times to refine the mesh uniformly in parallel.");
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver:\n\t"
"1 - Forward Euler,\n\t"
"2 - RK2,\n\t"
"3 - RK3 SSP,\n\t"
"4 - RK4,\n\t"
"5 - Backward Euler,\n\t"
"6 - SDIRK 2,\n\t"
"7 - SDIRK 3,\n\t"
"8 - Exponential Euler,\n\t");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
"Time step.");
args.AddOption(&alpha, "-a", "--alpha",
"Alpha coefficient.");
args.AddOption(&kappa, "-k", "--kappa",
"Kappa coefficient offset.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
"--no-visit-datafiles",
"Save data files for VisIt (visit.llnl.gov) visualization.");
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
"Visualize every n-th timestep.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// check for vaild ODE solver option
if (ode_solver_type < 1 || ode_solver_type > 8)
{
if (myid == 0)
{
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
}
MPI_Finalize();
return 1;
}
// 3. Read the serial mesh from the given mesh file on all processors. We can
// handle triangular, quadrilateral, tetrahedral and hexahedral meshes
// with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. Refine the mesh in serial to increase the resolution. In this example
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
// a command-line parameter.
for (int lev = 0; lev < ser_ref_levels; lev++)
{
mesh->UniformRefinement();
}
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
for (int lev = 0; lev < par_ref_levels; lev++)
{
pmesh->UniformRefinement();
}
// 6. Define the vector finite element space representing the current and the
// initial temperature, u_ref.
H1_FECollection fe_coll(order, dim);
ParFiniteElementSpace fespace(pmesh, &fe_coll);
int fe_size = fespace.GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of temperature unknowns: " << fe_size << endl;
}
ParGridFunction u_gf(&fespace);
// 7. Set the initial conditions for u. All boundaries are considered
// natural.
FunctionCoefficient u_0(InitialTemperature);
u_gf.ProjectCoefficient(u_0);
Vector u;
u_gf.GetTrueDofs(u);
// 8. Initialize the conduction operator and the VisIt visualization.
ConductionOperator oper(fespace, alpha, kappa, u);
u_gf.SetFromTrueDofs(u);
{
ostringstream mesh_name, sol_name;
mesh_name << "ex16-mesh." << setfill('0') << setw(6) << myid;
sol_name << "ex16-init." << setfill('0') << setw(6) << myid;
ofstream omesh(mesh_name.str().c_str());
omesh.precision(precision);
pmesh->Print(omesh);
ofstream osol(sol_name.str().c_str());
osol.precision(precision);
u_gf.Save(osol);
}
VisItDataCollection visit_dc("Example16-Parallel", pmesh);
visit_dc.RegisterField("temperature", &u_gf);
if (visit)
{
visit_dc.SetCycle(0);
visit_dc.SetTime(0.0);
visit_dc.Save();
}
socketstream sout;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
sout.open(vishost, visport);
sout << "parallel " << num_procs << " " << myid << endl;
int good = sout.good(), all_good;
MPI_Allreduce(&good, &all_good, 1, MPI_INT, MPI_MIN, pmesh->GetComm());
if (!all_good)
{
sout.close();
visualization = false;
if (myid == 0)
{
cout << "Unable to connect to GLVis server at "
<< vishost << ':' << visport << endl;
cout << "GLVis visualization disabled.\n";
}
}
else
{
sout.precision(precision);
sout << "solution\n" << *pmesh << u_gf;
sout << "pause\n";
sout << flush;
if (myid == 0)
{
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
}
}
}
// 9. Define the ODE solver used for time integration.
double t = 0.0;
ODESolver *ode_solver = NULL;
EPICSolver *epic_solver = NULL;
switch (ode_solver_type)
{
// MFEM explicit methods
case 1: ode_solver = new ForwardEulerSolver; break;
case 2: ode_solver = new RK2Solver(0.5); break; // midpoint method
case 3: ode_solver = new RK3SSPSolver; break;
case 4: ode_solver = new RK4Solver; break;
// MFEM implicit L-stable methods
case 5: ode_solver = new BackwardEulerSolver; break;
case 6: ode_solver = new SDIRK23Solver(2); break;
case 7: ode_solver = new SDIRK33Solver; break;
// EPIC
case 8:
epic_solver = new EPICSolver();
epic_solver->Init(oper);
ode_solver = epic_solver;
break;
}
// Initialize MFEM integrators
ode_solver->Init(oper);
// 10. Perform time-integration (looping over the time iterations, ti, with a
// time-step dt).
if (myid == 0)
{
cout << "Integrating the ODE ..." << endl;
}
tic_toc.Clear();
tic_toc.Start();
bool last_step = false;
for (int ti = 1; !last_step; ti++)
{
double dt_real = min(dt, t_final - t);
// Note that since we are using the "one-step" mode of the SUNDIALS
// solvers, they will, generally, step over the final time and will not
// explicitly perform the interpolation to t_final as they do in the
// "normal" step mode.
ode_solver->Step(u, t, dt_real);
last_step = (t >= t_final - 1e-8*dt);
if (last_step || (ti % vis_steps) == 0)
{
if (myid == 0)
{
cout << "step " << ti << ", t = " << t << endl;
}
u_gf.SetFromTrueDofs(u);
if (visualization)
{
sout << "parallel " << num_procs << " " << myid << "\n";
sout << "solution\n" << *pmesh << u_gf << flush;
}
if (visit)
{
visit_dc.SetCycle(ti);
visit_dc.SetTime(t);
visit_dc.Save();
}
}
oper.SetParameters(u);
}
tic_toc.Stop();
if (myid == 0)
{
cout << "Done, " << tic_toc.RealTime() << "s." << endl;
}
// 11. Save the final solution in parallel. This output can be viewed later
// using GLVis: "glvis -np <np> -m ex16-mesh -g ex16-final".
{
ostringstream sol_name;
sol_name << "ex16-final." << setfill('0') << setw(6) << myid;
ofstream osol(sol_name.str().c_str());
osol.precision(precision);
u_gf.Save(osol);
}
// 12. Free the used memory.
delete ode_solver;
delete pmesh;
MPI_Finalize();
return 0;
}
ConductionOperator::ConductionOperator(ParFiniteElementSpace &f, double al,
double kap, const Vector &u)
: TimeDependentOperator(f.GetTrueVSize(), 0.0), fespace(f), M(NULL), K(NULL),
T(NULL),
M_solver(f.GetComm()), T_solver(f.GetComm()), z(height)
{
const double rel_tol = 1e-8;
M = new ParBilinearForm(&fespace);
M->AddDomainIntegrator(new MassIntegrator());
M->Assemble(0); // keep sparsity pattern of M and K the same
M->FormSystemMatrix(ess_tdof_list, Mmat);
M_solver.iterative_mode = false;
M_solver.SetRelTol(rel_tol);
M_solver.SetAbsTol(0.0);
M_solver.SetMaxIter(100);
M_solver.SetPrintLevel(0);
M_prec.SetType(HypreSmoother::Jacobi);
M_solver.SetPreconditioner(M_prec);
M_solver.SetOperator(Mmat);
alpha = al;
kappa = kap;
T_solver.iterative_mode = false;
T_solver.SetRelTol(rel_tol);
T_solver.SetAbsTol(0.0);
T_solver.SetMaxIter(100);
T_solver.SetPrintLevel(0);
T_solver.SetPreconditioner(T_prec);
SetParameters(u);
}
void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
{
// Compute:
// du_dt = M^{-1}*-K(u)
// for du_dt
Kmat.Mult(u, z);
z.Neg(); // z = -z
M_solver.Mult(z, du_dt);
}
void ConductionOperator::ImplicitSolve(const double dt,
const Vector &u, Vector &du_dt)
{
// Solve the equation:
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
// for du_dt
if (T) { delete T; }
T = Add(1.0, Mmat, dt, Kmat);
T_solver.SetOperator(*T);
Kmat.Mult(u, z);
z.Neg();
T_solver.Mult(z, du_dt);
}
int ConductionOperator::SUNImplicitSetup(const Vector &x,
const Vector &fx, int jok, int *jcur,
double gamma)
{
// Setup the ODE Jacobian T = M + gamma K.
if (T) { delete T; }
T = Add(1.0, Mmat, gamma, Kmat);
T_solver.SetOperator(*T);
*jcur = 1;
return (0);
}
int ConductionOperator::SUNImplicitSolve(const Vector &b, Vector &x, double tol)
{
// Solve the system A x = z => (M - gamma K) x = M b.
Mmat.Mult(b, z);
T_solver.Mult(z, x);
return (0);
}
void ConductionOperator::SetParameters(const Vector &u)
{
ParGridFunction u_alpha_gf(&fespace);
u_alpha_gf.SetFromTrueDofs(u);
for (int i = 0; i < u_alpha_gf.Size(); i++)
{
u_alpha_gf(i) = kappa + alpha*u_alpha_gf(i);
}
delete K;
K = new ParBilinearForm(&fespace);
GridFunctionCoefficient u_coeff(&u_alpha_gf);
K->AddDomainIntegrator(new DiffusionIntegrator(u_coeff));
K->Assemble(0); // keep sparsity pattern of M and K the same
K->FormSystemMatrix(ess_tdof_list, Kmat);
}
ConductionOperator::~ConductionOperator()
{
delete T;
delete M;
delete K;
}
double InitialTemperature(const Vector &x)
{
if (x.Norml2() < 0.5)
{
return 2.0;
}
else
{
return 1.0;
}
}
-76
View File
@@ -1,76 +0,0 @@
# Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/epic/,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
# Use the MFEM install directory
# MFEM_INSTALL_DIR = ../../mfem
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES = ex16
PAR_EXAMPLES = ex16p
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
else
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all clean clean-build clean-exec
# Remove built-in rule
%: %.cpp
# Replace the default implicit rule for *.cpp files
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
all: $(EXAMPLES)
ifeq ($(MFEM_USE_EPIC),NO)
$(EXAMPLES):
$(error MFEM is not configured with EPIC)
endif
MFEM_TESTS = EXAMPLES
include $(MFEM_TEST_MK)
# Testing: Parallel vs. serial runs
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
SERIAL_NAME := Serial EPIC example
PARALLEL_NAME := Parallel EPIC example
%-test-par: %
@$(call mfem-test,$<, $(RUN_MPI), $(PARALLEL_NAME))
%-test-seq: %
@$(call mfem-test,$<,, $(SERIAL_NAME))
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
# Generate an error message if the MFEM library is not built and exit
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -f deformed.* velocity.* elastic_energy.*
@rm -f ex16.mesh ex16-mesh.* ex16-init.* ex16-final.* Example16*
-3
View File
@@ -45,9 +45,6 @@ endif
ifeq ($(MFEM_USE_HIOP),YES)
SUBDIRS += hiop
endif
ifeq ($(MFEM_USE_EPIC),YES)
SUBDIRS += epic
endif
ifeq ($(MFEM_USE_PETSC),YES)
SUBDIRS += petsc
endif
+2
View File
@@ -105,6 +105,7 @@ set(SRCS
tmop/tmop_pa_w3.cpp
tmop/tmop_pa_w3_c0.cpp
tmop_tools.cpp
tmop_amr.cpp
gslib.cpp
transfer.cpp
lor.cpp
@@ -164,6 +165,7 @@ set(HDRS
tmop.hpp
tmop/tmop_pa.hpp
tmop_tools.hpp
tmop_amr.hpp
gslib.hpp
transfer.hpp
lor.hpp
+1
View File
@@ -34,6 +34,7 @@
#include "staticcond.hpp"
#include "tmop.hpp"
#include "tmop_tools.hpp"
#include "tmop_amr.hpp"
#include "gslib.hpp"
#include "restriction.hpp"
#include "quadinterpolator.hpp"
+6
View File
@@ -95,6 +95,12 @@ public:
: Vector(data, f->GetVSize())
{ fes = f; fec = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
/** @brief Construct a GridFunction using previously allocated Vector @a base
starting at the given offset, @a base_offset. */
GridFunction(FiniteElementSpace *f, Vector &base, int base_offset = 0)
: Vector(base, base_offset, f->GetVSize())
{ fes = f; fec = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
/// Construct a GridFunction on the given Mesh, using the data from @a input.
/** The content of @a input should be in the format created by the method
Save(). The reconstructed FiniteElementSpace and FiniteElementCollection
+6 -1
View File
@@ -610,7 +610,12 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
{
if (gsl_code[i] == 1) { indl2.Append(i); }
}
if (indl2.Size() == 0) { return; } // no points on element borders
int borderPts = indl2.Size();
#ifdef MFEM_USE_MPI
MPI_Allreduce(MPI_IN_PLACE, &borderPts, 1, MPI_INT, MPI_SUM, gsl_comm->c);
#endif
if (borderPts == 0) { return; } // no points on element borders
Vector field_out_l2(field_out.Size());
VectorGridFunctionCoefficient field_in_dg(&field_in);
+3 -4
View File
@@ -317,10 +317,6 @@ ParallelEliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
void ParBilinearForm::TrueAddMult(const Vector &x, Vector &y, const double a)
const
{
MFEM_VERIFY(interior_face_integs.Size() == 0,
"the case of interior face integrators is not"
" implemented");
if (X.ParFESpace() != pfes)
{
X.SetSpace(pfes);
@@ -334,6 +330,9 @@ const
}
else
{
MFEM_VERIFY(interior_face_integs.Size() == 0,
"the case of interior face integrators is not"
" implemented");
mat->Mult(X, Y);
}
pfes->Dof_TrueDof_Matrix()->MultTranspose(a, Y, 1.0, y);
+5
View File
@@ -65,6 +65,11 @@ public:
ParGridFunction(ParFiniteElementSpace *pf, double *data) :
GridFunction(pf, data), pfes(pf) { }
/** @brief Construct a ParGridFunction using previously allocated Vector
@a base starting at the given offset, @a base_offset. */
ParGridFunction(ParFiniteElementSpace *pf, Vector &base, int base_offset = 0)
: GridFunction(pf, base, base_offset), pfes(pf) { }
/// Construct a ParGridFunction using a GridFunction as external data.
/** The parallel space @a *pf and the space used by @a *gf should match. The
data from @a *gf is used as the local data of the ParGridFunction on each
+4
View File
@@ -33,6 +33,10 @@ ParL2FaceRestriction::ParL2FaceRestriction(const ParFiniteElementSpace &fes,
// If fespace == L2
const ParFiniteElementSpace &pfes =
static_cast<const ParFiniteElementSpace&>(this->fes);
// Ensure the face neighbor data is constructed
pfes.GetParMesh()->ExchangeFaceNbrData();
const FiniteElement *fe = pfes.GetFE(0);
const TensorBasisElement *tfe = dynamic_cast<const TensorBasisElement*>(fe);
MFEM_VERIFY(tfe != NULL &&
+19
View File
@@ -15,6 +15,12 @@
#include "../general/forall.hpp"
#include <climits>
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
#endif
namespace mfem
{
@@ -675,6 +681,19 @@ H1FaceRestriction::H1FaceRestriction(const FiniteElementSpace &fes,
gather_indices(nf*dof)
{
if (nf==0) { return; }
#ifdef MFEM_USE_MPI
// If the underlying finite element space is parallel, ensure the face
// neighbor information is generated.
if (const ParFiniteElementSpace *pfes
= dynamic_cast<const ParFiniteElementSpace*>(&fes))
{
pfes->GetParMesh()->ExchangeFaceNbrData();
}
#endif
// If fespace == H1
const FiniteElement *fe = fes.GetFE(0);
const TensorBasisElement *tfe = dynamic_cast<const TensorBasisElement*>(fe);
+382 -67
View File
@@ -1314,33 +1314,61 @@ static inline void device_copy(double *d_dest, const double *d_src, int size)
} // namespace internal
#ifdef MFEM_USE_MPI
void DiscreteAdaptTC::FinalizeParDiscreteTargetSpec(const ParGridFunction
&tspec_)
void DiscreteAdaptTC::FinalizeParDiscreteTargetSpec(const ParGridFunction &t)
{
MFEM_VERIFY(adapt_eval, "SetAdaptivityEvaluator() has not been called!")
MFEM_VERIFY(ncomp > 0, "No target specifications have been set!");
ParFiniteElementSpace *ptspec_fes = tspec_.ParFESpace();
ParFiniteElementSpace *ptspec_fes = t.ParFESpace();
adapt_eval->SetParMetaInfo(*ptspec_fes->GetParMesh(),
*ptspec_fes->FEColl(), ncomp);
adapt_eval->SetInitialField(*tspec_fes->GetMesh()->GetNodes(), tspec);
adapt_eval->SetInitialField(*ptspec_fes->GetMesh()->GetNodes(), tspec);
tspec_sav = tspec;
delete tspec_fesv;
tspec_fesv = new FiniteElementSpace(tspec_fes->GetMesh(),
tspec_fes->FEColl(), ncomp);
tspec_fesv = new FiniteElementSpace(ptspec_fes->GetMesh(),
ptspec_fes->FEColl(), ncomp);
delete ptspec_fesv;
ptspec_fesv = new ParFiniteElementSpace(ptspec_fes->GetParMesh(),
ptspec_fes->FEColl(), ncomp);
delete tspec_pgf;
tspec_pgf = new ParGridFunction(ptspec_fesv, tspec);
tspec_gf = tspec_pgf;
}
void DiscreteAdaptTC::ParUpdateAfterMeshTopologyChange()
{
ptspec_fesv->Update();
if (tspec_fesv)
{
delete tspec_fesv;
tspec_fesv = new FiniteElementSpace(ptspec_fesv->GetMesh(),
ptspec_fesv->FEColl(), ncomp);
}
tspec_pgf->Update();
tspec_gf = tspec_pgf;
tspec.SetDataAndSize(tspec_pgf->GetData(), tspec_pgf->Size());
tspec_sav = tspec;
adapt_eval->SetParMetaInfo(*ptspec_fesv->GetParMesh(),
*ptspec_fesv->FEColl(), ncomp);
adapt_eval->SetInitialField(*ptspec_fesv->GetMesh()->GetNodes(), tspec);
}
void DiscreteAdaptTC::SetTspecAtIndex(int idx, const ParGridFunction &tspec_)
{
const int vdim = tspec_.FESpace()->GetVDim(),
dof_cnt = tspec_.Size()/vdim;
const int vdim = tspec_.FESpace()->GetVDim(),
ndof = tspec_.FESpace()->GetNDofs();
MFEM_VERIFY(ndof == tspec.Size()/ncomp, "Inconsistency in SetTspecAtIndex.");
const auto tspec__d = tspec_.Read();
auto tspec_d = tspec.ReadWrite();
const int offset = idx*dof_cnt;
internal::device_copy(tspec_d + offset, tspec__d, dof_cnt*vdim);
const int offset = idx*ndof;
internal::device_copy(tspec_d + offset, tspec__d, ndof*vdim);
FinalizeParDiscreteTargetSpec(tspec_);
}
@@ -1360,78 +1388,71 @@ void DiscreteAdaptTC::SetParDiscreteTargetSkew(const ParGridFunction &tspec_)
FinalizeParDiscreteTargetSpec(tspec_);
}
void DiscreteAdaptTC::SetParDiscreteTargetAspectRatio(const ParGridFunction
&tspec_)
void DiscreteAdaptTC::SetParDiscreteTargetAspectRatio(const ParGridFunction &ar)
{
if (aspectratioidx > -1) { SetTspecAtIndex(aspectratioidx, tspec_); return; }
if (aspectratioidx > -1) { SetTspecAtIndex(aspectratioidx, ar); return; }
aspectratioidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeParDiscreteTargetSpec(tspec_);
SetDiscreteTargetBase(ar);
FinalizeParDiscreteTargetSpec(ar);
}
void DiscreteAdaptTC::SetParDiscreteTargetOrientation(const ParGridFunction
&tspec_)
void DiscreteAdaptTC::SetParDiscreteTargetOrientation(const ParGridFunction &o)
{
if (orientationidx > -1) { SetTspecAtIndex(orientationidx, tspec_); return; }
if (orientationidx > -1) { SetTspecAtIndex(orientationidx, o); return; }
orientationidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeParDiscreteTargetSpec(tspec_);
SetDiscreteTargetBase(o);
FinalizeParDiscreteTargetSpec(o);
}
void DiscreteAdaptTC::SetParDiscreteTargetSpec(const ParGridFunction &tspec_)
{
SetParDiscreteTargetSize(tspec_);
FinalizeParDiscreteTargetSpec(tspec_);
}
#endif // MFEM_USE_MPI
void DiscreteAdaptTC::SetDiscreteTargetBase(const GridFunction &tspec_)
{
const int vdim = tspec_.FESpace()->GetVDim(),
dof_cnt = tspec_.Size()/vdim;
const int vdim = tspec_.FESpace()->GetVDim(),
ndof = tspec_.FESpace()->GetNDofs();
ncomp += vdim;
delete tspec_fes;
tspec_fes = new FiniteElementSpace(tspec_.FESpace()->GetMesh(),
tspec_.FESpace()->FEColl(), 1);
// need to append data to tspec
// make a copy of tspec->tspec_temp, increase its size, and
// copy data from tspec_temp -> tspec, then add new entries
Vector tspec_temp = tspec;
tspec.UseDevice(true);
tspec_sav.UseDevice(true);
tspec.SetSize(ncomp*dof_cnt);
tspec.SetSize(ncomp*ndof);
const auto tspec_temp_d = tspec_temp.Read();
auto tspec_d = tspec.ReadWrite();
internal::device_copy(tspec_d, tspec_temp_d, tspec_temp.Size());
const auto tspec__d = tspec_.Read();
const int offset = (ncomp-vdim)*dof_cnt;
internal::device_copy(tspec_d + offset, tspec__d, dof_cnt*vdim);
const int offset = (ncomp-vdim)*ndof;
internal::device_copy(tspec_d + offset, tspec__d, ndof*vdim);
}
void DiscreteAdaptTC::SetTspecAtIndex(int idx, const GridFunction &tspec_)
{
const int vdim = tspec_.FESpace()->GetVDim(),
dof_cnt = tspec_.Size()/vdim;
const int vdim = tspec_.FESpace()->GetVDim(),
ndof = tspec_.FESpace()->GetNDofs();
MFEM_VERIFY(ndof == tspec.Size()/ncomp, "Inconsistency in SetTargetSpec.");
const auto tspec__d = tspec_.Read();
auto tspec_d = tspec.ReadWrite();
const int offset = idx*dof_cnt;
internal::device_copy(tspec_d + offset, tspec__d, dof_cnt*vdim);
FinalizeSerialDiscreteTargetSpec();
const int offset = idx*ndof;
internal::device_copy(tspec_d + offset, tspec__d, ndof*vdim);
FinalizeSerialDiscreteTargetSpec(tspec_);
}
void DiscreteAdaptTC::SetSerialDiscreteTargetSize(const GridFunction &tspec_)
{
if (sizeidx > -1) { SetTspecAtIndex(sizeidx, tspec_); return; }
sizeidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeSerialDiscreteTargetSpec();
FinalizeSerialDiscreteTargetSpec(tspec_);
}
void DiscreteAdaptTC::SetSerialDiscreteTargetSkew(const GridFunction &tspec_)
@@ -1439,32 +1460,31 @@ void DiscreteAdaptTC::SetSerialDiscreteTargetSkew(const GridFunction &tspec_)
if (skewidx > -1) { SetTspecAtIndex(skewidx, tspec_); return; }
skewidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeSerialDiscreteTargetSpec();
FinalizeSerialDiscreteTargetSpec(tspec_);
}
void DiscreteAdaptTC::SetSerialDiscreteTargetAspectRatio(
const GridFunction &tspec_)
void DiscreteAdaptTC::SetSerialDiscreteTargetAspectRatio(const GridFunction &ar)
{
if (aspectratioidx > -1) { SetTspecAtIndex(aspectratioidx, tspec_); return; }
if (aspectratioidx > -1) { SetTspecAtIndex(aspectratioidx, ar); return; }
aspectratioidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeSerialDiscreteTargetSpec();
SetDiscreteTargetBase(ar);
FinalizeSerialDiscreteTargetSpec(ar);
}
void DiscreteAdaptTC::SetSerialDiscreteTargetOrientation(
const GridFunction &tspec_)
void DiscreteAdaptTC::SetSerialDiscreteTargetOrientation(const GridFunction &o)
{
if (orientationidx > -1) { SetTspecAtIndex(orientationidx, tspec_); return; }
if (orientationidx > -1) { SetTspecAtIndex(orientationidx, o); return; }
orientationidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeSerialDiscreteTargetSpec();
SetDiscreteTargetBase(o);
FinalizeSerialDiscreteTargetSpec(o);
}
void DiscreteAdaptTC::FinalizeSerialDiscreteTargetSpec()
void DiscreteAdaptTC::FinalizeSerialDiscreteTargetSpec(const GridFunction &t)
{
MFEM_VERIFY(adapt_eval, "SetAdaptivityEvaluator() has not been called!")
MFEM_VERIFY(ncomp > 0, "No target specifications have been set!");
const FiniteElementSpace *tspec_fes = t.FESpace();
adapt_eval->SetSerialMetaInfo(*tspec_fes->GetMesh(),
*tspec_fes->FEColl(), ncomp);
adapt_eval->SetInitialField(*tspec_fes->GetMesh()->GetNodes(), tspec);
@@ -1474,12 +1494,40 @@ void DiscreteAdaptTC::FinalizeSerialDiscreteTargetSpec()
delete tspec_fesv;
tspec_fesv = new FiniteElementSpace(tspec_fes->GetMesh(),
tspec_fes->FEColl(), ncomp);
delete tspec_gf;
tspec_gf = new GridFunction(tspec_fesv, tspec);
}
void DiscreteAdaptTC::GetDiscreteTargetSpec(GridFunction &tspec_, int idx)
{
if (idx < 0) { return; }
const int ndof = tspec_.FESpace()->GetNDofs(),
vdim = tspec_.FESpace()->GetVDim();
MFEM_VERIFY(ndof == tspec.Size()/ncomp,
"Inconsistency in GetSerialDiscreteTargetSpec.");
for (int i = 0; i < ndof*vdim; i++)
{
tspec_(i) = tspec(i + idx*ndof);
}
}
void DiscreteAdaptTC::UpdateAfterMeshTopologyChange()
{
tspec_fesv->Update();
tspec_gf->Update();
tspec.SetDataAndSize(tspec_gf->GetData(), tspec_gf->Size());
tspec_sav = tspec;
adapt_eval->SetSerialMetaInfo(*tspec_fesv->GetMesh(),
*tspec_fesv->FEColl(), ncomp);
adapt_eval->SetInitialField(*tspec_fesv->GetMesh()->GetNodes(), tspec);
}
void DiscreteAdaptTC::SetSerialDiscreteTargetSpec(const GridFunction &tspec_)
{
SetSerialDiscreteTargetSize(tspec_);
FinalizeSerialDiscreteTargetSpec();
}
@@ -1509,7 +1557,7 @@ void DiscreteAdaptTC::UpdateTargetSpecificationAtNode(const FiniteElement &el,
MFEM_VERIFY(tspec.Size() > 0, "Target specification is not set!");
Array<int> dofs;
tspec_fes->GetElementDofs(T.ElementNo, dofs);
tspec_fesv->GetElementDofs(T.ElementNo, dofs);
const int cnt = tspec.Size()/ncomp; // dofs per scalar-field
for (int i = 0; i < ncomp; i++)
@@ -1524,7 +1572,7 @@ void DiscreteAdaptTC::RestoreTargetSpecificationAtNode(ElementTransformation &T,
MFEM_VERIFY(tspec.Size() > 0, "Target specification is not set!");
Array<int> dofs;
tspec_fes->GetElementDofs(T.ElementNo, dofs);
tspec_fesv->GetElementDofs(T.ElementNo, dofs);
const int cnt = tspec.Size()/ncomp;
for (int i = 0; i < ncomp; i++)
{
@@ -1532,6 +1580,40 @@ void DiscreteAdaptTC::RestoreTargetSpecificationAtNode(ElementTransformation &T,
}
}
void DiscreteAdaptTC::SetTspecFromIntRule(int e_id,
const IntegrationRule &intrule)
{
switch (target_type)
{
case IDEAL_SHAPE_GIVEN_SIZE:
case GIVEN_SHAPE_AND_SIZE:
{
const int ndofs = tspec_fesv->GetFE(e_id)->GetDof(),
ntspec_dofs = ndofs*ncomp;
Vector tspec_vals(ntspec_dofs);
Array<int> dofs;
tspec_fesv->GetElementVDofs(e_id, dofs);
tspec.GetSubVector(dofs, tspec_vals);
DenseMatrix tr;
tspec_gf->GetVectorValues(e_id, intrule, tspec_refine, tr);
tspec_refine.Transpose();
break;
}
default:
MFEM_ABORT("Incompatible target type for discrete adaptation!");
}
}
void DiscreteAdaptTC::SetTspecDataForDerefinement(FiniteElementSpace *fes)
{
coarse_tspec_fesv = fes;
const Operator *c_op = fes->GetUpdateOperator();
tspec_derefine.SetSize(c_op->Height());
c_op->Mult(tspec, tspec_derefine);
}
void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
@@ -1542,6 +1624,8 @@ void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
nqp = ir.GetNPoints();
Jtrcomp.SetSize(dim, dim, 4*nqp);
FiniteElementSpace *src_fes = tspec_fesv;
switch (target_type)
{
case IDEAL_SHAPE_GIVEN_SIZE:
@@ -1550,7 +1634,7 @@ void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
const DenseMatrix &Wideal =
Geometries.GetGeomToPerfGeomJac(fe.GetGeomType());
const int dim = Wideal.Height(),
ndofs = tspec_fes->GetFE(e_id)->GetDof(),
ndofs = tspec_fesv->GetFE(e_id)->GetDof(),
ntspec_dofs = ndofs*ncomp;
Vector shape(ndofs), tspec_vals(ntspec_dofs), par_vals,
@@ -1561,11 +1645,29 @@ void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
tspec_fesv->GetElementVDofs(e_id, dofs);
tspec.UseDevice(true);
tspec.GetSubVector(dofs, tspec_vals);
if (tspec_refine.NumCols() > 0) // Refinement
{
MFEM_VERIFY(amr_el >= 0, " Target being constructed for an AMR element.");
for (int i = 0; i < ncomp; i++)
{
for (int j = 0; j < ndofs; j++)
{
tspec_vals(j + i*ndofs) = tspec_refine(j + amr_el*ndofs, i);
}
}
}
else if (tspec_derefine.Size() > 0) // Derefinement
{
dofs.SetSize(0);
coarse_tspec_fesv->GetElementVDofs(e_id, dofs);
tspec_derefine.GetSubVector(dofs, tspec_vals);
src_fes = coarse_tspec_fesv;
}
for (int q = 0; q < nqp; q++)
{
const IntegrationPoint &ip = ir.IntPoint(q);
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
src_fes->GetFE(e_id)->CalcShape(ip, shape);
Jtr(q) = Wideal; // Initialize to identity
for (int d = 0; d < 4; d++)
{
@@ -1576,9 +1678,16 @@ void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
if (sizeidx != -1) // Set size
{
par_vals.SetDataAndSize(tspec_vals.GetData()+sizeidx*ndofs, ndofs);
const double min_size = par_vals.Min();
MFEM_VERIFY(min_size > 0.0,
"Non-positive size propagated in the target definition.");
double min_size = par_vals.Min();//0.001; //
if (lim_min_size > 0.)
{
min_size = lim_min_size;
}
else
{
MFEM_VERIFY(min_size > 0.0,
"Non-positive size propagated in the target definition.");
}
const double size = std::max(shape * par_vals, min_size);
Jtr(q).Set(std::pow(size, 1.0/dim), Jtr(q));
DenseMatrix Jtrcomp_q(Jtrcomp.GetData(0 + 4*q), dim, dim);
@@ -1593,6 +1702,9 @@ void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
{
par_vals.SetDataAndSize(tspec_vals.GetData()+
aspectratioidx*ndofs, ndofs);
const double min_size = par_vals.Min();
MFEM_VERIFY(min_size > 0.0,
"Non-positive aspect-ratio propagated in the target definition.");
const double aspectratio = shape * par_vals;
D_rho = 0.;
@@ -1777,7 +1889,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
grad_phys.Mult(par_vals, grad_ptr_c1);
Vector grad_q(dim);
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
grad_e_c1.MultTranspose(shape, grad_q);
const double min_size = par_vals.Min();
@@ -1810,7 +1922,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
grad_phys.Mult(par_vals, grad_ptr_c1);
Vector grad_q(dim);
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
grad_e_c1.MultTranspose(shape, grad_q);
const double aspectratio = shape * par_vals;
@@ -1841,7 +1953,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
grad_phys.Mult(par_vals_c2, grad_ptr_c2);
grad_phys.Mult(par_vals_c3, grad_ptr_c3);
Vector grad_q1(dim), grad_q2(dim), grad_q3(dim);
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
grad_e_c1.MultTranspose(shape, grad_q1);
grad_e_c2.MultTranspose(shape, grad_q2);
grad_e_c3.MultTranspose(shape, grad_q3);
@@ -1880,7 +1992,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
grad_phys.Mult(par_vals, grad_ptr_c1);
Vector grad_q(dim);
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
grad_e_c1.MultTranspose(shape, grad_q);
const double skew = shape * par_vals;
@@ -1913,7 +2025,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
grad_phys.Mult(par_vals_c2, grad_ptr_c2);
grad_phys.Mult(par_vals_c3, grad_ptr_c3);
Vector grad_q1(dim), grad_q2(dim), grad_q3(dim);
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
grad_e_c1.MultTranspose(shape, grad_q1);
grad_e_c2.MultTranspose(shape, grad_q2);
grad_e_c3.MultTranspose(shape, grad_q3);
@@ -1960,7 +2072,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
grad_phys.Mult(par_vals, grad_ptr_c1);
Vector grad_q(dim);
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
grad_e_c1.MultTranspose(shape, grad_q);
const double theta = shape * par_vals;
@@ -1991,7 +2103,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
grad_phys.Mult(par_vals_c2, grad_ptr_c2);
grad_phys.Mult(par_vals_c3, grad_ptr_c3);
Vector grad_q1(dim), grad_q2(dim), grad_q3(dim);
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
grad_e_c1.MultTranspose(shape, grad_q1);
grad_e_c2.MultTranspose(shape, grad_q2);
grad_e_c3.MultTranspose(shape, grad_q3);
@@ -2071,7 +2183,7 @@ void DiscreteAdaptTC::UpdateGradientTargetSpecification(const Vector &x,
{
if (use_flag && good_tspec_grad) { return; }
const int dim = tspec_fes->GetFE(0)->GetDim(),
const int dim = tspec_fesv->GetFE(0)->GetDim(),
cnt = x.Size()/dim;
tspec_pert1h.SetSize(x.Size()*ncomp);
@@ -2097,7 +2209,7 @@ void DiscreteAdaptTC::UpdateHessianTargetSpecification(const Vector &x,
if (use_flag && good_tspec_hess) { return; }
const int dim = tspec_fes->GetFE(0)->GetDim(),
const int dim = tspec_fesv->GetFE(0)->GetDim(),
cnt = x.Size()/dim,
totmix = 1+2*(dim-2);
@@ -2145,6 +2257,16 @@ void DiscreteAdaptTC::UpdateHessianTargetSpecification(const Vector &x,
good_tspec_hess = use_flag;
}
DiscreteAdaptTC::~DiscreteAdaptTC()
{
delete tspec_gf;
delete adapt_eval;
delete tspec_fesv;
#ifdef MFEM_USE_MPI
delete ptspec_fesv;
#endif
}
void AdaptivityEvaluator::SetSerialMetaInfo(const Mesh &m,
const FiniteElementCollection &fec,
int num_comp)
@@ -2258,6 +2380,7 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
AdaptivityEvaluator &ae)
{
zeta_0 = &z0;
pzeta_0 = &z0;
delete zeta;
zeta = new GridFunction(z0);
coeff_zeta = &coeff;
@@ -2270,6 +2393,33 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
}
#endif
void TMOP_Integrator::UpdateAfterMeshTopologyChange()
{
if (zeta)
{
zeta->Update();
adapt_eval->SetSerialMetaInfo(*zeta->FESpace()->GetMesh(),
*zeta->FESpace()->FEColl(), 1);
adapt_eval->SetInitialField
(*zeta->FESpace()->GetMesh()->GetNodes(), *zeta);
}
}
#ifdef MFEM_USE_MPI
void TMOP_Integrator::ParUpdateAfterMeshTopologyChange()
{
if (zeta)
{
zeta->Update();
adapt_eval->SetParMetaInfo(*pzeta_0->ParFESpace()->GetParMesh(),
*pzeta_0->ParFESpace()->FEColl(), 1);
adapt_eval->SetInitialField
(*zeta->FESpace()->GetMesh()->GetNodes(), *zeta);
}
}
#endif
double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun)
@@ -2378,6 +2528,145 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
return energy;
}
double TMOP_Integrator::GetRefinementElementEnergy(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun,
const IntegrationRule &irule)
{
int dof = el.GetDof(), dim = el.GetDim(),
NEsplit = elfun.Size() / (dof*dim), el_id = T.ElementNo;
double energy = 0.;
TargetConstructor *tc = const_cast<TargetConstructor *>(targetC);
DiscreteAdaptTC *dtc = dynamic_cast<DiscreteAdaptTC *>(tc);
// For DiscreteAdaptTC the GridFunctions used to set the targets must be
// mapped onto the fine elements.
if (dtc) { dtc->SetTspecFromIntRule(el_id, irule); }
for (int e = 0; e < NEsplit; e++)
{
DSh.SetSize(dof, dim);
Jrt.SetSize(dim);
Jpr.SetSize(dim);
Jpt.SetSize(dim);
Vector elfun_child(dof*dim);
for (int i = 0; i < dof; i++)
{
for (int d = 0; d < dim; d++)
{
// elfun is (xe1,xe2,...xen,ye1,ye2...yen) and has nodal coordinates
// for all the children element of the parent element being considered.
// So we must index and get (xek, yek) i.e. nodal coordinates for
// the fine element being considered.
elfun_child(i + d*dof) = elfun(i + e*dof + d*dof*NEsplit);
}
}
PMatI.UseExternalData(elfun_child.GetData(), dof, dim);
const IntegrationRule &ir = EnergyIntegrationRule(el);
double el_energy = 0;
DenseTensor Jtr(dim, dim, ir.GetNPoints());
if (dtc)
{
// This is used to index into the tspec vector inside DiscreteAdaptTC.
dtc->SetRefinementSubElement(e);
}
targetC->ComputeElementTargets(el_id, el, ir, elfun_child, Jtr);
// Define ref->physical transformation, wn a Coefficient is specified.
IsoparametricTransformation *Tpr = NULL;
if (coeff1 || coeff0)
{
Tpr = new IsoparametricTransformation;
Tpr->SetFE(&el);
Tpr->ElementNo = T.ElementNo;
Tpr->ElementType = ElementTransformation::ELEMENT;
Tpr->Attribute = T.Attribute;
Tpr->GetPointMat().Transpose(PMatI); // PointMat = PMatI^T
}
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
const DenseMatrix &Jtr_i = Jtr(i);
h_metric->SetTargetJacobian(Jtr_i);
CalcInverse(Jtr_i, Jrt);
const double weight = ip.weight * Jtr_i.Det();
el.CalcDShape(ip, DSh);
MultAtB(PMatI, DSh, Jpr);
Mult(Jpr, Jrt, Jpt);
double val = metric_normal * h_metric->EvalW(Jpt);
if (coeff1) { val *= coeff1->Eval(*Tpr, ip); }
el_energy += weight * val;
delete Tpr;
}
energy += el_energy;
}
energy /= NEsplit;
if (dtc) { dtc->ResetRefinementTspecData(); }
return energy;
}
double TMOP_Integrator::GetDerefinementElementEnergy(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun)
{
int dof = el.GetDof(), dim = el.GetDim();
double energy = 0.;
DSh.SetSize(dof, dim);
Jrt.SetSize(dim);
Jpr.SetSize(dim);
Jpt.SetSize(dim);
PMatI.UseExternalData(elfun.GetData(), dof, dim);
const IntegrationRule &ir = EnergyIntegrationRule(el);
energy = 0.0;
DenseTensor Jtr(dim, dim, ir.GetNPoints());
targetC->ComputeElementTargets(T.ElementNo, el, ir, elfun, Jtr);
// Define ref->physical transformation, wn a Coefficient is specified.
IsoparametricTransformation *Tpr = NULL;
if (coeff1)
{
Tpr = new IsoparametricTransformation;
Tpr->SetFE(&el);
Tpr->ElementNo = T.ElementNo;
Tpr->ElementType = ElementTransformation::ELEMENT;
Tpr->Attribute = T.Attribute;
Tpr->GetPointMat().Transpose(PMatI); // PointMat = PMatI^T
}
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
const DenseMatrix &Jtr_i = Jtr(i);
h_metric->SetTargetJacobian(Jtr_i);
CalcInverse(Jtr_i, Jrt);
const double weight = ip.weight * Jtr_i.Det();
el.CalcDShape(ip, DSh);
MultAtB(PMatI, DSh, Jpr);
Mult(Jpr, Jrt, Jpt);
double val = metric_normal * h_metric->EvalW(Jpt);
if (coeff1) { val *= coeff1->Eval(*Tpr, ip); }
energy += weight * val;
}
delete Tpr;
return energy;
}
void TMOP_Integrator::AssembleElementVector(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun, Vector &elvect)
@@ -3039,7 +3328,7 @@ void TMOP_Integrator::ComputeMinJac(const Vector &x,
dx = detv_avg_min / dxscale;
}
void TMOP_Integrator::UpdateAfterMeshChange(const Vector &new_x)
void TMOP_Integrator::UpdateAfterMeshPositionChange(const Vector &new_x)
{
if (discr_tc)
{
@@ -3168,6 +3457,32 @@ void TMOPComboIntegrator::AssembleElementGrad(const FiniteElement &el,
}
}
double TMOPComboIntegrator::GetRefinementElementEnergy(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun,
const IntegrationRule &irule)
{
double energy= 0.0;
for (int i = 0; i < tmopi.Size(); i++)
{
energy += tmopi[i]->GetRefinementElementEnergy(el, T, elfun, irule);
}
return energy;
}
double TMOPComboIntegrator::GetDerefinementElementEnergy(
const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun)
{
double energy= 0.0;
for (int i = 0; i < tmopi.Size(); i++)
{
energy += tmopi[i]->GetDerefinementElementEnergy(el, T, elfun);
}
return energy;
}
void TMOPComboIntegrator::EnableNormalization(const GridFunction &x)
{
const int cnt = tmopi.Size();
+118 -16
View File
@@ -1057,14 +1057,31 @@ protected:
// eta1(x+h,y), eta2(x+h,y) ... etan(x+h,y), eta1(x,y+h), eta2(x,y+h) ...
// same for tspec_pert2h and tspec_pertmix.
// DenseMatrix to hold target_spec values for the (children of the)
// element being refined to consider for h-refinement.
DenseMatrix tspec_refine;
// Vector to hold the target_spec values for the coarse version of the
// current mesh. Used for derefinement decision with hr-adaptivity.
Vector tspec_derefine;
// Components of Target Jacobian at each quadrature point of an element. This
// is required for computation of the derivative using chain rule.
mutable DenseTensor Jtrcomp;
// Note: do not use the Nodes of this space as they may not be on the
// positions corresponding to the values of tspec.
const FiniteElementSpace *tspec_fes;
const FiniteElementSpace *tspec_fesv;
FiniteElementSpace *tspec_fesv; //owned
FiniteElementSpace *coarse_tspec_fesv; //not owned, derefinement FESpace
GridFunction *tspec_gf; //owned, uses tspec and tspec_fes
// discrete adaptivity
#ifdef MFEM_USE_MPI
ParFiniteElementSpace *ptspec_fesv; //owned, needed for derefinement to
// get update operator.
ParGridFunction *tspec_pgf; // similar to tspec_gf
#endif
int amr_el;
double lim_min_size;
// These flags can be used by outside functions to avoid recomputing the
// tspec and tspec_perth fields again on the same mesh.
@@ -1076,7 +1093,7 @@ protected:
void SetDiscreteTargetBase(const GridFunction &tspec_);
void SetTspecAtIndex(int idx, const GridFunction &tspec_);
void FinalizeSerialDiscreteTargetSpec();
void FinalizeSerialDiscreteTargetSpec(const GridFunction &tspec_);
#ifdef MFEM_USE_MPI
void SetTspecAtIndex(int idx, const ParGridFunction &tspec_);
void FinalizeParDiscreteTargetSpec(const ParGridFunction &tspec_);
@@ -1088,16 +1105,16 @@ public:
ncomp(0),
sizeidx(-1), skewidx(-1), aspectratioidx(-1), orientationidx(-1),
tspec(), tspec_sav(), tspec_pert1h(), tspec_pert2h(), tspec_pertmix(),
tspec_fes(NULL), tspec_fesv(NULL),
tspec_refine(), tspec_derefine(),
tspec_fesv(NULL), coarse_tspec_fesv(NULL), tspec_gf(NULL),
#ifdef MFEM_USE_MPI
ptspec_fesv(NULL), tspec_pgf(NULL),
#endif
amr_el(-1), lim_min_size(-0.1),
good_tspec(false), good_tspec_grad(false), good_tspec_hess(false),
adapt_eval(NULL) { }
virtual ~DiscreteAdaptTC()
{
delete adapt_eval;
delete tspec_fes;
delete tspec_fesv;
}
virtual ~DiscreteAdaptTC();
/** @name Target specification methods.
The following methods are used to specify geometric parameters of the
@@ -1128,6 +1145,20 @@ public:
void ResetUpdateFlags()
{ good_tspec = good_tspec_grad = good_tspec_hess = false; }
/// Get one of the discrete fields from tspec.
void GetDiscreteTargetSpec(GridFunction &tspec_, int idx);
/// Get the FESpace associated with tspec.
FiniteElementSpace *GetTSpecFESpace() { return tspec_fesv; }
/// Get the entire tspec.
GridFunction *GetTSpecData() { return tspec_gf; }
/// Update all discrete fields based on tspec and update for AMR
void UpdateAfterMeshTopologyChange();
#ifdef MFEM_USE_MPI
ParFiniteElementSpace *GetTSpecParFESpace() { return ptspec_fesv; }
void ParUpdateAfterMeshTopologyChange();
#endif
/** Used to update the target specification after the mesh has changed. The
new mesh positions are given by new_x. If @a use_flags is true, repeated
calls won't do anything until ResetUpdateFlags() is called. */
@@ -1184,6 +1215,36 @@ public:
const Vector &elfun,
IsoparametricTransformation &Tpr,
DenseTensor &dJtr) const;
// Generates tspec_vals for target construction using intrule
// Used for the refinement component in hr-adaptivity.
void SetTspecFromIntRule(int e_id, const IntegrationRule &intrule);
// Targets based on discrete functions can result in invalid (negative)
// size at the quadrature points. This method can be used to set a
// minimum target size.
void SetMinSizeForTargets(double min_size_) { lim_min_size = min_size_; }
/// Computes target specification data with respect to the coarse FE space.
void SetTspecDataForDerefinement(FiniteElementSpace *fes);
// Reset refinement data associated with h-adaptivity component.
void ResetRefinementTspecData()
{
tspec_refine.Clear();
amr_el = -1;
}
// Reset derefinement data associated with h-adaptivity component.
void ResetDerefinementTspecData()
{
tspec_derefine.Destroy();
coarse_tspec_fesv = NULL;
}
// Used to specify the fine element for determining energy of children of a
// parent element.
void SetRefinementSubElement(int amr_el_) { amr_el = amr_el_; }
};
class TMOPNewtonSolver;
@@ -1201,6 +1262,7 @@ protected:
friend class TMOPNewtonSolver;
friend class TMOPComboIntegrator;
TMOP_QualityMetric *h_metric;
TMOP_QualityMetric *metric; // not owned
const TargetConstructor *targetC; // not owned
@@ -1227,6 +1289,9 @@ protected:
// Adaptive limiting.
const GridFunction *zeta_0; // Not owned.
#ifdef MFEM_USE_MPI
const ParGridFunction *pzeta_0;
#endif
GridFunction *zeta; // Owned. Updated by adapt_eval.
Coefficient *coeff_zeta; // Not owned.
AdaptivityEvaluator *adapt_eval; // Not owned.
@@ -1337,7 +1402,7 @@ protected:
#endif
void ComputeMinJac(const Vector &x, const FiniteElementSpace &fes);
void UpdateAfterMeshChange(const Vector &new_x);
void UpdateAfterMeshPositionChange(const Vector &new_x);
void DisableLimiting()
{
@@ -1395,11 +1460,13 @@ protected:
void ComputeAllElementTargets(const Vector &xe = Vector()) const;
public:
/** @param[in] m TMOP_QualityMetric that will be integrated (not owned).
@param[in] tc Target-matrix construction algorithm to use (not owned). */
TMOP_Integrator(TMOP_QualityMetric *m, TargetConstructor *tc)
: metric(m), targetC(tc), IntegRules(NULL), integ_order(-1),
coeff1(NULL), metric_normal(1.0),
/** @param[in] m TMOP_QualityMetric for r-adaptivity (not owned).
@param[in] tc Target-matrix construction algorithm to use (not owned).
@param[in] hm TMOP_QualityMetric for h-adaptivity (not owned). */
TMOP_Integrator(TMOP_QualityMetric *m, TargetConstructor *tc,
TMOP_QualityMetric *hm)
: h_metric(hm), metric(m), targetC(tc), IntegRules(NULL),
integ_order(-1), coeff1(NULL), metric_normal(1.0),
nodes0(NULL), coeff0(NULL),
lim_dist(NULL), lim_func(NULL), lim_normal(1.0),
zeta_0(NULL), zeta(NULL), coeff_zeta(NULL), adapt_eval(NULL),
@@ -1407,6 +1474,9 @@ public:
fdflag(false), dxscale(1.0e3), fd_call_flag(false), exact_action(false)
{ PA.enabled = false; }
TMOP_Integrator(TMOP_QualityMetric *m, TargetConstructor *tc)
: TMOP_Integrator(m, tc, m) { }
~TMOP_Integrator();
/// Release the device memory of large PA allocations. This will copy device
@@ -1478,6 +1548,22 @@ public:
ElementTransformation &T,
const Vector &elfun);
/** @brief Computes the mean of the energies of the given element's children.
In addition to the inputs for GetElementEnergy, this function requires an
IntegrationRule to be specified that will give the decomposition of the
given element based on the refinement type being considered. */
virtual double GetRefinementElementEnergy(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun,
const IntegrationRule &irule);
/// This function is similar to GetElementEnergy, but ignores components
/// such as limiting etc. to compute the element energy.
virtual double GetDerefinementElementEnergy(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun);
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun, Vector &elvect);
@@ -1486,6 +1572,13 @@ public:
ElementTransformation &T,
const Vector &elfun, DenseMatrix &elmat);
TMOP_QualityMetric &GetAMRQualityMetric() { return *h_metric; }
void UpdateAfterMeshTopologyChange();
#ifdef MFEM_USE_MPI
void ParUpdateAfterMeshTopologyChange();
#endif
// PA extension
using NonlinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace&);
@@ -1564,6 +1657,15 @@ public:
ElementTransformation &T,
const Vector &elfun, DenseMatrix &elmat);
virtual double GetRefinementElementEnergy(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun,
const IntegrationRule &irule);
virtual double GetDerefinementElementEnergy(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun);
/// Normalization factor that considers all integrators in the combination.
void EnableNormalization(const GridFunction &x);
#ifdef MFEM_USE_MPI
+896
View File
@@ -0,0 +1,896 @@
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "tmop_amr.hpp"
namespace mfem
{
using namespace mfem;
void TMOPRefinerEstimator::ComputeEstimates()
{
bool iso = false;
bool aniso = false;
if (amrmetric == 1 || amrmetric == 2 || amrmetric == 58)
{
aniso = true;
}
if (amrmetric == 55 || amrmetric == 56 || amrmetric == 77 ||
amrmetric == 315 || amrmetric == 316 || amrmetric == 321)
{
iso = true;
}
if (amrmetric == 7 || amrmetric == 9)
{
iso = true; aniso = true;
}
MFEM_VERIFY(iso || aniso, "Metric type not supported in hr-adaptivity.");
const int dim = mesh->Dimension();
const int num_ref_types = 3 + 4*(dim-2);
const int NEorig = mesh->GetNE();
aniso_flags.SetSize(NEorig);
error_estimates.SetSize(NEorig);
Vector amr_base_energy(NEorig), amr_temp_energy(NEorig);
error_estimates = 1.*std::numeric_limits<float>::max();
aniso_flags = -1;
GetTMOPRefinementEnergy(0, amr_base_energy);
for (int i = 1; i < num_ref_types+1; i++)
{
if ( dim == 2 && i < 3 && aniso != true ) { continue; }
if ( dim == 2 && i == 3 && iso != true ) { continue; }
if ( dim == 3 && i < 7 && aniso != true ) { continue; }
if ( dim == 3 && i == 7 && iso != true ) { continue; }
GetTMOPRefinementEnergy(i, amr_temp_energy);
for (int e = 0; e < NEorig; e++)
{
if ( amr_temp_energy(e) < error_estimates(e) )
{
error_estimates(e) = amr_temp_energy(e);
aniso_flags[e] = i;
}
}
}
error_estimates *= energy_scaling_factor;
if (spat_gf)
{
L2_FECollection avg_fec(0, mesh->Dimension());
FiniteElementSpace avg_fes(spat_gf->FESpace()->GetMesh(), &avg_fec);
GridFunction elem_avg(&avg_fes);
spat_gf->GetElementAverages(elem_avg);
for (int i = 0; i < amr_base_energy.Size(); i++)
{
if (elem_avg(i) < spat_gf_critical) { amr_base_energy(i) = 0.; }
}
}
error_estimates -= amr_base_energy;
error_estimates *= -1; // error = E(parent) - scaling_factor*mean(E(children))
current_sequence = mesh->GetSequence();
}
void TMOPRefinerEstimator::GetTMOPRefinementEnergy(int reftype,
Vector &el_energy_vec)
{
const FiniteElementSpace *fes = mesh->GetNodalFESpace();
const int NE = fes->GetNE();
GridFunction *xdof = mesh->GetNodes();
xdof->SetTrueVector();
xdof->SetFromTrueVector();
el_energy_vec.SetSize(NE);
el_energy_vec = std::numeric_limits<float>::max();
for (int e = 0; e < NE; e++)
{
Geometry::Type gtype = fes->GetFE(e)->GetGeomType();
DenseMatrix tr, xsplit;
IntegrationRule *irule = NULL;
if ( (gtype == Geometry::TRIANGLE && reftype > 0 && reftype < 3) ||
(gtype == Geometry::CUBE && reftype > 0 && reftype < 7) ||
(gtype == Geometry::TETRAHEDRON && reftype > 0 && reftype < 7) )
{
continue;
}
switch (gtype)
{
case Geometry::TRIANGLE:
{
int ref_access = reftype == 0 ? 0 : 1;
xdof->GetVectorValues(e, *TriIntRule[ref_access], xsplit, tr);
irule = TriIntRule[ref_access];
break;
}
case Geometry::TETRAHEDRON:
{
int ref_access = reftype == 0 ? 0 : 1;
xdof->GetVectorValues(e, *TetIntRule[ref_access], xsplit, tr);
irule = TetIntRule[ref_access];
break;
}
case Geometry::SQUARE:
{
MFEM_VERIFY(QuadIntRule[reftype], " Integration rule does not exist.");
xdof->GetVectorValues(e, *QuadIntRule[reftype], xsplit, tr);
irule = QuadIntRule[reftype];
break;
}
case Geometry::CUBE:
{
int ref_access = reftype == 0 ? 0 : 1;
xdof->GetVectorValues(e, *HexIntRule[ref_access], xsplit, tr);
irule = HexIntRule[ref_access];
break;
}
default:
MFEM_ABORT("Incompatible geometry type!");
}
xsplit.Transpose();
el_energy_vec(e) = 0.; // Re-set to 0
// The data format is xe1,xe2,..xen,ye1,ye2..yen.
// We will reformat it inside GetRefinementElementEnergy
Vector elfun(xsplit.GetData(), xsplit.NumCols()*xsplit.NumRows());
Array<NonlinearFormIntegrator*> &integs = *(nlf->GetDNFI());
TMOP_Integrator *ti = NULL;
TMOPComboIntegrator *co = NULL;
for (int i = 0; i < integs.Size(); i++)
{
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
if (ti)
{
el_energy_vec(e) = ti->GetRefinementElementEnergy(*fes->GetFE(e),
*mesh->GetElementTransformation(e),
elfun,
*irule);
}
co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
if (co)
{
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
for (int j = 0; j < ati.Size(); j++)
{
el_energy_vec(e) += ati[j]->GetRefinementElementEnergy(*fes->GetFE(e),
*mesh->GetElementTransformation(e),
elfun,
*irule);
}
}
}
}
}
void TMOPRefinerEstimator::SetHexIntRules()
{
HexIntRule.SetSize(1+1);
// Reftype = 0 -> original element
Mesh meshsplit = Mesh::MakeCartesian3D(1, 1, 1, Element::HEXAHEDRON);
Mesh base_mesh_copy(meshsplit);
HexIntRule[0] = SetIntRulesFromMesh(meshsplit);
meshsplit.Clear();
// Reftype = 7
for (int i = 7; i < 8; i++)
{
Array<Refinement> marked_elements;
Mesh mesh_ref(base_mesh_copy);
for (int e = 0; e < mesh_ref.GetNE(); e++)
{
marked_elements.Append(Refinement(e, i));
}
mesh_ref.GeneralRefinement(marked_elements, 1, 0);
HexIntRule[1] = SetIntRulesFromMesh(mesh_ref);
mesh_ref.Clear();
}
}
void TMOPRefinerEstimator::SetQuadIntRules()
{
QuadIntRule.SetSize(3+1);
// Reftype = 0 -> original element
Mesh meshsplit = Mesh::MakeCartesian2D(1, 1, Element::QUADRILATERAL);
Mesh base_mesh_copy(meshsplit);
QuadIntRule[0] = SetIntRulesFromMesh(meshsplit);
meshsplit.Clear();
// Reftype = 1-3
for (int i = 1; i < 4; i++)
{
Array<Refinement> marked_elements;
Mesh mesh_ref(base_mesh_copy);
for (int e = 0; e < mesh_ref.GetNE(); e++)
{
marked_elements.Append(Refinement(e, i));
}
mesh_ref.GeneralRefinement(marked_elements, 1, 0);
QuadIntRule[i] = SetIntRulesFromMesh(mesh_ref);
mesh_ref.Clear();
}
}
void TMOPRefinerEstimator::SetTriIntRules()
{
TriIntRule.SetSize(1+1);
// Reftype = 0 // original element
const int Nvert = 3, NEsplit = 1;
Mesh meshsplit(2, Nvert, NEsplit, 0 ,2);
const double tri_v[3][2] =
{
{0, 0}, {1, 0}, {0, 1}
};
const int tri_e[1][3] =
{
{0, 1, 2}
};
for (int j = 0; j < Nvert; j++)
{
meshsplit.AddVertex(tri_v[j]);
}
meshsplit.AddTriangle(tri_e[0], 1);
meshsplit.FinalizeTriMesh(1, 1, true);
Mesh base_mesh_copy(meshsplit);
TriIntRule[0] = SetIntRulesFromMesh(meshsplit);
meshsplit.Clear();
// no anisotropic refinements for triangle
// Reftype = 3
for (int i = 1; i < 2; i++)
{
Array<Refinement> marked_elements;
Mesh mesh_ref(base_mesh_copy);
for (int e = 0; e < mesh_ref.GetNE(); e++)
{
marked_elements.Append(Refinement(e, i));
}
mesh_ref.GeneralRefinement(marked_elements, 1, 0);
TriIntRule[i] = SetIntRulesFromMesh(mesh_ref);
mesh_ref.Clear();
}
}
void TMOPRefinerEstimator::SetTetIntRules()
{
TetIntRule.SetSize(1+1);
// Reftype = 0 // original element
const int Nvert = 4, NEsplit = 1;
Mesh meshsplit(3, Nvert, NEsplit, 0, 3);
const double tet_v[4][3] =
{
{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}
};
const int tet_e[1][4] =
{
{0, 1, 2, 3}
};
for (int j = 0; j < Nvert; j++)
{
meshsplit.AddVertex(tet_v[j]);
}
meshsplit.AddTet(tet_e[0], 1);
meshsplit.FinalizeTetMesh(1, 1, true);
Mesh base_mesh_copy(meshsplit);
TetIntRule[0] = SetIntRulesFromMesh(meshsplit);
meshsplit.Clear();
// no anisotropic refinements for triangle
// Reftype = 7
for (int i = 1; i < 2; i++)
{
Array<Refinement> marked_elements;
Mesh mesh_ref(base_mesh_copy);
for (int e = 0; e < mesh_ref.GetNE(); e++)
{
marked_elements.Append(Refinement(e, i)); //ref_type will default to 7
}
mesh_ref.GeneralRefinement(marked_elements, 1, 0);
TetIntRule[i] = SetIntRulesFromMesh(mesh_ref);
mesh_ref.Clear();
}
}
IntegrationRule* TMOPRefinerEstimator::SetIntRulesFromMesh(Mesh &meshsplit)
{
const int dim = meshsplit.Dimension();
H1_FECollection fec(order, dim);
FiniteElementSpace nodal_fes(&meshsplit, &fec, dim);
meshsplit.SetNodalFESpace(&nodal_fes);
const int NEsplit = meshsplit.GetNE();
const int dof_cnt = nodal_fes.GetFE(0)->GetDof(),
pts_cnt = NEsplit * dof_cnt;
DenseMatrix pos(dof_cnt, dim);
Vector posV(pos.Data(), dof_cnt * dim);
Array<int> xdofs(dof_cnt * dim);
// Create an IntegrationRule on the nodes of the reference submesh.
IntegrationRule *irule = new IntegrationRule(pts_cnt);
GridFunction *nodesplit = meshsplit.GetNodes();
int pt_id = 0;
for (int i = 0; i < NEsplit; i++)
{
nodal_fes.GetElementVDofs(i, xdofs);
nodesplit->GetSubVector(xdofs, posV);
for (int j = 0; j < dof_cnt; j++)
{
if (dim == 2)
{
irule->IntPoint(pt_id).Set2(pos(j, 0), pos(j, 1));
}
else if (dim == 3)
{
irule->IntPoint(pt_id).Set3(pos(j, 0), pos(j, 1), pos(j, 2));
}
pt_id++;
}
}
return irule;
}
bool TMOPDeRefinerEstimator::GetDerefineEnergyForIntegrator(
TMOP_Integrator &tmopi,
Vector &fine_energy)
{
DiscreteAdaptTC *tcd = tmopi.GetDiscreteAdaptTC();
fine_energy.SetSize(mesh->GetNE());
if (serial)
{
Mesh meshcopy(*mesh);
FiniteElementSpace *tcdfes = NULL;
if (tcd)
{
tcdfes = new FiniteElementSpace(*tcd->GetTSpecFESpace(), &meshcopy);
}
Vector local_err(meshcopy.GetNE());
local_err = 0.;
double threshold = std::numeric_limits<float>::max();
meshcopy.DerefineByError(local_err, threshold, 0, 1);
if (meshcopy.GetGlobalNE() == mesh->GetGlobalNE())
{
delete tcdfes;
return false;
}
if (tcd)
{
tcdfes->Update();
tcd->SetTspecDataForDerefinement(tcdfes);
}
Vector coarse_energy(meshcopy.GetNE());
GetTMOPDerefinementEnergy(meshcopy, tmopi, coarse_energy);
if (tcd) { tcd->ResetDerefinementTspecData(); }
GetTMOPDerefinementEnergy(*mesh, tmopi, fine_energy);
const CoarseFineTransformations &dtrans =
meshcopy.ncmesh->GetDerefinementTransforms();
Table coarse_to_fine;
dtrans.GetCoarseToFineMap(meshcopy, coarse_to_fine);
for (int pe = 0; pe < coarse_to_fine.Size(); pe++)
{
Array<int> tabrow;
coarse_to_fine.GetRow(pe, tabrow);
int nchild = tabrow.Size();
double parent_energy = coarse_energy(pe);
for (int fe = 0; fe < nchild; fe++)
{
int child = tabrow[fe];
MFEM_VERIFY(child < mesh->GetNE(), " invalid coarse to fine mapping");
fine_energy(child) -= parent_energy;
}
}
delete tcdfes;
}
else
{
#ifdef MFEM_USE_MPI
ParMesh meshcopy(*pmesh);
ParFiniteElementSpace *tcdfes = NULL;
if (tcd)
{
tcdfes = new ParFiniteElementSpace(*tcd->GetTSpecParFESpace(), meshcopy);
}
Vector local_err(meshcopy.GetNE());
local_err = 0.;
double threshold = std::numeric_limits<float>::max();
meshcopy.DerefineByError(local_err, threshold, 0, 1);
if (meshcopy.GetGlobalNE() == pmesh->GetGlobalNE())
{
delete tcdfes;
return false;
}
if (tcd)
{
tcdfes->Update();
tcd->SetTspecDataForDerefinement(tcdfes);
}
Vector coarse_energy(meshcopy.GetNE());
GetTMOPDerefinementEnergy(meshcopy, tmopi, coarse_energy);
if (tcd) { tcd->ResetDerefinementTspecData(); }
GetTMOPDerefinementEnergy(*pmesh, tmopi, fine_energy);
const CoarseFineTransformations &dtrans =
meshcopy.pncmesh->GetDerefinementTransforms();
Table coarse_to_fine;
dtrans.GetCoarseToFineMap(meshcopy, coarse_to_fine);
for (int pe = 0; pe < meshcopy.GetNE(); pe++)
{
Array<int> tabrow;
coarse_to_fine.GetRow(pe, tabrow);
int nchild = tabrow.Size();
double parent_energy = coarse_energy(pe);
for (int fe = 0; fe < nchild; fe++)
{
int child = tabrow[fe];
MFEM_VERIFY(child < pmesh->GetNE(), " invalid coarse to fine mapping");
fine_energy(child) -= parent_energy;
}
}
delete tcdfes;
#endif
}
// error_estimate(e) = energy(parent_of_e)-energy(e)
// Negative energy means derefinement is desirable.
fine_energy *= -1;
return true;
}
void TMOPDeRefinerEstimator::ComputeEstimates()
{
Array<NonlinearFormIntegrator*> &integs = *(nlf->GetDNFI());
TMOP_Integrator *ti = NULL;
TMOPComboIntegrator *co = NULL;
error_estimates.SetSize(mesh->GetNE());
error_estimates = 0.;
Vector fine_energy(mesh->GetNE());
for (int i = 0; i < integs.Size(); i++)
{
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
if (ti)
{
bool deref = GetDerefineEnergyForIntegrator(*ti, fine_energy);
if (!deref) { error_estimates = 1; return; }
error_estimates += fine_energy;
}
co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
if (co)
{
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
for (int j = 0; j < ati.Size(); j++)
{
bool deref = GetDerefineEnergyForIntegrator(*ati[j], fine_energy);
if (!deref) { error_estimates = 1; return; }
error_estimates += fine_energy;
}
}
}
}
void TMOPDeRefinerEstimator::GetTMOPDerefinementEnergy(Mesh &cmesh,
TMOP_Integrator &tmopi,
Vector &el_energy_vec)
{
const int cNE = cmesh.GetNE();
el_energy_vec.SetSize(cNE);
const FiniteElementSpace *fespace = cmesh.GetNodalFESpace();
GridFunction *cxdof = cmesh.GetNodes();
Array<int> vdofs;
Vector el_x;
const FiniteElement *fe;
ElementTransformation *T;
for (int j = 0; j < cNE; j++)
{
fe = fespace->GetFE(j);
fespace->GetElementVDofs(j, vdofs);
T = cmesh.GetElementTransformation(j);
cxdof->GetSubVector(vdofs, el_x);
el_energy_vec(j) = tmopi.GetDerefinementElementEnergy(*fe, *T, el_x);
}
}
TMOPHRSolver::TMOPHRSolver(Mesh &mesh_, NonlinearForm &nlf_,
TMOPNewtonSolver &tmopns_, GridFunction &x_,
bool move_bnd_, bool hradaptivity_,
int mesh_poly_deg_, int amr_metric_id_,
int hr_iter_, int h_per_r_iter_) :
mesh(&mesh_), nlf(&nlf_), tmopns(&tmopns_), x(&x_),
gridfuncarr(), fespacearr(),
move_bnd(move_bnd_), hradaptivity(hradaptivity_),
mesh_poly_deg(mesh_poly_deg_), amr_metric_id(amr_metric_id_),
serial(true), hr_iter(hr_iter_), h_per_r_iter(h_per_r_iter_)
{
if (!hradaptivity) { return; }
tmop_r_est = new TMOPRefinerEstimator(*mesh, *nlf, mesh_poly_deg,
amr_metric_id);
tmop_r = new ThresholdRefiner(*tmop_r_est);
tmop_r->SetTotalErrorFraction(0.0);
tmop_r_est->SetEnergyScalingFactor(1.);
tmop_dr_est= new TMOPDeRefinerEstimator(*mesh, *nlf);
tmop_dr = new ThresholdDerefiner(*tmop_dr_est);
AddGridFunctionForUpdate(x);
}
#ifdef MFEM_USE_MPI
TMOPHRSolver::TMOPHRSolver(ParMesh &pmesh_, ParNonlinearForm &pnlf_,
TMOPNewtonSolver &tmopns_, ParGridFunction &px_,
bool move_bnd_, bool hradaptivity_,
int mesh_poly_deg_, int amr_metric_id_,
int hr_iter_, int h_per_r_iter_) :
mesh(&pmesh_), nlf(&pnlf_), tmopns(&tmopns_), x(&px_),
gridfuncarr(), fespacearr(),
move_bnd(move_bnd_), hradaptivity(hradaptivity_),
mesh_poly_deg(mesh_poly_deg_), amr_metric_id(amr_metric_id_),
pmesh(&pmesh_), pnlf(&pnlf_), pgridfuncarr(), pfespacearr(),
serial(false), hr_iter(hr_iter_), h_per_r_iter(h_per_r_iter_)
{
if (!hradaptivity) { return; }
tmop_r_est = new TMOPRefinerEstimator(*pmesh, *pnlf, mesh_poly_deg,
amr_metric_id);
tmop_r = new ThresholdRefiner(*tmop_r_est);
tmop_r->SetTotalErrorFraction(0.0);
tmop_r_est->SetEnergyScalingFactor(1.);
tmop_dr_est= new TMOPDeRefinerEstimator(*pmesh, *pnlf);
tmop_dr = new ThresholdDerefiner(*tmop_dr_est);
AddGridFunctionForUpdate(&px_);
}
#endif
void TMOPHRSolver::Mult()
{
Vector b(0);
int myid = 0;
if (serial)
{
tmopns->SetOperator(*nlf);
}
else
{
#ifdef MFEM_USE_MPI
myid = pnlf->ParFESpace()->GetMyRank();
tmopns->SetOperator(*pnlf);
#endif
}
if (!hradaptivity)
{
tmopns->Mult(b, x->GetTrueVector());
if (tmopns->GetConverged() == false)
{
if (myid == 0) { mfem::out << "Nonlinear solver: rtol not achieved.\n"; }
}
x->SetFromTrueVector();
return;
}
bool radaptivity = true;
tmop_dr->Reset();
tmop_r->Reset();
if (serial)
{
for (int i_hr = 0; i_hr < hr_iter; i_hr++)
{
if (!radaptivity)
{
break;
}
mfem::out << i_hr << " r-adaptivity iteration.\n";
tmopns->SetOperator(*nlf);
tmopns->Mult(b, x->GetTrueVector());
x->SetFromTrueVector();
mfem::out << "TMOP energy after r-adaptivity: " <<
nlf->GetGridFunctionEnergy(*x)/mesh->GetNE() <<
", Elements: " << mesh->GetNE() << std::endl;
for (int i_h = 0; i_h < h_per_r_iter; i_h++)
{
// Derefinement step.
if (mesh->ncmesh)
{
tmop_dr->Apply(*mesh);
Update();
}
mfem::out << "TMOP energy after derefinement: " <<
nlf->GetGridFunctionEnergy(*x)/mesh->GetNE() <<
", Elements: " << mesh->GetNE() << std::endl;
// Refinement step.
tmop_r->Apply(*mesh);
Update();
mfem::out << "TMOP energy after refinement: " <<
nlf->GetGridFunctionEnergy(*x)/mesh->GetNE() <<
", Elements: " << mesh->GetNE() << std::endl;
if (!tmop_dr->Derefined() && tmop_r->Stop())
{
radaptivity = false;
mfem::out << "AMR stopping criterion satisfied. Stop.\n";
break;
}
} //n_h
} //n_hr
}
else
{
#ifdef MFEM_USE_MPI
int NEGlob;
double tmopenergy;
for (int i_hr = 0; i_hr < hr_iter; i_hr++)
{
if (!radaptivity)
{
break;
}
if (myid == 0) { mfem::out << i_hr << " r-adaptivity iteration.\n"; }
tmopns->SetOperator(*pnlf);
tmopns->Mult(b, x->GetTrueVector());
x->SetFromTrueVector();
NEGlob = pmesh->GetGlobalNE();
tmopenergy = pnlf->GetParGridFunctionEnergy(*x) / NEGlob;
if (myid == 0)
{
mfem::out << "TMOP energy after r-adaptivity: " << tmopenergy <<
", Elements: " << NEGlob << std::endl;
}
for (int i_h = 0; i_h < h_per_r_iter; i_h++)
{
// Derefinement step.
if (pmesh->pncmesh)
{
RebalanceParNCMesh();
ParUpdate();
tmop_dr->Apply(*pmesh);
ParUpdate();
}
NEGlob = pmesh->GetGlobalNE();
tmopenergy = pnlf->GetParGridFunctionEnergy(*x) / NEGlob;
if (myid == 0)
{
mfem::out << "TMOP energy after derefinement: " << tmopenergy <<
", Elements: " << NEGlob << std::endl;
}
// Refinement step.
tmop_r->Apply(*pmesh);
ParUpdate();
NEGlob = pmesh->GetGlobalNE();
tmopenergy = pnlf->GetParGridFunctionEnergy(*x) / NEGlob;
if (myid == 0)
{
mfem::out << "TMOP energy after refinement: " << tmopenergy <<
", Elements: " << NEGlob << std::endl;
}
if (!tmop_dr->Derefined() && tmop_r->Stop())
{
radaptivity = false;
if (myid == 0)
{
mfem::out << "AMR stopping criterion satisfied. Stop.\n";
}
break;
}
} //n_r limit
} //n_hr
#endif
}
}
#ifdef MFEM_USE_MPI
void TMOPHRSolver::RebalanceParNCMesh()
{
ParNCMesh *pncmesh = pmesh->pncmesh;
if (pncmesh)
{
const Table &dreftable = pncmesh->GetDerefinementTable();
Array<int> drefs, new_ranks;
for (int i = 0; i < dreftable.Size(); i++)
{
drefs.Append(i);
}
pncmesh->GetFineToCoarsePartitioning(drefs, new_ranks);
pmesh->Rebalance(new_ranks);
}
}
#endif
void TMOPHRSolver::Update()
{
// Update FESpace
for (int i = 0; i < fespacearr.Size(); i++)
{
fespacearr[i]->Update();
}
// Update nodal GF
for (int i = 0; i < gridfuncarr.Size(); i++)
{
gridfuncarr[i]->Update();
gridfuncarr[i]->SetTrueVector();
gridfuncarr[i]->SetFromTrueVector();
}
// Update Discrete Indicator for all the TMOP_Integrators in NonLinearForm
Array<NonlinearFormIntegrator*> &integs = *(nlf->GetDNFI());
TMOP_Integrator *ti = NULL;
TMOPComboIntegrator *co = NULL;
DiscreteAdaptTC *dtc = NULL;
for (int i = 0; i < integs.Size(); i++)
{
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
if (ti)
{
ti->UpdateAfterMeshTopologyChange();
dtc = ti->GetDiscreteAdaptTC();
if (dtc) { dtc->UpdateAfterMeshTopologyChange(); }
}
co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
if (co)
{
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
for (int j = 0; j < ati.Size(); j++)
{
ati[j]->UpdateAfterMeshTopologyChange();
dtc = ati[j]->GetDiscreteAdaptTC();
if (dtc) { dtc->UpdateAfterMeshTopologyChange(); }
}
}
}
// Update the Nonlinear form and set Essential BC.
UpdateNonlinearFormAndBC(mesh, nlf);
}
#ifdef MFEM_USE_MPI
void TMOPHRSolver::ParUpdate()
{
// Update FESpace
for (int i = 0; i < pfespacearr.Size(); i++)
{
pfespacearr[i]->Update();
}
// Update nodal GF
for (int i = 0; i < pgridfuncarr.Size(); i++)
{
pgridfuncarr[i]->Update();
pgridfuncarr[i]->SetTrueVector();
pgridfuncarr[i]->SetFromTrueVector();
}
// Update Discrete Indicator
Array<NonlinearFormIntegrator*> &integs = *(nlf->GetDNFI());
TMOP_Integrator *ti = NULL;
TMOPComboIntegrator *co = NULL;
DiscreteAdaptTC *dtc = NULL;
for (int i = 0; i < integs.Size(); i++)
{
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
if (ti)
{
ti->ParUpdateAfterMeshTopologyChange();
dtc = ti->GetDiscreteAdaptTC();
if (dtc) { dtc->ParUpdateAfterMeshTopologyChange(); }
}
co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
if (co)
{
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
for (int j = 0; j < ati.Size(); j++)
{
ati[j]->ParUpdateAfterMeshTopologyChange();
dtc = ati[j]->GetDiscreteAdaptTC();
if (dtc) { dtc->ParUpdateAfterMeshTopologyChange(); }
}
}
}
// Update the Nonlinear form and set Essential BC.
UpdateNonlinearFormAndBC(pmesh, pnlf);
}
#endif
void TMOPHRSolver::UpdateNonlinearFormAndBC(Mesh *mesh, NonlinearForm *nlf)
{
const FiniteElementSpace &fes = *mesh->GetNodalFESpace();
// Update Nonlinear form and Set Essential BC
nlf->Update();
const int dim = fes.GetFE(0)->GetDim();
if (move_bnd == false)
{
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
nlf->SetEssentialBC(ess_bdr);
}
else
{
const int nd = fes.GetBE(0)->GetDof();
int n = 0;
for (int i = 0; i < mesh->GetNBE(); i++)
{
const int attr = mesh->GetBdrElement(i)->GetAttribute();
MFEM_VERIFY(!(dim == 2 && attr == 3),
"Boundary attribute 3 must be used only for 3D meshes. "
"Adjust the attributes (1/2/3/4 for fixed x/y/z/all "
"components, rest for free nodes), or use -fix-bnd.");
if (attr == 1 || attr == 2 || attr == 3) { n += nd; }
if (attr == 4) { n += nd * dim; }
}
Array<int> ess_vdofs(n), vdofs;
n = 0;
for (int i = 0; i < mesh->GetNBE(); i++)
{
const int attr = mesh->GetBdrElement(i)->GetAttribute();
fes.GetBdrElementVDofs(i, vdofs);
if (attr == 1) // Fix x components.
{
for (int j = 0; j < nd; j++)
{ ess_vdofs[n++] = vdofs[j]; }
}
else if (attr == 2) // Fix y components.
{
for (int j = 0; j < nd; j++)
{ ess_vdofs[n++] = vdofs[j+nd]; }
}
else if (attr == 3) // Fix z components.
{
for (int j = 0; j < nd; j++)
{ ess_vdofs[n++] = vdofs[j+2*nd]; }
}
else if (attr == 4) // Fix all components.
{
for (int j = 0; j < vdofs.Size(); j++)
{ ess_vdofs[n++] = vdofs[j]; }
}
}
nlf->SetEssentialVDofs(ess_vdofs);
}
}
}
+284
View File
@@ -0,0 +1,284 @@
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_TMOP_AMR_HPP
#define MFEM_TMOP_AMR_HPP
#include "tmop_tools.hpp"
#include "nonlinearform.hpp"
#include "pnonlinearform.hpp"
#include "estimators.hpp"
#include "../mesh/mesh_operators.hpp"
namespace mfem
{
class TMOPRefinerEstimator : public AnisotropicErrorEstimator
{
protected:
Mesh *mesh; // not owned
NonlinearForm *nlf; // not owned
int order;
int amrmetric;
Array<IntegrationRule *> TriIntRule, QuadIntRule, TetIntRule, HexIntRule;
long current_sequence;
Vector error_estimates;
Array<int> aniso_flags;
// An element is refined only if
// [mean TMOPEnergy(children)]*energy_scaling_factor < TMOPEnergy(parent)
double energy_scaling_factor;
GridFunction *spat_gf; // If specified, can be used to specify the
double spat_gf_critical; // region where hr-adaptivity is done.
/// Check if the mesh of the solution was modified.
bool MeshIsModified()
{
long mesh_sequence = mesh->GetSequence();
MFEM_ASSERT(mesh_sequence >= current_sequence, "");
return (mesh_sequence > current_sequence);
}
/// Compute the element error estimates. For an element E in the mesh,
/// error(E) = TMOPEnergy(E)*energy_scaling_factor-Mean(TMOPEnergy(ChildofE)),
/// where TMOPEnergy of Children of E is obtained by assuming the element E
/// is refined using the refinement type being considered based on the TMOP
/// mesh quality metric.
void ComputeEstimates();
/// Construct the integration rules to model how each element type is split
/// using different refinement types. ref_type = 0 is the original element
/// and reftype \ in [1, 7] represent different refinement type based on
/// NCMesh class.
void SetQuadIntRules(); // supports ref_type = 1 to 3.
void SetTriIntRules(); // currently supports only isotropic refinement.
void SetHexIntRules(); // currently supports only isotropic refinement.
void SetTetIntRules(); // currently supports only isotropic refinement.
/// Get TMOP energy for each element corresponding to the refinement type
/// specified.
void GetTMOPRefinementEnergy(int reftype, Vector &el_energy_vec);
/// Use a mesh to setup an integration rule that will mimic the different
/// refinement types.
IntegrationRule* SetIntRulesFromMesh(Mesh &meshsplit);
public:
TMOPRefinerEstimator(Mesh &mesh_, NonlinearForm &nlf_, int order_,
int amrmetric_) :
mesh(&mesh_), nlf(&nlf_), order(order_), amrmetric(amrmetric_),
TriIntRule(0), QuadIntRule(0), TetIntRule(0), HexIntRule(0),
current_sequence(-1), error_estimates(), aniso_flags(),
energy_scaling_factor(1.), spat_gf(NULL), spat_gf_critical(0.)
{
if (mesh->Dimension() == 2)
{
SetQuadIntRules();
SetTriIntRules();
}
else
{
SetHexIntRules();
SetTetIntRules();
}
}
~TMOPRefinerEstimator()
{
for (int i = 0; i < QuadIntRule.Size(); i++) { delete QuadIntRule[i]; }
for (int i = 0; i < TriIntRule.Size(); i++) { delete TriIntRule[i]; }
for (int i = 0; i < HexIntRule.Size(); i++) { delete HexIntRule[i]; }
for (int i = 0; i < TetIntRule.Size(); i++) { delete TetIntRule[i]; }
}
/// Get TMOP-based errors for each element in the mesh computed based on the
/// refinement types being considered.
virtual const Vector &GetLocalErrors()
{
if (MeshIsModified()) { ComputeEstimates(); }
return error_estimates;
}
/// For anisotropic refinements, get the refinement type (e.g., x or y)
virtual const Array<int> &GetAnisotropicFlags()
{
if (MeshIsModified()) { ComputeEstimates(); }
return aniso_flags;
}
/// Scaling factor for the TMOP refinement energy. An element is refined if
/// [mean TMOPEnergy(children)]*energy_scaling_factor < TMOPEnergy(parent)
void SetEnergyScalingFactor(double scale) { energy_scaling_factor = scale; }
/// Spatial indicator function (eta) that can be used to prevent elements
/// from being refined even if the energy criterion is met. Using this,
/// an element E is not refined if mean(@a spat_gf(E)) < @a spat_gf_critical.
void SetSpatialIndicator(GridFunction &spat_gf_,
double spat_gf_critical_ = 0.5)
{ spat_gf = &spat_gf_; spat_gf_critical = spat_gf_critical_; }
void SetSpatialIndicatorCritical(double val_) { spat_gf_critical = val_; }
/// Reset the error estimator.
virtual void Reset() { current_sequence = -1; }
};
class TMOPDeRefinerEstimator : public ErrorEstimator
{
protected:
Mesh *mesh;
NonlinearForm *nlf;
#ifdef MFEM_USE_MPI
ParMesh *pmesh;
ParNonlinearForm *pnlf;
#endif
int order;
int amrmetric;
long current_sequence;
Vector error_estimates;
bool serial;
/// Check if the mesh of the solution was modified.
bool MeshIsModified()
{
long mesh_sequence = mesh->GetSequence();
MFEM_ASSERT(mesh_sequence >= current_sequence, "");
return (mesh_sequence > current_sequence);
}
/// Compute the element error estimates. For a given element E in the mesh,
/// error(E) = TMOPEnergy(parent_of_E)-TMOPEnergy(E). Children element of an
/// element are derefined if the mean TMOP energy of children is greated than
/// the TMOP energy associated with their parent.
void ComputeEstimates();
void GetTMOPDerefinementEnergy(Mesh &cmesh,
TMOP_Integrator &tmopi,
Vector &el_energy_vec);
bool GetDerefineEnergyForIntegrator(TMOP_Integrator &tmopi,
Vector &fine_energy);
public:
TMOPDeRefinerEstimator(Mesh &mesh_, NonlinearForm &nlf_) :
mesh(&mesh_), nlf(&nlf_),
current_sequence(-1), error_estimates(), serial(true) { }
#ifdef MFEM_USE_MPI
TMOPDeRefinerEstimator(ParMesh &pmesh_, ParNonlinearForm &pnlf_) :
mesh(&pmesh_), nlf(&pnlf_), pmesh(&pmesh_), pnlf(&pnlf_),
current_sequence(-1), error_estimates(), serial(false) { }
#endif
~TMOPDeRefinerEstimator() { }
virtual const Vector &GetLocalErrors()
{
if (MeshIsModified()) { ComputeEstimates(); }
return error_estimates;
}
/// Reset the error estimator.
virtual void Reset() { current_sequence = -1; }
};
// hr-adaptivity using TMOP.
// If hr-adaptivity is disabled, r-adaptivity is done once using the
// TMOPNewtonSolver.
// Otherwise, "hr_iter" iterations of r-adaptivity are done followed by
// "h_per_r_iter" iterations of h-adaptivity after each r-adaptivity iteration.
// The solver terminates early if an h-adaptivity iteration does not
// refine/derefine any element in the mesh.
class TMOPHRSolver
{
protected:
Mesh *mesh;
NonlinearForm *nlf;
TMOPNewtonSolver *tmopns;
GridFunction *x;
Array<GridFunction *> gridfuncarr;
Array<FiniteElementSpace *> fespacearr;
bool move_bnd, hradaptivity;
const int mesh_poly_deg, amr_metric_id;
#ifdef MFEM_USE_MPI
ParMesh *pmesh;
ParNonlinearForm *pnlf;
Array<ParGridFunction *> pgridfuncarr;
Array<ParFiniteElementSpace *> pfespacearr;
#endif
bool serial;
// All are owned.
TMOPRefinerEstimator *tmop_r_est;
ThresholdRefiner *tmop_r;
TMOPDeRefinerEstimator *tmop_dr_est;
ThresholdDerefiner *tmop_dr;
int hr_iter, h_per_r_iter;
void Update();
#ifdef MFEM_USE_MPI
void ParUpdate();
#endif
void UpdateNonlinearFormAndBC(Mesh *mesh, NonlinearForm *nlf);
#ifdef MFEM_USE_MPI
// Rebalance ParMesh such that all the children elements are moved to the same
// MPI rank where the parent will be if the mesh were to be derefined.
void RebalanceParNCMesh();
#endif
public:
TMOPHRSolver(Mesh &mesh_, NonlinearForm &nlf_,
TMOPNewtonSolver &tmopns_, GridFunction &x_,
bool move_bnd_, bool hradaptivity_,
int mesh_poly_deg_, int amr_metric_id_,
int hr_iter_ = 5, int h_per_r_iter_ = 1);
#ifdef MFEM_USE_MPI
TMOPHRSolver(ParMesh &pmesh_, ParNonlinearForm &pnlf_,
TMOPNewtonSolver &tmopns_, ParGridFunction &x_,
bool move_bnd_, bool hradaptivity_,
int mesh_poly_deg_, int amr_metric_id_,
int hr_iter_ = 5, int h_per_r_iter_ = 1);
#endif
void Mult();
/// These are used to update spaces and functions that are not owned by the
/// TMOPIntegrator or DiscreteAdaptTC. The owned ones are updated in the
/// functions UpdateAfterMeshTopologyChange() of both classes.
void AddGridFunctionForUpdate(GridFunction *gf) { gridfuncarr.Append(gf); }
void AddFESpaceForUpdate(FiniteElementSpace *fes) { fespacearr.Append(fes); }
#ifdef MFEM_USE_MPI
void AddGridFunctionForUpdate(ParGridFunction *pgf_)
{
pgridfuncarr.Append(pgf_);
}
void AddFESpaceForUpdate(ParFiniteElementSpace *pfes_)
{
pfespacearr.Append(pfes_);
}
#endif
~TMOPHRSolver()
{
if (!hradaptivity) { return; }
delete tmop_dr;
delete tmop_dr_est;
delete tmop_r;
delete tmop_r_est;
}
/// Total number of hr-adaptivity iterations. At each iteration, we do an
/// r-adaptivity iteration followed by a number of h-adaptivity iterations.
void SetHRAdaptivityIterations(int iter) { hr_iter = iter; }
/// Total number of h-adaptivity iterations per r-adaptivity iteration.
void SetHAdaptivityIterations(int iter) { h_per_r_iter = iter; }
};
}
#endif
+6 -4
View File
@@ -407,6 +407,8 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
{
// Needed for the line search below. The untangling metrics see this
// reference to detect deteriorations.
MFEM_VERIFY(min_det_ptr != NULL, " Initial mesh was valid, but"
" intermediate mesh is invalid. Contact TMOP Developers.");
*min_det_ptr = untangle_factor * min_detT_in;
}
@@ -576,7 +578,7 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
if (ti)
{
ti->UpdateAfterMeshChange(x_loc);
ti->UpdateAfterMeshPositionChange(x_loc);
ti->ComputeFDh(x_loc, *pfesc);
UpdateDiscreteTC(*ti, x_loc);
}
@@ -586,7 +588,7 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
for (int j = 0; j < ati.Size(); j++)
{
ati[j]->UpdateAfterMeshChange(x_loc);
ati[j]->UpdateAfterMeshPositionChange(x_loc);
ati[j]->ComputeFDh(x_loc, *pfesc);
UpdateDiscreteTC(*ati[j], x_loc);
}
@@ -613,7 +615,7 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
if (ti)
{
ti->UpdateAfterMeshChange(x_loc);
ti->UpdateAfterMeshPositionChange(x_loc);
ti->ComputeFDh(x_loc, *fesc);
UpdateDiscreteTC(*ti, x_loc);
}
@@ -623,7 +625,7 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
for (int j = 0; j < ati.Size(); j++)
{
ati[j]->UpdateAfterMeshChange(x_loc);
ati[j]->UpdateAfterMeshPositionChange(x_loc);
ati[j]->ComputeFDh(x_loc, *fesc);
UpdateDiscreteTC(*ati[j], x_loc);
}
+16 -2
View File
@@ -494,8 +494,7 @@ public:
/// Copy @a size entries from @a *this to @a dest.
/** The given @a size should not exceed the Capacity() of @a *this and the
destination, @a dest. */
inline void CopyTo(Memory &dest, int size) const
{ dest.CopyFrom(*this, size); }
inline void CopyTo(Memory &dest, int size) const;
/// Copy @a size entries from @a *this to the host pointer @a dest.
/** The given @a size should not exceed the Capacity() of @a *this. */
@@ -923,6 +922,11 @@ inline void Memory<T>::Wrap(T *ptr, T *d_ptr, int size, MemoryType mt, bool own)
template <typename T>
inline void Memory<T>::MakeAlias(const Memory &base, int offset, int size)
{
MFEM_ASSERT(0 <= offset, "invalid offset = " << offset);
MFEM_ASSERT(0 <= size, "invalid size = " << size);
MFEM_ASSERT(offset + size <= base.capacity,
"invalid offset + size = " << offset + size
<< " > base capacity = " << base.capacity);
capacity = size;
h_mt = base.h_mt;
h_ptr = base.h_ptr + offset;
@@ -1136,6 +1140,7 @@ inline bool Memory<T>::DeviceIsValid() const
template <typename T>
inline void Memory<T>::CopyFrom(const Memory &src, int size)
{
MFEM_VERIFY(src.capacity>=size && capacity>=size, "Incorrect size");
if (!(flags & REGISTERED) && !(src.flags & REGISTERED))
{
if (h_ptr != src.h_ptr && size != 0)
@@ -1155,6 +1160,7 @@ inline void Memory<T>::CopyFrom(const Memory &src, int size)
template <typename T>
inline void Memory<T>::CopyFromHost(const T *src, int size)
{
MFEM_VERIFY(capacity>=size, "Incorrect size");
if (!(flags & REGISTERED))
{
if (h_ptr != src && size != 0)
@@ -1171,9 +1177,17 @@ inline void Memory<T>::CopyFromHost(const T *src, int size)
}
}
template <typename T>
inline void Memory<T>::CopyTo(Memory &dest, int size) const
{
MFEM_VERIFY(capacity>=size, "Incorrect size");
dest.CopyFrom(*this, size);
}
template <typename T>
inline void Memory<T>::CopyToHost(T *dest, int size) const
{
MFEM_VERIFY(capacity>=size, "Incorrect size");
if (!(flags & REGISTERED))
{
if (h_ptr != dest && size != 0)
-3
View File
@@ -82,9 +82,6 @@ const char *GetConfigStr()
#ifdef MFEM_USE_CUDA
"MFEM_USE_CUDA\n"
#endif
#ifdef MFEM_USE_EPIC
"MFEM_USE_EPIC\n"
#endif
#ifdef MFEM_USE_EXCEPTIONS
"MFEM_USE_EXCEPTIONS\n"
#endif
-5
View File
@@ -83,11 +83,6 @@ if (MFEM_USE_SUNDIALS)
list(APPEND HDRS sundials.hpp)
endif()
if (MFEM_USE_EPIC)
list(APPEND SRCS epic.cpp)
list(APPEND HDRS epic.hpp)
endif()
if (MFEM_USE_SUPERLU)
list(APPEND SRCS superlu.cpp)
# If this list (HDRS -> HEADERS) is used for install, we probably want the
-171
View File
@@ -1,171 +0,0 @@
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "epic.hpp"
#ifdef MFEM_USE_EPIC
namespace mfem
{
EPICSolver::EPICSolver(bool exactJacobian_, EPICNumJacDelta delta)
{
// Allocate an empty serial N_Vector
temp = N_VNewEmpty_Serial(0);
m[0] = 10;
m[1] = 10;
MFEM_VERIFY(temp, "error in N_VNewEmpty_Serial()");
exactJacobian = exactJacobian_;
Jtv = NULL;
Delta = delta;
}
#ifdef MFEM_USE_MPI
EPICSolver::EPICSolver(MPI_Comm comm)
{
m[0] = 10;
m[1] = 10;
// Allocate an empty vector
if (comm == MPI_COMM_NULL)
{
// Allocate an empty serial N_Vector
temp = N_VNewEmpty_Serial(0);
MFEM_VERIFY(temp, "error in N_VNewEmpty_Serial()");
}
else
{
// Allocate an empty parallel N_Vector
temp = N_VNewEmpty_Parallel(comm, 0, 0); // calls MPI_Allreduce()
MFEM_VERIFY(temp, "error in N_VNewEmpty_Parallel()");
}
}
#endif
int EPICSolver::RHS(realtype t, const N_Vector y, N_Vector ydot, void *user_data)
{
// Get data from N_Vectors
const Vector mfem_y(y);
Vector mfem_ydot(ydot);
EPICSolver *self = static_cast<EPICSolver*>(user_data);
// Compute y' = f(t, y)
self->f->SetTime(t);
self->f->Mult(mfem_y, mfem_ydot);
// Return success
return 0;
}
int EPICSolver::Jacobian(N_Vector v, N_Vector Jv, realtype t, N_Vector y, N_Vector fy, void *user_data, N_Vector tmp)
{
// Get data from N_Vectors
const Vector mfem_v(v);
Vector mfem_Jv(Jv);
EPICSolver *self = static_cast<EPICSolver*>(user_data);
// Compute J(t, y) v
self->Jtv->Mult(mfem_v, mfem_Jv);
return 0;
}
void EPICSolver::Init(TimeDependentOperator &f)
{
ODESolver::Init(f);
long local_size = f.Height();
long global_size = 0;
#ifdef MFEM_USE_MPI
if (Parallel())
{
MPI_Allreduce(&local_size, &global_size, 1, MPI_LONG, MPI_SUM,
NV_COMM_P(temp));
}
#endif
Vector mfem_temp(local_size);
mfem_temp.ToNVector(temp, global_size);
}
EPI2::EPI2(bool exactJacobian, EPICNumJacDelta delta) : EPICSolver(exactJacobian, delta) {}
void EPI2::Init(TimeDependentOperator &f)
{
EPICSolver::Init(f);
long local_size = f.Height();
if (exactJacobian) {
integrator = new Epi2_KIOPS(EPICSolver::RHS, EPICSolver::Jacobian, this, 100, temp ,local_size);
} else {
integrator = new Epi2_KIOPS(EPICSolver::RHS, Delta, this, 100, temp ,local_size);
}
}
EPIRK4::EPIRK4(bool exactJacobian, EPICNumJacDelta delta) : EPICSolver(exactJacobian, delta) {}
void EPIRK4::Init(TimeDependentOperator &f)
{
EPICSolver::Init(f);
long local_size = f.Height();
if (exactJacobian) {
integrator = new EpiRK4SC_KIOPS(EPICSolver::RHS, EPICSolver::Jacobian, this, 100, temp ,local_size);
} else {
integrator = new EpiRK4SC_KIOPS(EPICSolver::RHS, Delta, this, 100, temp ,local_size);
}
}
void EPICSolver::Step(Vector &x, double &t, double &dt)
{
if (!Parallel())
{
NV_DATA_S(temp) = x.GetData();
MFEM_VERIFY(NV_LENGTH_S(temp) == x.Size(), "");
}
else
{
#ifdef MFEM_USE_MPI
NV_DATA_P(temp) = x.GetData();
MFEM_VERIFY(NV_LOCLENGTH_P(temp) == x.Size(), "");
#endif
}
Jtv = &(this->f->GetGradient(x));
}
void EPI2::Step(Vector &x, double &t, double &dt)
{
EPICSolver::Step(x, t, dt);
integrator->Integrate(dt, t, t+dt, 0, temp, 1e-10, m);
t += dt;
}
void EPIRK4::Step(Vector &x, double &t, double &dt)
{
EPICSolver::Step(x, t, dt);
integrator->Integrate(dt, t, t+dt, 0, temp, 1e-10, m);
t += dt;
}
EPI2::~EPI2()
{
delete integrator;
}
EPIRK4::~EPIRK4()
{
delete integrator;
}
}
#endif
-98
View File
@@ -1,98 +0,0 @@
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_EPIC
#define MFEM_EPIC
#include "../config/config.hpp"
#ifdef MFEM_USE_EPIC
// SUNDIALS vectors
#include <nvector/nvector_serial.h>
#ifdef MFEM_USE_MPI
#include <mpi.h>
#include <nvector/nvector_parallel.h>
#endif
#include "ode.hpp"
#include "solvers.hpp"
#include <Epic.h>
namespace mfem
{
typedef void (*JacobianFun)(const realtype t, const Vector &y, const Vector& v, Vector& Jv, void* user_data);
// ---------------------------------------------------------------------------
// Interface to the EPIC library -- exponential methods
// ---------------------------------------------------------------------------
class EPICSolver : public ODESolver
{
protected:
EPICNumJacDelta Delta;
Operator* Jtv;
N_Vector temp;
int m[2];
bool exactJacobian;
#ifdef MFEM_USE_MPI
bool Parallel() const
{
return (N_VGetVectorID(temp) != SUNDIALS_NVEC_SERIAL);
}
#else
bool Parallel() const { return false; }
#endif
public:
EPICSolver(bool exactJacobian, EPICNumJacDelta delta=&DefaultDelta);
EPICSolver(MPI_Comm comm);
static int RHS(realtype t, const N_Vector y, N_Vector ydot, void *user_data);
static int Jacobian(N_Vector v, N_Vector Jv, realtype t,
N_Vector y, N_Vector fy, void *user_data, N_Vector tmp);
virtual void Init(TimeDependentOperator &f);
virtual void Step(Vector &x, double &t, double &dt);
virtual ~EPICSolver() {}
};
class EPI2 : public EPICSolver
{
protected:
Epi2_KIOPS* integrator;
public:
EPI2(bool exactJacobian=true, EPICNumJacDelta delta=&DefaultDelta);
virtual void Init(TimeDependentOperator &f);
virtual void Step(Vector &x, double &t, double &dt);
virtual ~EPI2();
};
class EPIRK4 : public EPICSolver
{
protected:
EpiRK4SC_KIOPS* integrator;
public:
EPIRK4(bool exactJacobian=true, EPICNumJacDelta delta=&DefaultDelta);
virtual void Init(TimeDependentOperator &f);
virtual void Step(Vector &x, double &t, double &dt);
virtual ~EPIRK4();
};
} // namespace mfem
#endif // MFEM_USE_EPIC
#endif // MFEM_EPIC
+1169
View File
File diff suppressed because it is too large Load Diff
+244
View File
@@ -0,0 +1,244 @@
#ifndef MFEM_IMEX
#define MFEM_IMEX
#include "../config/config.hpp"
#include "operator.hpp"
#include "ode.hpp"
#include <vector>
namespace mfem
{
/** Class for spatial discretizations of a PDE resulting in the time-dependent,
nonlinear set of ODEs with implicit-explicit additive partition
M*du/dt = N_E(u,t) + N_I(u,t).
MFEM typically treats time integration as
du/dt = F^{-1} G(u),
Here F represents what MFEM calls the implicit part, and G represents the
explicit part; in simpler terms, F is typically just a mass matrix.
For BDF schemes, the ImplicitSolve function is a bit different, and it is
more natural to apply M and M^{-1} separate from the Mult functions, so we
include MassMult and MassInv as functions to be provided, and do not include
such actions in the Mult functions. */
class IMEXTimeDependentOperator : public TimeDependentOperator
{
protected:
mutable Vector temp; // Auxillary vector
public:
// Sets linearly implicit to false by default
IMEXTimeDependentOperator(int n, double t=0.0, Type type=EXPLICIT)
: TimeDependentOperator(n, t, type) { };
~IMEXTimeDependentOperator() { };
/** Apply action of implicit part of operator y <- N_I(x,y). For fully
implicit schemes, this just corresponds to applying the time-dependent
(nonlinear) operator.
PREVIOUSLY CALLED ExplicitMult */
virtual void ImplicitMult(const Vector &x, Vector &y) const = 0;
/** Apply action of explicit part of operator y <- N_E(x,y) */
virtual void ExplicitMult(const Vector &x, Vector &y) const { y = 0.0; };
/** Solve k = f(x+dt*k) for stage k, where f() is the implicit part of
the operator. Used in Runge-Kutta methods. */
virtual void ImplicitSolve(const double dt, const Vector &x, Vector &k)
{ mfem::mfem_error("IMEXTimeDependentOperator::ImplicitSolve() is not overridden!"); };
/** Solve M*x - dtf(x, t) = b for solution x, where f() is the implicit
part of the operator. Used in BDF methods. */
virtual void ImplicitSolve2(const double dt, const Vector &b, Vector &x)
{ mfem::mfem_error("IMEXTimeDependentOperator::ImplicitSolve2() is not overridden!"); };
/** Apply action mass matrix, y = M*x.
If not re-implemented, this method simply generates an error.
PREVIOUSLY CALLED ImplictMult */
virtual void MassMult(const Vector &x, Vector &y) const = 0;
/** Apply action of inverse of mass matrix, y = M^{-1}*x.
If not re-implemented, this method simply generates an error.
NOTE : only necessary for PolyIMEX methods. */
virtual void MassInv(const Vector &x, Vector &y) const = 0;
};
/** Class holding RK Butcher tableau, and associated data required by
implicit and explicit splitting. */
class IMEXRKData
{
public:
// Implicit Runge Kutta type. Enumeration (s, \sigma, p):
// - s = number of implicit stages
// - \sigma = number of explicit stages
// - p = order
// In this notation, when s = \sigma, we satisfy (2.3)/(2.4) in
// Ascher et al., and do not need to compute the final explicit
// stage. This is represented in the stiffly_accurate boolean.
enum Type {
IMEX111 = 111,
IMEX121 = 121,
IMEX122 = 122,
IMEX222 = 222,
IMEX232 = 232,
IMEX233 = 233,
IMEX443 = 443,
// ARK ESDIRK-ERK schemes: enumeration (s,p), for total number of
// stages s.
ARK43 = -43
};
IMEXRKData() : s(-1) { };
IMEXRKData(Type ID_) : ID(ID_) { SetData(); };
~IMEXRKData() { };
/// Set explicit RK data
void SetExplicitData(DenseMatrix Ae_, Vector be_, Vector ce_);
/// Set implicit RK data
void SetImplicitData(DenseMatrix Ai_, Vector bi_, Vector ci_, bool esdirk_=false);
void SetID(Type ID_) { ID=ID_; SetData(); };
bool esdirk;
bool stiffly_accurate;
bool use_final_exp_stage;
int s;
DenseMatrix Ai; // Implicit Butcher matrix
Vector bi; // Implicit Butcher tableau weights
DenseMatrix Ae; // Explicit Butcher matrix
Vector be; // Explicit Butcher tableau weights
Vector c0; // Butcher tableau nodes (same for implicit and explicit!)
private:
Type ID;
void SetData();
void InitData();
};
/** Class for two-part additive IMEX RK method, where explicit and implicit
stage vectors are stored. Assume same abscissae, {c}, for both schemes.
Butcher Data must be provided either in a custom IMEXRKData object, or
using the IMEXRKData::Type for predefined tableaux. */
class IMEXRK : public ODESolver
{
protected:
IMEXRKData tableaux;
std::vector< Vector *> exp_stages;
std::vector< Vector *> imp_stages;
IMEXTimeDependentOperator *imex; // Spatial discretization.
public:
IMEXRK(IMEXRKData tableaux_) : ODESolver(), tableaux(tableaux_) { };
IMEXRK(IMEXRKData::Type type_) : ODESolver(), tableaux(type_) { };
~IMEXRK();
void Init(IMEXTimeDependentOperator &_imex);
void Step(Vector &x, double &t, double &dt) override;
};
/** Class holding BDF integrator data. Setting alpha < 0 (the default
constructor) defines alpha = 2/(q-1), corresponding to classical BDF
of order q. */
class BDFData
{
public:
enum Type {
BDF1 = 01, BDF2 = 02, BDF3 = 03, BDF4 = 04,
IMEX_BDF1 = 11, IMEX_BDF2 = 12, IMEX_BDF3 = 13,
IMEX_BDF4 = 14
};
BDFData() { };
BDFData(Type ID_, double alpha_=-1) : ID(ID_), alpha(alpha_) { SetData(); };
~BDFData() { };
int GetID() { return static_cast<int>(ID); };
void SetID(Type ID_, double alpha_=-1)
{
ID=ID_;
alpha = alpha_;
SetData();
};
void Print()
{
std::cout << "q = " << q << "\n";
std::cout << "alpha = " << alpha << "\n";
std::cout << "A:\n";
A.PrintMatlab();
std::cout << "Be:\n";
Be.PrintMatlab();
std::cout << "Bi:\n";
Bi.Print();
std::cout << "z:\n";
z0.Print();
};
double alpha;
int q; // Number of previous values stored
bool shifted_nodes; // false = clssical BDF, true = Polynomial BDF w/ shifted nodes
DenseMatrix A; // Previous solution coefficients
Vector Bi; // Implicit coefficients
DenseMatrix Be; // Explicit coefficients
Vector z0;
private:
Type ID;
void SetData();
void InitData();
};
/** Class for IMEX-BDF methods, including classical IMEX-BDF and IMEX-
Polynomial-BDF (IMEX-PBDF). IMEX-PBDF methods have an additional
alpha parameter, where larger alpha leads to smaller stability
regions and a smaller leading accuracy constant, while smaller
alpha leads to larger stabiltiy regions and a larger accuracy
constant. For classical methods, there are two implementations:
- ClassicalStep() stores previous solutions and the explicit
part of the operator evaluated on the solution, and
- ClassicalStepNoStore() does not store the explicit
component, but must re-evaluate q times during each time
step.
This option can be set via the recompute_exp input. The type of
scheme must be set through the BDFData structure or BDFData::Type.
There is also an option to use pointwise Lagrange interpolating
polynomials to provide an initial guess for the ImplicitSolve. This
is set via InterpolateGuess(). This option is only implemented for
PBDF. */
class IMEXBDF : public ODESolver
{
private:
BDFData data;
bool recompute_exp;
bool interpolate;
int initialized;
double dt_prev;
std::vector< Vector*> sols;
std::vector< Vector*> exp_sols;
IMEXTimeDependentOperator *imex; // Spatial discretization
IMEXRK *RKsolver;
std::vector<double> exp_nodes;
void AlphaStep(Vector &x, double &t, double &dt);
void ClassicalStep(Vector &x, double &t, double &dt);
void ClassicalStepNoStore(Vector &x, double &t, double &dt);
public:
IMEXBDF(BDFData data_, bool recompute_exp_=false) :
ODESolver(), data(data_), recompute_exp(recompute_exp_),
interpolate(false) { };
IMEXBDF(BDFData::Type scheme, bool recompute_exp_=false) :
ODESolver(), recompute_exp(recompute_exp_), interpolate(false)
{ data.SetID(scheme); };
IMEXBDF(BDFData::Type scheme, double alpha) :
ODESolver(), interpolate(false), recompute_exp(false)
{ data.SetID(scheme, alpha); };
~IMEXBDF();
void Init(IMEXTimeDependentOperator &_imex);
void Step(Vector &x, double &t, double &dt);
void InterpolateGuess() {interpolate = true; };
};
}
#endif
+1 -4
View File
@@ -31,6 +31,7 @@
#include "invariants.hpp"
#include "constraints.hpp"
#include "auxiliary.hpp"
#include "imex.hpp"
#ifdef MFEM_USE_AMGX
#include "amgxsolver.hpp"
@@ -40,10 +41,6 @@
#include "sundials.hpp"
#endif
#ifdef MFEM_USE_EPIC
#include "epic.hpp"
#endif
#ifdef MFEM_USE_HIOP
#include "hiop.hpp"
#endif
+11 -23
View File
@@ -33,7 +33,12 @@ int SparseMatrix::SparseMatrixCount = 0;
cusparseHandle_t SparseMatrix::handle = nullptr;
size_t SparseMatrix::bufferSize = 0;
void * SparseMatrix::dBuffer = nullptr;
#endif
# if CUSPARSE_VERSION >= 11400
# define MFEM_CUSPARSE_ALG CUSPARSE_SPMV_CSR_ALG1
# else
# define MFEM_CUSPARSE_ALG CUSPARSE_CSRMV_ALG1
# endif // CUSPARSE_VERSION >= 11400
#endif // MFEM_USE_CUDA
void SparseMatrix::InitCuSparse()
{
@@ -679,25 +684,16 @@ void SparseMatrix::AddMult(const Vector &x, Vector &y, const double a) const
cusparseCreateMatDescr(&matA_descr);
cusparseSetMatIndexBase(matA_descr, CUSPARSE_INDEX_BASE_ZERO);
cusparseSetMatType(matA_descr, CUSPARSE_MATRIX_TYPE_GENERAL);
#endif
initBuffers = true;
}
// Allocate kernel space. Buffer is shared between different sparsemats
size_t newBufferSize = 0;
#if CUDA_VERSION >= 11020
cusparseSpMV_bufferSize(handle, CUSPARSE_OPERATION_NON_TRANSPOSE, &alpha,
matA_descr,
vecX_descr, &beta, vecY_descr, CUDA_R_64F,
CUSPARSE_SPMV_CSR_ALG1, &newBufferSize);
#elif CUDA_VERSION >= 10010
cusparseSpMV_bufferSize(handle, CUSPARSE_OPERATION_NON_TRANSPOSE, &alpha,
matA_descr,
vecX_descr, &beta, vecY_descr, CUDA_R_64F,
CUSPARSE_CSRMV_ALG1, &newBufferSize);
#endif
MFEM_CUSPARSE_ALG, &newBufferSize);
// Check if we need to resize
if (newBufferSize > bufferSize)
@@ -707,30 +703,22 @@ void SparseMatrix::AddMult(const Vector &x, Vector &y, const double a) const
CuMemAlloc(&dBuffer, bufferSize);
}
#if CUDA_VERSION >= 11020
#if CUDA_VERSION >= 10010
// Update input/output vectors
cusparseDnVecSetValues(vecX_descr, const_cast<double *>(d_x));
cusparseDnVecSetValues(vecY_descr, d_y);
// Y = alpha A * X + beta * Y
cusparseSpMV(handle, CUSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA_descr,
vecX_descr, &beta, vecY_descr, CUDA_R_64F, CUSPARSE_SPMV_CSR_ALG1, dBuffer);
#elif CUDA_VERSION >= 10010
// Update input/output vectors
cusparseDnVecSetValues(vecX_descr, const_cast<double *>(d_x));
cusparseDnVecSetValues(vecY_descr, d_y);
// Y = alpha A * X + beta * Y
cusparseSpMV(handle, CUSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA_descr,
vecX_descr, &beta, vecY_descr, CUDA_R_64F, CUSPARSE_CSRMV_ALG1, dBuffer);
vecX_descr, &beta, vecY_descr, CUDA_R_64F, MFEM_CUSPARSE_ALG, dBuffer);
#else
cusparseDcsrmv(handle, CUSPARSE_OPERATION_NON_TRANSPOSE,
Height(), Width(), J.Capacity(),
&alpha, matA_descr,
const_cast<double *>(d_A), const_cast<int *>(d_I), const_cast<int *>(d_J),
const_cast<double *>(d_x), &beta, d_y);
#endif
#endif
#endif // CUDA_VERSION >= 10010
#endif // MFEM_USE_CUDA
}
else
{
+1 -32
View File
@@ -425,26 +425,6 @@ static SUNMatrix_ID MatGetID(SUNMatrix)
return (SUNMATRIX_CUSTOM);
}
//BEGIN WORKAROUND CODE
static SUNMatrix MatClone(SUNMatrix A)
{
SUNMatrix B = SUNMatNewEmpty();
MFEM_VERIFY(B, "error in MatClone()");
B->content = A->content;
int flag = SUNMatCopyOps(A, B);
MFEM_VERIFY(flag == SUNMAT_SUCCESS, "error in MatClone()");
return B;
}
static int MatCopy(SUNMatrix, SUNMatrix)
{
return 0;
}
// END WORKAROUN CODE
static void MatDestroy(SUNMatrix A)
{
if (A->content) { A->content = NULL; }
@@ -1513,12 +1493,7 @@ void ARKStepSolver::UseMFEMMassLinearSolver(int tdep)
MFEM_VERIFY(M, "error in SUNMatNewEmpty()");
M->content = this;
// BEGIN WORKAROUND CODE
// M->ops->getid = SUNMatGetID;
M->ops->getid = MatGetID;
M->ops->clone = MatClone;
M->ops->copy = MatCopy;
// END WORKAROUND CODE
M->ops->getid = SUNMatGetID;
M->ops->matvec = ARKStepSolver::MassMult1;
M->ops->destroy = MatDestroy;
@@ -1562,12 +1537,6 @@ void ARKStepSolver::SetSStolerances(double reltol, double abstol)
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSStolerances()");
}
void ARKStepSolver::SetMaxIter(int iterations)
{
flag = ARKStepSetMaxNonlinIters(sundials_mem, iterations);
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetMaxNonlinIters()");
}
void ARKStepSolver::SetMaxStep(double dt_max)
{
flag = ARKStepSetMaxStep(sundials_mem, dt_max);
-3
View File
@@ -666,9 +666,6 @@ public:
/// Set the scalar relative and scalar absolute tolerances.
void SetSStolerances(double reltol, double abstol);
/// Set the maximum number of nonlinear iterations per RK stage
void SetMaxIter(int iterations);
/// Set the maximum time step.
void SetMaxStep(double dt_max);
+5
View File
@@ -82,6 +82,11 @@ public:
Vector(double *data_, int size_)
{ data.Wrap(data_, size_, false); size = size_; }
/** @brief Create a Vector referencing a sub-vector of the Vector @a base
starting at the given offset, @a base_offset, and size @a size_. */
Vector(Vector &base, int base_offset, int size_)
: data(base.data, base_offset, size_), size(size_) { }
/// Create a Vector of size @a size_ using MemoryType @a mt.
Vector(int size_, MemoryType mt)
: data(size_, mt), size(size_) { }
+4 -5
View File
@@ -1,4 +1,4 @@
# Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
# Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
@@ -119,7 +119,7 @@ $(if $(word 2,$(SRC)),$(error Spaces in SRC = "$(SRC)" are not supported))
MFEM_GIT_STRING = $(shell [ -d $(MFEM_DIR)/.git ] && git -C $(MFEM_DIR) \
describe --all --long --abbrev=40 --dirty --always 2> /dev/null)
EXAMPLE_SUBDIRS = amgx ginkgo hiop petsc pumi sundials epic superlu
EXAMPLE_SUBDIRS = amgx ginkgo hiop petsc pumi sundials superlu
EXAMPLE_DIRS := examples $(addprefix examples/,$(EXAMPLE_SUBDIRS))
EXAMPLE_TEST_DIRS := examples
@@ -272,7 +272,7 @@ ifeq ($(MFEM_USE_LEGACY_OPENMP),YES)
endif
# List of MFEM dependencies, that require the *_LIB variable to be non-empty
MFEM_REQ_LIB_DEPS = SUPERLU MUMPS METIS FMS CONDUIT SIDRE LAPACK SUNDIALS EPIC MESQUITE\
MFEM_REQ_LIB_DEPS = SUPERLU MUMPS METIS FMS CONDUIT SIDRE LAPACK SUNDIALS MESQUITE\
SUITESPARSE STRUMPACK GINKGO GNUTLS NETCDF PETSC SLEPC MPFR PUMI HIOP GSLIB\
OCCA CEED RAJA UMPIRE MKL_CPARDISO AMGX CALIPER
@@ -333,7 +333,7 @@ endif
MFEM_DEFINES = MFEM_VERSION MFEM_VERSION_STRING MFEM_GIT_STRING MFEM_USE_MPI\
MFEM_USE_METIS MFEM_USE_METIS_5 MFEM_DEBUG MFEM_USE_EXCEPTIONS MFEM_USE_ZLIB\
MFEM_USE_LIBUNWIND MFEM_USE_LAPACK MFEM_THREAD_SAFE MFEM_USE_OPENMP\
MFEM_USE_LEGACY_OPENMP MFEM_USE_MEMALLOC MFEM_TIMER_TYPE MFEM_USE_SUNDIALS MFEM_USE_EPIC\
MFEM_USE_LEGACY_OPENMP MFEM_USE_MEMALLOC MFEM_TIMER_TYPE MFEM_USE_SUNDIALS\
MFEM_USE_MESQUITE MFEM_USE_SUITESPARSE MFEM_USE_GINKGO MFEM_USE_SUPERLU\
MFEM_USE_STRUMPACK MFEM_USE_GNUTLS MFEM_USE_NETCDF MFEM_USE_PETSC\
MFEM_USE_SLEPC MFEM_USE_MPFR MFEM_USE_SIDRE MFEM_USE_FMS MFEM_USE_CONDUIT\
@@ -650,7 +650,6 @@ status info:
$(info MFEM_USE_MEMALLOC = $(MFEM_USE_MEMALLOC))
$(info MFEM_TIMER_TYPE = $(MFEM_TIMER_TYPE))
$(info MFEM_USE_SUNDIALS = $(MFEM_USE_SUNDIALS))
$(info MFEM_USE_EPIC = $(MFEM_USE_EPIC))
$(info MFEM_USE_MESQUITE = $(MFEM_USE_MESQUITE))
$(info MFEM_USE_SUITESPARSE = $(MFEM_USE_SUITESPARSE))
$(info MFEM_USE_SUPERLU = $(MFEM_USE_SUPERLU))
+3 -3
View File
@@ -75,7 +75,7 @@ void Mesh::GetElementCenter(int i, Vector &center)
double Mesh::GetElementSize(ElementTransformation *T, int type)
{
DenseMatrix J(Dim);
DenseMatrix J(spaceDim,Dim);
Geometry::Type geom = T->GetGeometryType();
T->SetIntPoint(&Geometries.GetCenter(geom));
@@ -83,7 +83,7 @@ double Mesh::GetElementSize(ElementTransformation *T, int type)
if (type == 0)
{
return pow(fabs(J.Det()), 1./Dim);
return pow(fabs(J.Weight()), 1./Dim);
}
else if (type == 1)
{
@@ -102,7 +102,7 @@ double Mesh::GetElementSize(int i, int type)
double Mesh::GetElementSize(int i, const Vector &dir)
{
DenseMatrix J(Dim);
DenseMatrix J(spaceDim,Dim);
Vector d_hat(Dim);
GetElementJacobian(i, J);
J.MultTranspose(dir, d_hat);
+1 -1
View File
@@ -503,7 +503,7 @@ public:
Mesh& operator=(Mesh &&mesh);
/// Explicitly delete the copy assignment operator.
Mesh& operator=(Mesh &mesh) = delete;
Mesh& operator=(const Mesh &mesh) = delete;
/** @name Named mesh constructors.
-1
View File
@@ -2283,7 +2283,6 @@ void NCMesh::GetMeshComponents(Mesh &mesh) const
// left uninitialized here; they will be initialized later by the Mesh from
// Nodes -- here we just make sure mesh.vertices has the correct size.
mesh.elements.SetSize(NElements);
mesh.elements.SetSize(0);
mesh.boundary.SetSize(0);
+1 -1
View File
@@ -131,6 +131,7 @@ public:
int GetNVertices() const { return NVertices; }
int GetNEdges() const { return NEdges; }
int GetNFaces() const { return NFaces; }
virtual int GetNGhostElements() const { return 0; }
/** Perform the given batch of refinements. Please note that in the presence
of anisotropic splits additional refinements may be necessary to keep
@@ -160,7 +161,6 @@ public:
derefinements may have to be skipped to preserve mesh consistency. */
virtual void Derefine(const Array<int> &derefs);
// master/slave lists
/// Identifies a vertex/edge/face in both Mesh and NCMesh.
+31
View File
@@ -1339,6 +1339,37 @@ void ParNCMesh::LimitNCLevel(int max_nc_level)
}
}
void ParNCMesh::GetFineToCoarsePartitioning(const Array<int> &derefs,
Array<int> &new_ranks) const
{
new_ranks.SetSize(leaf_elements.Size()-GetNGhostElements());
for (int i = 0; i < leaf_elements.Size()-GetNGhostElements(); i++)
{
new_ranks[i] = elements[leaf_elements[i]].rank;
}
for (int i = 0; i < derefs.Size(); i++)
{
int row = derefs[i];
MFEM_VERIFY(row >= 0 && row < derefinements.Size(),
"invalid derefinement number.");
const int* fine = derefinements.GetRow(row);
int size = derefinements.RowSize(row);
int coarse_rank = INT_MAX;
for (int j = 0; j < size; j++)
{
int fine_rank = elements[leaf_elements[fine[j]]].rank;
coarse_rank = std::min(coarse_rank, fine_rank);
}
for (int j = 0; j < size; j++)
{
new_ranks[fine[j]] = coarse_rank;
}
}
}
void ParNCMesh::Derefine(const Array<int> &derefs)
{
MFEM_VERIFY(Dim < 3 || Iso,
+5 -1
View File
@@ -95,6 +95,11 @@ public:
in sync. The interface is identical. */
virtual void Derefine(const Array<int> &derefs);
/** Gets partitioning for the coarse mesh if the current fine mesh were to
be derefined. */
virtual void GetFineToCoarsePartitioning(const Array<int> &derefs,
Array<int> &new_ranks) const;
/** Migrate leaf elements of the global refinement hierarchy (including ghost
elements) so that each processor owns the same number of leaves (+-1).
The default partitioning strategy is based on equal splitting of the
@@ -243,7 +248,6 @@ public:
The debug mesh will have element attributes set to element rank + 1. */
void GetDebugMesh(Mesh &debug_mesh) const;
protected: // interface for ParMesh
friend class ParMesh;
+36 -25
View File
@@ -35,7 +35,7 @@
// mpirun -np 2 pfindpts -m ../../data/inline-wedge.mesh -o 3
// mpirun -np 2 pfindpts -m ../../data/amr-quad.mesh -o 2
// mpirun -np 2 pfindpts -m ../../data/rt-2d-q3.mesh -o 3 -mo 4 -ft 2
// mpirun -np 2 pfindpts -m ../../data/inline-quad.mesh -ft 1 -no-vis -sr0
#include "mfem.hpp"
@@ -74,6 +74,7 @@ int main (int argc, char *argv[])
bool visualization = true;
int fieldtype = 0;
int ncomp = 1;
bool search_on_rank_0 = false;
// Parse command-line options.
OptionsParser args(argc, argv);
@@ -94,6 +95,9 @@ int main (int argc, char *argv[])
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&search_on_rank_0, "-sr0", "--search-on-r0", "-no-sr0",
"--no-search-on-r0",
"Enable search only on rank 0 (disable to search points on all tasks).");
args.Parse();
if (!args.Good())
{
@@ -211,7 +215,7 @@ int main (int argc, char *argv[])
// Note that some points might be outside, if the mesh is not a box. Note
// also that all tasks search the same points (not mandatory).
const int pts_cnt_1D = 10;
const int pts_cnt = pow(pts_cnt_1D, dim);
int pts_cnt = pow(pts_cnt_1D, dim);
Vector vxyz(pts_cnt * dim);
if (dim == 2)
{
@@ -237,6 +241,12 @@ int main (int argc, char *argv[])
}
}
if ( (myid != 0) && (search_on_rank_0) )
{
pts_cnt = 0;
vxyz.Destroy();
}
// Find and Interpolate FE function values on the desired points.
Vector interp_vals(pts_cnt*vec_dim);
FindPointsGSLIB finder(MPI_COMM_WORLD);
@@ -246,36 +256,37 @@ int main (int argc, char *argv[])
Array<unsigned int> task_id_out = finder.GetProc();
Vector dist_p_out = finder.GetDist();
int face_pts = 0, not_found = 0, found_loc = 0, found_away = 0;
double max_err = 0.0, max_dist = 0.0;
Vector pos(dim);
int npt = 0;
for (int j = 0; j < vec_dim; j++)
// Print the results for task 0 since either 1) all tasks have the
// same set of points or 2) only task 0 has any points.
if (myid == 0 )
{
for (int i = 0; i < pts_cnt; i++)
int face_pts = 0, not_found = 0, found_loc = 0, found_away = 0;
double max_err = 0.0, max_dist = 0.0;
Vector pos(dim);
int npt = 0;
for (int j = 0; j < vec_dim; j++)
{
if (j == 0)
for (int i = 0; i < pts_cnt; i++)
{
(task_id_out[i] == (unsigned)myid) ? found_loc++ : found_away++;
}
if (j == 0)
{
(task_id_out[i] == (unsigned)myid) ? found_loc++ : found_away++;
}
if (code_out[i] < 2)
{
for (int d = 0; d < dim; d++) { pos(d) = vxyz(d * pts_cnt + i); }
Vector exact_val(vec_dim);
F_exact(pos, exact_val);
max_err = std::max(max_err, fabs(exact_val(j) - interp_vals(npt)));
max_dist = std::max(max_dist, dist_p_out(i));
if (code_out[i] == 1 && j == 0) { face_pts++; }
if (code_out[i] < 2)
{
for (int d = 0; d < dim; d++) { pos(d) = vxyz(d * pts_cnt + i); }
Vector exact_val(vec_dim);
F_exact(pos, exact_val);
max_err = std::max(max_err, fabs(exact_val(j) - interp_vals(npt)));
max_dist = std::max(max_dist, dist_p_out(i));
if (code_out[i] == 1 && j == 0) { face_pts++; }
}
else { if (j == 0) { not_found++; } }
npt++;
}
else { if (j == 0) { not_found++; } }
npt++;
}
}
// Print the results for task 0 since all tasks have the same set of points.
if (myid == 0)
{
cout << setprecision(16)
<< "Searched unique points: " << pts_cnt
<< "\nFound on local mesh: " << found_loc
+107 -17
View File
@@ -39,6 +39,11 @@
// Adapted analytic shape+orientation:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
//
// Adapted analytic shape and/or size with hr-adaptivity:
// mesh-optimizer -m square01.mesh -o 2 -tid 9 -ni 50 -li 20 -hmid 55 -mid 7 -hr
// mesh-optimizer -m square01.mesh -o 2 -tid 10 -ni 50 -li 20 -hmid 55 -mid 7 -hr
// mesh-optimizer -m square01.mesh -o 2 -tid 11 -ni 50 -li 20 -hmid 58 -mid 7 -hr
//
// Adapted discrete size:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 80 -tid 5 -ni 50 -qo 4 -nor
// Adapted discrete size 3D with PA:
@@ -123,6 +128,8 @@ int main(int argc, char *argv[])
int max_lin_iter = 100;
bool move_bnd = true;
int combomet = 0;
bool hradaptivity = false;
int h_metric_id = -1;
bool normalization = false;
bool visualization = true;
int verbosity_level = 0;
@@ -131,6 +138,8 @@ int main(int argc, char *argv[])
bool exactaction = false;
const char *devopt = "cpu";
bool pa = false;
int n_hr_iter = 5;
int n_h_iter = 1;
// 1. Parse command-line options.
OptionsParser args(argc, argv);
@@ -221,6 +230,12 @@ int main(int argc, char *argv[])
"0: Use single metric\n\t"
"1: Shape + space-dependent size given analytically\n\t"
"2: Shape + adapted size given discretely; shared target");
args.AddOption(&hradaptivity, "-hr", "--hr-adaptivity", "-no-hr",
"--no-hr-adaptivity",
"Enable hr-adaptivity.");
args.AddOption(&h_metric_id, "-hmid", "--h-metric",
"Same options as metric_id. Used to determine refinement"
" type for each element if h-adaptivity is enabled.");
args.AddOption(&normalization, "-nor", "--normalization", "-no-nor",
"--no-normalization",
"Make all terms in the optimization functional unitless.");
@@ -241,6 +256,11 @@ int main(int argc, char *argv[])
"Device configuration string, see Device::Configure().");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&n_hr_iter, "-nhr", "--n_hr_iter",
"Number of hr-adaptivity iterations.");
args.AddOption(&n_h_iter, "-nh", "--n_h_iter",
"Number of h-adaptivity iterations per r-adaptivity"
"iteration.");
args.Parse();
if (!args.Good())
{
@@ -249,6 +269,13 @@ int main(int argc, char *argv[])
}
args.PrintOptions(cout);
if (h_metric_id < 0) { h_metric_id = metric_id; }
if (hradaptivity)
{
MFEM_VERIFY(strcmp(devopt,"cpu")==0, "HR-adaptivity is currently only"
" supported on cpus.");
}
Device device(devopt);
device.Print();
@@ -261,7 +288,9 @@ int main(int argc, char *argv[])
else { cout << "(NONE)"; }
cout << endl;
// 3. Define a finite element space on the mesh. Here we use vector finite
if (hradaptivity) { mesh->EnsureNCMesh(); }
// 3. Define a finite element space on the mesh-> Here we use vector finite
// elements which are tensor products of quadratic finite elements. The
// number of components in the vector finite element space is specified by
// the last parameter of the FiniteElementSpace constructor.
@@ -392,9 +421,41 @@ int main(int argc, char *argv[])
cout << "Unknown metric_id: " << metric_id << endl;
return 3;
}
TMOP_QualityMetric *h_metric = NULL;
if (hradaptivity)
{
switch (h_metric_id)
{
case 1: h_metric = new TMOP_Metric_001; break;
case 2: h_metric = new TMOP_Metric_002; break;
case 7: h_metric = new TMOP_Metric_007; break;
case 9: h_metric = new TMOP_Metric_009; break;
case 55: h_metric = new TMOP_Metric_055; break;
case 56: h_metric = new TMOP_Metric_056; break;
case 58: h_metric = new TMOP_Metric_058; break;
case 77: h_metric = new TMOP_Metric_077; break;
case 315: h_metric = new TMOP_Metric_315; break;
case 316: h_metric = new TMOP_Metric_316; break;
case 321: h_metric = new TMOP_Metric_321; break;
default: cout << "Metric_id not supported for h-adaptivity: " << h_metric_id <<
endl;
return 3;
}
}
if (metric_id < 300 || h_metric_id < 300)
{
MFEM_VERIFY(dim == 2, "Incompatible metric for 3D meshes");
}
if (metric_id >= 300 || h_metric_id >= 300)
{
MFEM_VERIFY(dim == 3, "Incompatible metric for 2D meshes");
}
TargetConstructor::TargetType target_t;
TargetConstructor *target_c = NULL;
HessianCoefficient *adapt_coeff = NULL;
HRHessianCoefficient *hr_adapt_coeff = NULL;
H1_FECollection ind_fec(mesh_poly_deg, dim);
FiniteElementSpace ind_fes(mesh, &ind_fec);
FiniteElementSpace ind_fesv(mesh, &ind_fec, dim);
@@ -604,6 +665,18 @@ int main(int argc, char *argv[])
target_c = tc;
break;
}
// Targets used for hr-adaptivity tests.
case 9: // size target in an annular region.
case 10: // size+aspect-ratio in an annular region.
case 11: // size+aspect-ratio target for a rotate sine wave
{
target_t = TargetConstructor::GIVEN_FULL;
AnalyticAdaptTC *tc = new AnalyticAdaptTC(target_t);
hr_adapt_coeff = new HRHessianCoefficient(dim, target_id - 9);
tc->SetAnalyticTargetSpec(NULL, NULL, hr_adapt_coeff);
target_c = tc;
break;
}
default: cout << "Unknown target_id: " << target_id << endl; return 3;
}
if (target_c == NULL)
@@ -611,7 +684,8 @@ int main(int argc, char *argv[])
target_c = new TargetConstructor(target_t);
}
target_c->SetNodes(x0);
TMOP_Integrator *he_nlf_integ = new TMOP_Integrator(metric, target_c);
TMOP_Integrator *he_nlf_integ = new TMOP_Integrator(metric, target_c,
h_metric);
// Finite differences for computations of derivatives.
if (fdscheme)
@@ -721,10 +795,10 @@ int main(int argc, char *argv[])
TargetConstructor::IDEAL_SHAPE_EQUAL_SIZE);
target_c2->SetVolumeScale(0.01);
target_c2->SetNodes(x0);
he_nlf_integ2 = new TMOP_Integrator(metric2, target_c2);
he_nlf_integ2 = new TMOP_Integrator(metric2, target_c2, h_metric);
he_nlf_integ2->SetCoefficient(coeff2);
}
else { he_nlf_integ2 = new TMOP_Integrator(metric2, target_c); }
else { he_nlf_integ2 = new TMOP_Integrator(metric2, target_c, h_metric); }
he_nlf_integ2->SetIntegrationRules(*irules, quad_order);
if (fdscheme) { he_nlf_integ2->EnableFiniteDifferences(x); }
he_nlf_integ2->SetExactActionFlag(exactaction);
@@ -778,7 +852,9 @@ int main(int argc, char *argv[])
tauval -= 0.01 * h0.Min();
}
const double init_energy = a.GetGridFunctionEnergy(x);
// For HR tests, the energy is normalized by the number of elements.
const double init_energy = a.GetGridFunctionEnergy(x) /
(hradaptivity ? mesh->GetNE() : 1);
// Visualize the starting mesh and metric values.
// Note that for combinations of metrics, this only shows the first metric.
@@ -906,13 +982,25 @@ int main(int argc, char *argv[])
solver.SetAdaptiveLinRtol(solver_art_type, 0.5, 0.9);
}
solver.SetPrintLevel(verbosity_level >= 1 ? 1 : -1);
solver.SetOperator(a);
solver.Mult(b, x.GetTrueVector());
x.SetFromTrueVector();
if (solver.GetConverged() == false)
// hr-adaptivity solver.
// If hr-adaptivity is disabled, r-adaptivity is done once using the
// TMOPNewtonSolver.
// Otherwise, "hr_iter" iterations of r-adaptivity are done followed by
// "h_per_r_iter" iterations of h-adaptivity after each r-adaptivity.
// The solver terminates if an h-adaptivity iteration does not modify
// any element in the mesh.
TMOPHRSolver hr_solver(*mesh, a, solver,
x, move_bnd, hradaptivity,
mesh_poly_deg, h_metric_id,
n_hr_iter, n_h_iter);
hr_solver.AddGridFunctionForUpdate(&x0);
if (adapt_lim_const > 0.)
{
cout << "Nonlinear solver: rtol = " << solver_rtol << " not achieved.\n";
hr_solver.AddGridFunctionForUpdate(&zeta_0);
hr_solver.AddFESpaceForUpdate(&ind_fes);
}
hr_solver.Mult();
// 15. Save the optimized mesh to a file. This output can be viewed later
// using GLVis: "glvis -m optimized.mesh".
@@ -922,14 +1010,15 @@ int main(int argc, char *argv[])
mesh->Print(mesh_ofs);
}
// 16. Compute the amount of energy decrease.
const double fin_energy = a.GetGridFunctionEnergy(x);
const double fin_energy = a.GetGridFunctionEnergy(x) /
(hradaptivity ? mesh->GetNE() : 1);
double metric_part = fin_energy;
if (lim_const > 0.0 || adapt_lim_const > 0.0)
{
lim_coeff.constant = 0.0;
coef_zeta.constant = 0.0;
metric_part = a.GetGridFunctionEnergy(x);
metric_part = a.GetGridFunctionEnergy(x) /
(hradaptivity ? mesh->GetNE() : 1);
lim_coeff.constant = lim_const;
coef_zeta.constant = adapt_lim_const;
}
@@ -942,7 +1031,7 @@ int main(int argc, char *argv[])
cout << "The strain energy decreased by: " << setprecision(12)
<< (init_energy - fin_energy) * 100.0 / init_energy << " %." << endl;
// 17. Visualize the final mesh and metric values.
// 16. Visualize the final mesh and metric values.
if (visualization)
{
char title[] = "Final metric values";
@@ -956,13 +1045,13 @@ int main(int argc, char *argv[])
600, 600, 300, 300);
}
// 18. Visualize the mesh displacement.
// 17. Visualize the mesh displacement.
if (visualization)
{
x0 -= x;
osockstream sock(19916, "localhost");
sock << "solution\n";
mesh->Print(sock);
x0 -= x;
x0.Save(sock);
sock.send();
sock << "window_title 'Displacements'\n"
@@ -971,7 +1060,6 @@ int main(int argc, char *argv[])
<< "keys jRmclA" << endl;
}
// 19. Free the used memory.
delete S;
delete S_prec;
delete target_c2;
@@ -979,7 +1067,9 @@ int main(int argc, char *argv[])
delete coeff1;
delete adapt_evaluator;
delete target_c;
delete hr_adapt_coeff;
delete adapt_coeff;
delete h_metric;
delete metric;
delete fespace;
delete fec;
+131 -5
View File
@@ -90,8 +90,6 @@ double discrete_aspr_2d(const Vector &x)
double th = 22.5*M_PI/180.;
double xn = cos(th)*xc + sin(th)*yc;
double yn = -sin(th)*xc + cos(th)*yc;
// double th2 = (th > 45.*M_PI/180) ? M_PI/2 - th : th;
// double stretch = 1/cos(th2);
xc = xn; yc = yn;
double tfac = 20;
@@ -223,6 +221,134 @@ public:
}
};
class HRHessianCoefficient : public TMOPMatrixCoefficient
{
private:
int dim;
// 0 - size target in an annular region,
// 1 - size+aspect-ratio in an annular region,
// 2 - size+aspect-ratio target for a rotate sine wave.
int hr_target_type;
public:
HRHessianCoefficient(int dim_, int hr_target_type_ = 0)
: TMOPMatrixCoefficient(dim_), dim(dim_),
hr_target_type(hr_target_type_) { }
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip)
{
Vector pos(3);
T.Transform(ip, pos);
if (hr_target_type == 0) // size only circle
{
double small = 0.001, big = 0.01;
if (dim == 3) { small = 0.005, big = 0.1; }
const double xc = pos(0) - 0.5, yc = pos(1) - 0.5;
double zc;
if (dim == 3) { zc = pos(2) - 0.5; }
double r = sqrt(xc*xc + yc*yc);
if (dim == 3) { r = sqrt(xc*xc + yc*yc + zc*zc); }
double r1 = 0.15; double r2 = 0.35; double sf=30.0;
const double tan1 = std::tanh(sf*(r-r1)),
tan2 = std::tanh(sf*(r-r2));
double ind = (tan1 - tan2);
if (ind > 1.0) {ind = 1.;}
if (ind < 0.0) {ind = 0.;}
double val = ind * small + (1.0 - ind) * big;
K = 0.0;
K(0, 0) = 1.0;
K(0, 1) = 0.0;
K(1, 0) = 0.0;
K(1, 1) = 1.0;
K(0, 0) *= pow(val,0.5);
K(1, 1) *= pow(val,0.5);
if (dim == 3) { K(2, 2) = pow(val,0.5); }
}
else if (hr_target_type == 1) // circle with size and AR
{
const double small = 0.001, big = 0.01;
const double xc = pos(0)-0.5, yc = pos(1)-0.5;
const double rv = xc*xc + yc*yc;
double r = 0;
if (rv>0.) {r = sqrt(rv);}
double r1 = 0.2; double r2 = 0.3; double sf=30.0;
const double szfac = 1;
const double asfac = 4;
const double eps2 = szfac/asfac;
const double eps1 = szfac;
double tan1 = std::tanh(sf*(r-r1)+1),
tan2 = std::tanh(sf*(r-r2)-1);
double wgt = 0.5*(tan1-tan2);
tan1 = std::tanh(sf*(r-r1)),
tan2 = std::tanh(sf*(r-r2));
double ind = (tan1 - tan2);
if (ind > 1.0) {ind = 1.;}
if (ind < 0.0) {ind = 0.;}
double szval = ind * small + (1.0 - ind) * big;
double th = std::atan2(yc,xc)*180./M_PI;
if (wgt > 1) { wgt = 1; }
if (wgt < 0) { wgt = 0; }
double maxval = eps2 + eps1*(1-wgt)*(1-wgt);
double minval = eps1;
double avgval = 0.5*(maxval+minval);
double ampval = 0.5*(maxval-minval);
double val1 = avgval + ampval*sin(2.*th*M_PI/180.+90*M_PI/180.);
double val2 = avgval + ampval*sin(2.*th*M_PI/180.-90*M_PI/180.);
K(0,1) = 0.0;
K(1,0) = 0.0;
K(0,0) = val1;
K(1,1) = val2;
K(0,0) *= pow(szval,0.5);
K(1,1) *= pow(szval,0.5);
}
else if (hr_target_type == 2) // sharp rotated sine wave
{
double xc = pos(0)-0.5, yc = pos(1)-0.5;
double th = 15.5*M_PI/180.;
double xn = cos(th)*xc + sin(th)*yc;
double yn = -sin(th)*xc + cos(th)*yc;
double th2 = (th > 45.*M_PI/180) ? M_PI/2 - th : th;
double stretch = 1/cos(th2);
xc = xn/stretch;
yc = yn;
double tfac = 20;
double s1 = 3;
double s2 = 2;
double yl1 = -0.025;
double yl2 = 0.025;
double wgt = std::tanh((tfac*(yc-yl1) + s2*std::sin(s1*M_PI*xc)) + 1) -
std::tanh((tfac*(yc-yl2) + s2*std::sin(s1*M_PI*xc)) - 1);
if (wgt > 1) { wgt = 1; }
if (wgt < 0) { wgt = 0; }
const double eps2 = 25;
const double eps1 = 1;
K(1,1) = eps1/eps2 + eps1*(1-wgt)*(1-wgt);
K(0,0) = eps1;
K(0,1) = 0.0;
K(1,0) = 0.0;
}
else { MFEM_ABORT("Unsupported option / wrong input."); }
}
virtual void EvalGrad(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip, int comp)
{
K = 0.;
}
};
// Additional IntegrationRules that can be used with the --quad-type option.
IntegrationRules IntRulesLo(0, Quadrature1D::GaussLobatto);
IntegrationRules IntRulesCU(0, Quadrature1D::ClosedUniform);
@@ -252,13 +378,13 @@ double adapt_lim_fun(const Vector &x)
void DiffuseField(GridFunction &field, int smooth_steps)
{
//Setup the Laplacian operator
// Setup the Laplacian operator
BilinearForm *Lap = new BilinearForm(field.FESpace());
Lap->AddDomainIntegrator(new DiffusionIntegrator());
Lap->Assemble();
Lap->Finalize();
//Setup the smoothing operator
// Setup the smoothing operator
DSmoother *S = new DSmoother(0,1.0,smooth_steps);
S->iterative_mode = true;
S->SetOperator(Lap->SpMat());
@@ -274,7 +400,7 @@ void DiffuseField(GridFunction &field, int smooth_steps)
#ifdef MFEM_USE_MPI
void DiffuseField(ParGridFunction &field, int smooth_steps)
{
//Setup the Laplacian operator
// Setup the Laplacian operator
ParBilinearForm *Lap = new ParBilinearForm(field.ParFESpace());
Lap->AddDomainIntegrator(new DiffusionIntegrator());
Lap->Assemble();
+107 -14
View File
@@ -39,6 +39,11 @@
// Adapted analytic shape+orientation:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
//
// Adapted analytic shape and/or size with hr-adaptivity:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -tid 9 -ni 50 -li 20 -hmid 55 -mid 7 -hr
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -tid 10 -ni 50 -li 20 -hmid 55 -mid 7 -hr
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -tid 11 -ni 50 -li 20 -hmid 58 -mid 7 -hr
//
// Adapted discrete size:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 80 -tid 5 -ni 50 -qo 4 -nor
// Adapted discrete size 3D with PA:
@@ -130,6 +135,8 @@ int main (int argc, char *argv[])
int max_lin_iter = 100;
bool move_bnd = true;
int combomet = 0;
bool hradaptivity = false;
int h_metric_id = -1;
bool normalization = false;
bool visualization = true;
int verbosity_level = 0;
@@ -138,6 +145,8 @@ int main (int argc, char *argv[])
bool exactaction = false;
const char *devopt = "cpu";
bool pa = false;
int n_hr_iter = 5;
int n_h_iter = 1;
// 2. Parse command-line options.
OptionsParser args(argc, argv);
@@ -230,6 +239,12 @@ int main (int argc, char *argv[])
"0: Use single metric\n\t"
"1: Shape + space-dependent size given analytically\n\t"
"2: Shape + adapted size given discretely; shared target");
args.AddOption(&hradaptivity, "-hr", "--hr-adaptivity", "-no-hr",
"--no-hr-adaptivity",
"Enable hr-adaptivity.");
args.AddOption(&h_metric_id, "-hmid", "--h-metric",
"Same options as metric_id. Used to determine refinement"
" type for each element if h-adaptivity is enabled.");
args.AddOption(&normalization, "-nor", "--normalization", "-no-nor",
"--no-normalization",
"Make all terms in the optimization functional unitless.");
@@ -250,6 +265,11 @@ int main (int argc, char *argv[])
"Device configuration string, see Device::Configure().");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&n_hr_iter, "-nhr", "--n_hr_iter",
"Number of hr-adaptivity iterations.");
args.AddOption(&n_h_iter, "-nh", "--n_h_iter",
"Number of h-adaptivity iterations per r-adaptivity"
"iteration.");
args.Parse();
if (!args.Good())
{
@@ -257,7 +277,13 @@ int main (int argc, char *argv[])
return 1;
}
if (myid == 0) { args.PrintOptions(cout); }
if (h_metric_id < 0) { h_metric_id = metric_id; }
if (hradaptivity)
{
MFEM_VERIFY(strcmp(devopt,"cpu")==0, "HR-adaptivity is currently only"
" supported on cpus.");
}
Device device(devopt);
if (myid == 0) { device.Print();}
@@ -276,6 +302,7 @@ int main (int argc, char *argv[])
cout << endl;
}
if (hradaptivity) { mesh->EnsureNCMesh(); }
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
@@ -422,9 +449,41 @@ int main (int argc, char *argv[])
if (myid == 0) { cout << "Unknown metric_id: " << metric_id << endl; }
return 3;
}
TMOP_QualityMetric *h_metric = NULL;
if (hradaptivity)
{
switch (h_metric_id)
{
case 1: h_metric = new TMOP_Metric_001; break;
case 2: h_metric = new TMOP_Metric_002; break;
case 7: h_metric = new TMOP_Metric_007; break;
case 9: h_metric = new TMOP_Metric_009; break;
case 55: h_metric = new TMOP_Metric_055; break;
case 56: h_metric = new TMOP_Metric_056; break;
case 58: h_metric = new TMOP_Metric_058; break;
case 77: h_metric = new TMOP_Metric_077; break;
case 315: h_metric = new TMOP_Metric_315; break;
case 316: h_metric = new TMOP_Metric_316; break;
case 321: h_metric = new TMOP_Metric_321; break;
default: cout << "Metric_id not supported for h-adaptivity: " << h_metric_id <<
endl;
return 3;
}
}
if (metric_id < 300 || h_metric_id < 300)
{
MFEM_VERIFY(dim == 2, "Incompatible metric for 3D meshes");
}
if (metric_id >= 300 || h_metric_id >= 300)
{
MFEM_VERIFY(dim == 3, "Incompatible metric for 2D meshes");
}
TargetConstructor::TargetType target_t;
TargetConstructor *target_c = NULL;
HessianCoefficient *adapt_coeff = NULL;
HRHessianCoefficient *hr_adapt_coeff = NULL;
H1_FECollection ind_fec(mesh_poly_deg, dim);
ParFiniteElementSpace ind_fes(pmesh, &ind_fec);
ParFiniteElementSpace ind_fesv(pmesh, &ind_fec, dim);
@@ -639,6 +698,18 @@ int main (int argc, char *argv[])
target_c = tc;
break;
}
// Targets used for hr-adaptivity tests.
case 9: // size target in an annular region.
case 10: // size+aspect-ratio in an annular region.
case 11: // size+aspect-ratio target for a rotate sine wave
{
target_t = TargetConstructor::GIVEN_FULL;
AnalyticAdaptTC *tc = new AnalyticAdaptTC(target_t);
hr_adapt_coeff = new HRHessianCoefficient(dim, target_id - 9);
tc->SetAnalyticTargetSpec(NULL, NULL, hr_adapt_coeff);
target_c = tc;
break;
}
default:
if (myid == 0) { cout << "Unknown target_id: " << target_id << endl; }
return 3;
@@ -649,13 +720,13 @@ int main (int argc, char *argv[])
target_c = new TargetConstructor(target_t, MPI_COMM_WORLD);
}
target_c->SetNodes(x0);
TMOP_Integrator *he_nlf_integ= new TMOP_Integrator(metric, target_c);
TMOP_Integrator *he_nlf_integ = new TMOP_Integrator(metric, target_c,
h_metric);
// Finite differences for computations of derivatives.
if (fdscheme)
{
MFEM_VERIFY(pa == false, "PA for finite differences is not implemented.");
he_nlf_integ->EnableFiniteDifferences(x);
}
he_nlf_integ->SetExactActionFlag(exactaction);
@@ -753,7 +824,8 @@ int main (int argc, char *argv[])
he_nlf_integ->SetCoefficient(*coeff1);
// Second metric.
metric2 = new TMOP_Metric_077;
if (dim == 2) { metric2 = new TMOP_Metric_077; }
else { metric2 = new TMOP_Metric_315; }
TMOP_Integrator *he_nlf_integ2 = NULL;
if (combomet == 1)
{
@@ -761,10 +833,10 @@ int main (int argc, char *argv[])
TargetConstructor::IDEAL_SHAPE_EQUAL_SIZE, MPI_COMM_WORLD);
target_c2->SetVolumeScale(0.01);
target_c2->SetNodes(x0);
he_nlf_integ2 = new TMOP_Integrator(metric2, target_c2);
he_nlf_integ2 = new TMOP_Integrator(metric2, target_c2, h_metric);
he_nlf_integ2->SetCoefficient(coeff2);
}
else { he_nlf_integ2 = new TMOP_Integrator(metric2, target_c); }
else { he_nlf_integ2 = new TMOP_Integrator(metric2, target_c, h_metric); }
he_nlf_integ2->SetIntegrationRules(*irules, quad_order);
if (fdscheme) { he_nlf_integ2->EnableFiniteDifferences(x); }
he_nlf_integ2->SetExactActionFlag(exactaction);
@@ -777,7 +849,10 @@ int main (int argc, char *argv[])
a.AddDomainIntegrator(combo);
}
else { a.AddDomainIntegrator(he_nlf_integ); }
else
{
a.AddDomainIntegrator(he_nlf_integ);
}
if (pa) { a.Setup(); }
@@ -821,7 +896,9 @@ int main (int argc, char *argv[])
tauval -= 0.01 * h0min_all;
}
const double init_energy = a.GetParGridFunctionEnergy(x);
// For HR tests, the energy is normalized by the number of elements.
const double init_energy = a.GetParGridFunctionEnergy(x) /
(hradaptivity ? pmesh->GetGlobalNE() : 1);
// Visualize the starting mesh and metric values.
// Note that for combinations of metrics, this only shows the first metric.
@@ -952,13 +1029,25 @@ int main (int argc, char *argv[])
solver.SetAdaptiveLinRtol(solver_art_type, 0.5, 0.9);
}
solver.SetPrintLevel(verbosity_level >= 1 ? 1 : -1);
solver.SetOperator(a);
solver.Mult(b, x.GetTrueVector());
x.SetFromTrueVector();
if (myid == 0 && solver.GetConverged() == false)
// hr-adaptivity solver.
// If hr-adaptivity is disabled, r-adaptivity is done once using the
// TMOPNewtonSolver.
// Otherwise, "hr_iter" iterations of r-adaptivity are done followed by
// "h_per_r_iter" iterations of h-adaptivity after each r-adaptivity.
// The solver terminates if an h-adaptivity iteration does not modify
// any element in the mesh.
TMOPHRSolver hr_solver(*pmesh, a, solver,
x, move_bnd, hradaptivity,
mesh_poly_deg, h_metric_id,
n_hr_iter, n_h_iter);
hr_solver.AddGridFunctionForUpdate(&x0);
if (adapt_lim_const > 0.)
{
cout << "Nonlinear solver: rtol = " << solver_rtol << " not achieved.\n";
hr_solver.AddGridFunctionForUpdate(&zeta_0);
hr_solver.AddFESpaceForUpdate(&ind_fes);
}
hr_solver.Mult();
// 16. Save the optimized mesh to a file. This output can be viewed later
// using GLVis: "glvis -m optimized -np num_mpi_tasks".
@@ -971,13 +1060,15 @@ int main (int argc, char *argv[])
}
// 17. Compute the amount of energy decrease.
const double fin_energy = a.GetParGridFunctionEnergy(x);
const double fin_energy = a.GetParGridFunctionEnergy(x) /
(hradaptivity ? pmesh->GetGlobalNE() : 1);
double metric_part = fin_energy;
if (lim_const > 0.0 || adapt_lim_const > 0.0)
{
lim_coeff.constant = 0.0;
coef_zeta.constant = 0.0;
metric_part = a.GetParGridFunctionEnergy(x);
metric_part = a.GetParGridFunctionEnergy(x) /
(hradaptivity ? pmesh->GetGlobalNE() : 1);
lim_coeff.constant = lim_const;
coef_zeta.constant = adapt_lim_const;
}
@@ -1036,7 +1127,9 @@ int main (int argc, char *argv[])
delete coeff1;
delete adapt_evaluator;
delete target_c;
delete hr_adapt_coeff;
delete adapt_coeff;
delete h_metric;
delete metric;
delete pfespace;
delete fec;
+2
View File
@@ -56,8 +56,10 @@ then
prefix_opt="--prefix=${prefix}"
fi
echo "Fetching uberenv."
tests/gitlab/get_mfem_uberenv || ( echo "Error fetching Uberenv" && exit 1 );
echo "Installing dependencies."
python tests/uberenv/uberenv.py --spec="${spec}" "${upstream_opt}" "${prefix_opt}"
fi
+1 -1
View File
@@ -18,7 +18,7 @@ set -o errexit
set -o nounset
uberenv_url="https://github.com/mfem/mfem-uberenv.git"
uberenv_ref="dbe35eb4dbeaabc7184ad110fcb98377092baeb0"
uberenv_ref="222e3743377d8c903e044f80ae44624e1668feb4"
[[ ! -d tests/uberenv ]] && git clone ${uberenv_url} tests/uberenv
cd tests/uberenv