Compare commits

...
Author SHA1 Message Date
blaz af84ca1ef5 wip 2021-07-10 10:33:09 -07:00
blaz c8a8562ea4 modification and examples 2021-07-02 11:21:48 -07:00
blaz a32ca0cb89 Another Lambda example 2021-05-26 15:53:39 -07:00
blaz 2e92b44070 Lambda function differentiation 2021-05-26 15:11:05 -07:00
blaz 2aa374efe8 Example of auto diff 2021-05-25 21:00:57 -07:00
blaz 476e642935 added cmake and make files for Eigen and CoDiPack 2021-05-25 17:53:33 -07:00
blaz 0da14179f7 initial copy frm fadg branch 2021-05-25 16:38:32 -07:00
31 changed files with 7588 additions and 3 deletions
+25 -1
View File
@@ -351,6 +351,30 @@ if (MFEM_USE_HIOP)
# find_package updates HIOP_FOUND, HIOP_INCLUDE_DIRS, HIOP_LIBRARIES
endif()
# ADEPT package
if (MFEM_USE_ADEPT)
find_package(ADEPT REQUIRED)
# find_package updates ADEPT_FOUND, ADEPT_INCLUDE_DIRS, ADEPT_LIBRARIES
endif()
# FADBAD++ package
if (MFEM_USE_FADBADPP)
find_package(FADBADPP REQUIRED)
# find_package updates FADBADPP_FOUND, FADBADPP_INCLUDE_DIRS, FADBADPP_LIBRARIES
endif()
# CoDiPack package
if (MFEM_USE_CODIPACK)
find_package(CODIPACK REQUIRED)
# find_package updates CODIPACK_FOUND, CODIPACK_INCLUDE_DIRS, CODIPACK_LIBRARIES
endif()
# Eigen package
if (MFEM_USE_EIGEN)
find_package(EIGEN REQUIRED)
# find_package updates EIGEN_FOUND, EIGEN_INCLUDE_DIRS, EIGEN_LIBRARIES
endif()
# OCCA
if (MFEM_USE_OCCA)
find_package(OCCA REQUIRED)
@@ -417,7 +441,7 @@ endif()
set(MFEM_TPLS MPI_CXX OPENMP HYPRE BLAS LAPACK SuperLUDist METIS SuiteSparse SUNDIALS PETSC
SLEPC MESQUITE MUMPS STRUMPACK AXOM CONDUIT Ginkgo GNUTLS GSLIB NETCDF
MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE ADIOS2
CUSPARSE MKL_CPARDISO AMGX CALIPER)
CUSPARSE MKL_CPARDISO AMGX CALIPER FADBADPP ADEPT CODIPACK EIGEN)
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
set(TPL_LIBRARIES "")
set(TPL_INCLUDE_DIRS "")
+50
View File
@@ -462,6 +462,27 @@ MFEM_USE_HIOP = YES/NO
Enable the usage of HiOp (https://github.com/LLNL/hiop) in MFEM. HiOp is an
HPC solver for nonlinear optimization problems.
MFEM_USE_ADEPT = YES/NO
Enable automatic differentiation using the ADEPT library.
(http://www.met.reading.ac.uk/clouds/adept)
Please, compile the library with flag --disable-openmp.
MFEM_USE_FADBADPP = YES/NO
Enable automatic differentiation using the FADBAD++ library.
www.fadbad.com/fadbad.html
MFEM_USE_CODIPACK = YES/NO
Enable automatic differentiation using the CoDiPack library.
www.scicomp.uni-kl.de/codi/
MFEM_USE_EIGEN = YES/NO
Enable the Eigen library.
https://gitlab.com/libeigen/eigen
MFEM_USE_ADFORWARD = YES/NO
Enable forward mode for AD packages. This option is valid
only if the AD package supports two modes (backward/forward).
MFEM_USE_CUDA = YES/NO
Enables support for CUDA devices in MFEM. CUDA is a parallel computing
platform and programming model for general computing on graphical processing
@@ -684,6 +705,26 @@ The specific libraries and their options are:
Options: HIOP_OPT, HIOP_LIB.
Versions: HIOP >= 0.4.
- ADEPT (optional), used with MFEM_USE_ADEPT = YES
URL: www.met.reading.ac.uk/clouds/adept/
Options: ADEPT_OPT, ADEPT_LIB
Versions: 1.1 and 2.0.5
- FADBAD++ (optiobal), used with MFEM_USE_FADBADPP = YES
URL: www.fadbad.com/fadbad.html
Options: FADBADPP_OPT
Versions: 2.1
- CoDiPack (optiobal), used with MFEM_USE_CODIPACK = YES
URL: https://www.scicomp.uni-kl.de/codi/
Options: CODIPACK_OPT
Versions: 1.9.3
- Eigen (optiobal), used with MFEM_USE_EIGEN = YES
URL: https://gitlab.com/libeigen/eigen
Options: EIGEN_OPT
Versions: 3.3.9
- GSLIB (optional), used when MFEM_USE_GSLIB = YES. The gslib library must be
built prior to the MFEM build, as follows: download gslib-1.0.7, untar it at
the same level as MFEM and create a symbolic link: "ln -s gslib-1.0.7 gslib".
@@ -875,6 +916,11 @@ MFEM_USE_MPFR
MFEM_USE_ZLIB
MFEM_USE_PUMI
MFEM_USE_HIOP
MFEM_USE_ADEPT
MFEM_USE_FADBADPP
MFEM_USE_CODIPACK
MFEM_USE_EIGEN
MFEM_USE_ADFORWARD
MFEM_USE_CUDA
MFEM_USE_OCCA
MFEM_USE_CEED
@@ -931,6 +977,10 @@ The CMake build system adds auto-detection for the following packages/libraries:
- POSIXCLOCKS
- PUMI
- HIOP
- ADEPT
- FADBAD++
- CoDiPack
- Eigen
- OCCA
- RAJA
- UMPIRE
+5
View File
@@ -53,6 +53,11 @@ set(MFEM_USE_CEED @MFEM_USE_CEED@)
set(MFEM_USE_UMPIRE @MFEM_USE_UMPIRE@)
set(MFEM_USE_SIMD @MFEM_USE_SIMD@)
set(MFEM_USE_ADIOS2 @MFEM_USE_ADIOS2@)
set(MFEM_USE_ADEPT @MFEM_USE_ADEPT@)
set(MFEM_USE_FADBADPP @MFEM_USE_FADBADPP@)
set(MFEM_USE_CODIPACK @MFEM_USE_CODIPACK@)
set(MFEM_USE_EIGEN @MFEM_USE_EIGEN@)
set(MFEM_USE_ADFORWARD @MFEM_USE_ADFORWARD@)
set(MFEM_USE_CALIPER @MFEM_USE_CALIPER@)
set(MFEM_CXX_COMPILER "@CMAKE_CXX_COMPILER@")
+15
View File
@@ -169,6 +169,21 @@
// library.
#cmakedefine MFEM_USE_SIMMETRIX
// use ADEPT library for AD
#cmakedefine MFEM_USE_ADEPT
// use FADBAD++ library for AD
#cmakedefine MFEM_USE_FADBADPP
// use CoDiPack library for AD
#cmakedefine MFEM_USE_CODIPACK
// use Eigen library
#cmakedefine MFEM_USE_EIGEN
// use forward mode for automatic differentiation
#cmakedefine MFEM_USE_ADFORWARD
// Enable interface to the MKL CPardiso library.
#cmakedefine MFEM_USE_MKL_CPARDISO
+23
View File
@@ -0,0 +1,23 @@
# Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
# Sets the following variables:
# - ADEPT_FOUND
# - ADEPT_INCLUDE_DIRS
# - ADEPT_LIBRARIES
include(MfemCmakeUtilities)
mfem_find_package(ADEPT ADEPT ADEPT_DIR
"include" "adept.hpp"
"lib" "libadept.so"
"Paths to headers required by ADEPT."
"Libraries required by ADEPT.")
+23
View File
@@ -0,0 +1,23 @@
# Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
# Sets the following variables:
# - CODIPACK_FOUND
# - CODIPACK_INCLUDE_DIRS
# - CODIPACK_LIBRARIES
include(MfemCmakeUtilities)
mfem_find_package(CODIPACK CODIPACK CODIPACK_DIR
"include" "codi.h"
"lib" ""
"Paths to headers required by CODIPACK."
"Libraries required by CODIPACK.")
+23
View File
@@ -0,0 +1,23 @@
# Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
# Sets the following variables:
# - EIGEN_FOUND
# - EIGEN_INCLUDE_DIRS
# - EIGEN_LIBRARIES
include(MfemCmakeUtilities)
mfem_find_package(EIGEN EIGEN EIGEN_DIR
"include" "codi.h"
"lib" ""
"Paths to headers required by EIGEN."
"Libraries required by EIGEN.")
+23
View File
@@ -0,0 +1,23 @@
# Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
# Sets the following variables:
# - FADBADPP_FOUND
# - FADBADPP_INCLUDE_DIRS
# - FADBADPP_LIBRARIES
include(MfemCmakeUtilities)
mfem_find_package(FADBADPP FADBADPP FADBADPP_DIR
"include" "fadiff.h"
"lib" ""
"Paths to headers required by FADBADPP."
"Libraries required by FADBADPP.")
+15
View File
@@ -174,6 +174,21 @@
// library.
// #define MFEM_USE_SIMMETRIX
// use ADEPT library for AD
// #define MFEM_USE_ADEPT
// use FADBAD++ library for AD
// #define MFEM_USE_FADBADPP
// use CoDiPack library for AD
// #define MFEM_USE_CODIPACK
// use Eigen library
// #define MFEM_USE_EIGEN
// use forward mode for automatic differentiation
// #define MFEM_USE_ADFORWARD
// Enable interface to the MKL CPardiso library.
// #define MFEM_USE_MKL_CPARDISO
+5
View File
@@ -46,6 +46,11 @@ MFEM_USE_SIDRE = @MFEM_USE_SIDRE@
MFEM_USE_CONDUIT = @MFEM_USE_CONDUIT@
MFEM_USE_PUMI = @MFEM_USE_PUMI@
MFEM_USE_HIOP = @MFEM_USE_HIOP@
MFEM_USE_ADEPT = @MFEM_USE_ADEPT@
MFEM_USE_FADBADPP = @MFEM_USE_FADBADPP@
MFEM_USE_CODIPACK = @MFEM_USE_CODIPACK@
MFEM_USE_EIGEN = @MFEM_USE_EIGEN@
MFEM_USE_ADFORWARD = @MFEM_USE_ADFORWARD@
MFEM_USE_GSLIB = @MFEM_USE_GSLIB@
MFEM_USE_CUDA = @MFEM_USE_CUDA@
MFEM_USE_HIP = @MFEM_USE_HIP@
+17
View File
@@ -55,6 +55,11 @@ option(MFEM_USE_CEED "Enable CEED" OFF)
option(MFEM_USE_UMPIRE "Enable Umpire" OFF)
option(MFEM_USE_SIMD "Enable use of SIMD intrinsics" OFF)
option(MFEM_USE_ADIOS2 "Enable ADIOS2" OFF)
option(MFEM_USE_ADEPT "Enable AD using ADEPT" OFF)
option(MFEM_USE_FADBADPP "Enable AD using FADBAD++" OFF)
option(MFEM_USE_CODIPACK "Enable AD using CoDiPack" OFF)
option(MFEM_USE_EIGEN "Enable Eigen" OFF)
option(MFEM_USE_ADFORWARD "Enable forward mode for AD" OFF)
option(MFEM_USE_CALIPER "Enable Caliper support" OFF)
option(MFEM_USE_MKL_CPARDISO "Enable MKL CPardiso" OFF)
@@ -218,6 +223,18 @@ set(BLAS_LIBRARIES "" CACHE STRING "The BLAS library.")
set(LAPACK_INCLUDE_DIRS "" CACHE STRING "Path to LAPACK headers.")
set(LAPACK_LIBRARIES "" CACHE STRING "The LAPACK library.")
set(ADEPT_INCLUDE_DIRS "${MFEM_DIR}/../adept-1.1/include" CACHE STRING "Path to ADEPT headers.")
set(ADEPT_LIBRARIES "-L${MFEM_DIR}/../adept-1.1/lib -ladept" CACHE STRING "The ADEPT library.")
set(FADBADPP_INCLUDE_DIRS "${MFEM_DIR}/../FADBAD++" CACHE STRING "Path to FADBAD++ headers.")
set(FADBADPP_LIBRARIES "")
set(CODIPACK_INCLUDE_DIRS "${MFEM_DIR}/../CoDiPack/inlude" CACHE STRING "Path to CoDoPack headers.")
set(CODIPACK_LIBRARIES "")
set(EIGEN_INCLUDE_DIRS "${MFEM_DIR}/../Eigen/" CACHE STRING "Path to Eigen headers.")
set(EIGEN_LIBRARIES "")
# Some useful variables:
set(CMAKE_SKIP_PREPROCESSED_SOURCE_RULES ON) # Skip *.i rules
set(CMAKE_SKIP_ASSEMBLY_SOURCE_RULES ON) # Skip *.s rules
+25
View File
@@ -146,6 +146,11 @@ MFEM_USE_CALIPER = NO
MFEM_USE_UMPIRE = NO
MFEM_USE_SIMD = NO
MFEM_USE_ADIOS2 = NO
MFEM_USE_ADEPT = NO
MFEM_USE_FADBADPP = NO
MFEM_USE_CODIAPCK = NO
MFEM_USE_EIGEN = NO
MFEM_USE_ADFORWARD = NO
MFEM_USE_MKL_CPARDISO = NO
# MPI library compile and link flags
@@ -379,6 +384,26 @@ HIOP_DIR = @MFEM_DIR@/../hiop/install
HIOP_OPT = -I$(HIOP_DIR)/include
HIOP_LIB = -L$(HIOP_DIR)/lib -lhiop $(LAPACK_LIB)
# ADEPT
ADEPT_DIR = @MFEM_DIR@/../adept-1.1
ADEPT_OPT = -I$(ADEPT_DIR)/include
ADEPT_LIB = -L$(ADEPT_DIR)/lib -ladept
# FADBAD++
FADBADPP_DIR = @MFEM_DIR@/../FADBAD++
FADBADPP_OPT = -I$(FADBADPP_DIR)
FADBADPP_LIB = -L.
# CoDiPack
CODIPACK_DIR = @MFEM_DIR@/../CoDiPack
CODIPACK_OPT = -I$(CODIPACK_DIR)
CODIPACK_LIB = -L.
# Eigen
EIGEN_DIR = @MFEM_DIR@/../Eigen
EIGEN_OPT = -I$(EIGEN_DIR)
EIGEN_LIB = -L.
# GSLIB library
GSLIB_DIR = @MFEM_DIR@/../gslib/build
GSLIB_OPT = -I$(GSLIB_DIR)/include
+2
View File
@@ -979,6 +979,8 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
for (int k = 0; k < bfnfi.Size(); ++k)
{
if (bfnfi_marker[k] &&
(*bfnfi_marker[k])[bdr_attr-1] == 0) { continue; }
bfnfi[k]->AssembleFaceGrad(fe, fe2, *tr, el_x_const, elmats);
for (int l=0; l<fes.Size(); ++l)
{
+8 -2
View File
@@ -267,7 +267,7 @@ endif
# List of MFEM dependencies, that require the *_LIB variable to be non-empty
MFEM_REQ_LIB_DEPS = SUPERLU MUMPS METIS CONDUIT SIDRE LAPACK SUNDIALS MESQUITE\
SUITESPARSE STRUMPACK GINKGO GNUTLS NETCDF PETSC SLEPC MPFR PUMI HIOP GSLIB\
OCCA CEED RAJA UMPIRE MKL_CPARDISO AMGX CALIPER
OCCA CEED RAJA UMPIRE MKL_CPARDISO AMGX CALIPER ADEPT FADBADPP CODIPACK EIGEN
PETSC_ERROR_MSG = $(if $(PETSC_FOUND),,. PETSC config not found: $(PETSC_VARS))
SLEPC_ERROR_MSG = $(if $(SLEPC_FOUND),,. SLEPC config not found: $(SLEPC_VARS))
@@ -333,7 +333,8 @@ MFEM_DEFINES = MFEM_VERSION MFEM_VERSION_STRING MFEM_GIT_STRING MFEM_USE_MPI\
MFEM_USE_HIOP MFEM_USE_GSLIB MFEM_USE_CUDA MFEM_USE_HIP MFEM_USE_OCCA\
MFEM_USE_CEED MFEM_USE_RAJA MFEM_USE_UMPIRE MFEM_USE_SIMD MFEM_USE_ADIOS2\
MFEM_USE_MKL_CPARDISO MFEM_USE_AMGX MFEM_USE_MUMPS MFEM_USE_CALIPER\
MFEM_SOURCE_DIR MFEM_INSTALL_DIR
MFEM_USE_ADEPT MFEM_USE_FADBADPP MFEM_USE_ADFORWARD MFEM_USE_CODIPACK\
MFEM_SOURCE_DIR MFEM_INSTALL_DIR MFEM_USE_EIGEN
# List of makefile variables that will be written to config.mk:
MFEM_CONFIG_VARS = MFEM_CXX MFEM_HOST_CXX MFEM_CPPFLAGS MFEM_CXXFLAGS\
@@ -658,6 +659,11 @@ status info:
$(info MFEM_USE_UMPIRE = $(MFEM_USE_UMPIRE))
$(info MFEM_USE_SIMD = $(MFEM_USE_SIMD))
$(info MFEM_USE_ADIOS2 = $(MFEM_USE_ADIOS2))
$(info MFEM_USE_ADEPT = $(MFEM_USE_ADEPT))
$(info MFEM_USE_FADBADPP = $(MFEM_USE_FADBADPP))
$(info MFEM_USE_CODIPACK = $(MFEM_USE_CODIPACK))
$(info MFEM_USE_EIGEN = $(MFEM_USE_EIGEN))
$(info MFEM_USE_ADFORWARD = $(MFEM_USE_ADFORWARD))
$(info MFEM_USE_MKL_CPARDISO = $(MFEM_USE_MKL_CPARDISO))
$(info MFEM_CXX = $(value MFEM_CXX))
$(info MFEM_HOST_CXX = $(value MFEM_HOST_CXX))
+1
View File
@@ -29,3 +29,4 @@ add_subdirectory(gslib)
add_subdirectory(solvers)
add_subdirectory(shifted)
add_subdirectory(mtop)
add_subdirectory(autodiff)
+75
View File
@@ -0,0 +1,75 @@
# 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.
list(APPEND SEQADIFF_COMMON_SOURCES
stokes.cpp
advdiff.cpp)
list(APPEND SEQADIFF_COMMON_HEADERS
fdual.hpp
tadvector.hpp
taddensemat.hpp
adnonlininteg.hpp
admfem.hpp
stokes.hpp
advdiff.hpp)
convert_filenames_to_full_paths(SEQADIFF_COMMON_SOURCES)
convert_filenames_to_full_paths(SEQADIFF_COMMON_HEADERS)
set(SEQADIFF_COMMON_FILES
EXTRA_SOURCES ${SEQADIFF_COMMON_SOURCES}
EXTRA_HEADERS ${SEQADIFF_COMMON_HEADERS})
add_mfem_miniapp(seqadiff
MAIN seq_example.cpp
${SEQADIFF_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(seqtest
MAIN seq_test.cpp
${SEQADIFF_COMMON_FILES}
LIBRARIES mfem)
if(MFEM_USE_MPI)
list(APPEND PARADIFF_COMMON_SOURCES
)
list(APPEND PARADIFF_COMMON_HEADERS
)
convert_filenames_to_full_paths(PARADIFF_COMMON_SOURCES)
convert_filenames_to_full_paths(PARADIFF_COMMON_HEADERS)
set(PARADIFF_COMMON_FILES
EXTRA_SOURCES ${PARADIFF_COMMON_SOURCES} ${SEQADIFF_COMMON_SOURCES}
EXTRA_HEADERS ${PARADIFF_COMMON_HEADERS} ${SEQADIFF_COMMON_HEADERS})
message(STATUS "PARADIFF_COMMON_FILES: ${PARADIFF_COMMON_FILES}")
message(STATUS "SEQADIFF_COMMON_FILES: ${SEQADIFF_COMMON_FILES}")
add_mfem_miniapp(paradiff
MAIN par_example.cpp
${PARADIFF_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(stokes
MAIN stokes_test.cpp
LIBRARIES mfem)
add_mfem_miniapp(advdiff
MAIN advdiff_test.cpp
LIBRARIES mfem)
endif ()
+576
View File
@@ -0,0 +1,576 @@
#ifndef ADMFEM_HPP
#define ADMFEM_HPP
#include "mfem.hpp"
#include "fdual.hpp"
#include "tadvector.hpp"
#include "taddensemat.hpp"
#ifdef MFEM_USE_CODIPACK
#include <codi.hpp>
namespace mfem {
namespace ad {
#ifdef MFEM_USE_ADFORWARD
typedef codi::RealForward ADFloatType;
typedef TADVector<ADFloatType> ADVectorType;
typedef TADDenseMatrix<ADFloatType> ADMatrixType;
#else
typedef codi::RealReverse ADFloatType;
typedef TADVector<ADFloatType> ADVectorType;
typedef TADDenseMatrix<ADFloatType> ADMatrixType;
#endif
}
template<int vector_size=1, int state_size=1, int param_size=0>
class VectorFuncAutoDiff
{
public:
VectorFuncAutoDiff(std::function<void(mfem::Vector&, ad::ADVectorType&, ad::ADVectorType&)> F_)
{
F=F_;
}
void QJacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
#ifdef MFEM_USE_ADFORWARD
// use forward mode
jac.SetSize(vector_size, state_size);
jac = 0.0;
{
ad::ADVectorType aduu(state_size);
ad::ADVectorType rr(vector_size);
for(int i=0;i<state_size;i++){
aduu[i].setValue(uu[i]);
aduu[i].setGradient(0.0);
}
for(int ii=0;ii<state_size;ii++){
aduu[ii].setGradient(1.0);
F(vparam,aduu,rr);
for(int jj=0;jj<vector_size;jj++)
{
jac(jj,ii)=rr[jj].getGradient();
}
aduu[ii].setGradient(0.0);
}
}
#else // use reverse mode
jac.SetSize(vector_size, state_size);
jac = 0.0;
{
ad::ADVectorType aduu(state_size);
ad::ADVectorType rr(vector_size);
for(int i=0;i<state_size;i++){
aduu[i]=uu[i];
}
ad::ADFloatType::TapeType& tape =ad::ADFloatType::getGlobalTape();
typename ad::ADFloatType::TapeType::Position pos=tape.getPosition();
tape.setActive();
for(int ii=0;ii<state_size;ii++){ tape.registerInput(aduu[ii]); }
F(vparam,aduu,rr);
for(int ii=0;ii<vector_size;ii++){ tape.registerOutput(rr[ii]); }
tape.setPassive();
for(int jj=0;jj<vector_size;jj++){
rr[jj].setGradient(1.0);
tape.evaluate();
for(int ii=0;ii<state_size;ii++){
jac(jj,ii)=aduu[ii].getGradient();
}
tape.clearAdjoints();
rr[jj].setGradient(0.0);
}
tape.reset(pos);
}
#endif
}
private:
std::function<void(mfem::Vector&, ad::ADVectorType&, ad::ADVectorType&)> F;
}; //VectorFuncAutoDiff
template<template<typename, typename, typename, int, int, int> class CTD
, int vector_size=1, int state_size=1, int param_size=0>
class QVectorFuncAutoDiff
{
public:
/// Evaluates the vector function for given set of parameters and state
/// values in vector uu. The result is returned in vector rr.
void QVectorFunc(const mfem::Vector &vparam, mfem::Vector &uu, mfem::Vector& rr)
{
CTD<double,const mfem::Vector, mfem::Vector,
vector_size, state_size, param_size> tf;
tf(vparam,uu,rr);
}
/// Returns the gradient of CTD(...) in the dense matrix jac.
/// The dimensions of jac are vector_size x state_size, where state_size is the
/// length of vector uu.
void QJacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
#ifdef MFEM_USE_ADFORWARD
// use forward mode
CTD<ad::ADFloatType, const Vector, ad::ADVectorType,
vector_size, state_size, param_size> tf;
jac.SetSize(vector_size, state_size);
jac = 0.0;
{
ad::ADVectorType aduu(state_size);
ad::ADVectorType rr(vector_size);
for(int i=0;i<state_size;i++){
aduu[i].setValue(uu[i]);
aduu[i].setGradient(0.0);
}
for(int ii=0;ii<state_size;ii++){
aduu[ii].setGradient(1.0);
tf(vparam,aduu,rr);
for(int jj=0;jj<vector_size;jj++)
{
jac(jj,ii)=rr[jj].getGradient();
}
aduu[ii].setGradient(0.0);
}
}
#else //end MFEM_USE_ADFORWARD
// use reverse mode
CTD<ad::ADFloatType, const Vector, ad::ADVectorType,
vector_size, state_size, param_size> tf;
jac.SetSize(vector_size, state_size);
jac = 0.0;
{
ad::ADVectorType aduu(state_size);
ad::ADVectorType rr(vector_size);
for(int i=0;i<state_size;i++){
aduu[i]=uu[i];
}
ad::ADFloatType::TapeType& tape =ad::ADFloatType::getGlobalTape();
typename ad::ADFloatType::TapeType::Position pos=tape.getPosition();
tape.setActive();
for(int ii=0;ii<state_size;ii++){ tape.registerInput(aduu[ii]); }
tf(vparam,aduu,rr);
for(int ii=0;ii<vector_size;ii++){ tape.registerOutput(rr[ii]); }
tape.setPassive();
for(int jj=0;jj<vector_size;jj++){
rr[jj].setGradient(1.0);
tape.evaluate();
for(int ii=0;ii<state_size;ii++){
jac(jj,ii)=aduu[ii].getGradient();
}
tape.clearAdjoints();
rr[jj].setGradient(0.0);
}
tape.reset(pos);
}
#endif
}
};
template<template<typename, typename, typename, int, int> class CTD
, int state_size=1, int param_size=0>
class QFunctionAutoDiff
{
public:
/// Evaluates a function for arguments vparam and uu.
/// The evaluatin is based on the operator() in the
/// user provided functor CTD.
double QEval(const Vector &vparam, Vector &uu)
{
CTD<double, const Vector, Vector, state_size, param_size> tf;
return tf(vparam,uu);
}
/// Provides the same functionality as QGrad.
void QVectorFunc(const Vector &vparam, Vector &uu, Vector &rr)
{
QGrad(vparam,uu,rr);
}
/// Returns the first derivative of CTD(...) with
/// respect to the active arguments proved in vector uu.
/// The length of rr is the same as for uu.
void QGrad(const Vector &vparam, Vector &uu, Vector &rr)
{
#ifdef MFEM_USE_ADFORWARD
// use forward mode
CTD<ad::ADFloatType, const Vector, ad::ADVectorType,
state_size, param_size> tf;
rr.SetSize(state_size);
{
ad::ADVectorType aduu(state_size);
for(int i=0;i<state_size;i++){
aduu[i].setValue(uu[i]);
aduu[i].setGradient(0.0);
}
ad::ADFloatType rez;
for(int ii=0;ii<state_size;ii++){
aduu[ii].setGradient(1.0);
rez=tf(vparam,aduu);
rr[ii]=rez.getGradient();
aduu[ii].setGradient(0.0);
}
}
#else
typedef codi::RealReverse ADFType;
typedef TADVector<ADFType> ADFVector;
CTD<ADFType, const Vector, ADFVector, state_size, param_size> tf;
{
ADFVector aduu(state_size);
ADFType rez;
for(int i=0;i<state_size;i++){
aduu[i]=uu[i];
}
ADFType::TapeType& tape =ADFType::getGlobalTape();
typename ADFType::TapeType::Position pos=tape.getPosition();
tape.setActive();
for(int ii=0;ii<state_size;ii++){ tape.registerInput(aduu[ii]); }
rez=tf(vparam,aduu);
tape.registerOutput(rez);
tape.setPassive();
rez.setGradient(1.0);
tape.evaluate();
for(int i=0;i<state_size;i++){
rr[i]=aduu[i].getGradient();
}
tape.reset(pos);
}
#endif
}
/// Provides same functionality as QHessian.
void QJacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
QHessian(vparam,uu,jac);
}
/// Returns the Hessian of CTD(...) in the dense matrix hh.
/// The dimensions of jac are state_size x state_size, where state_size is the
/// length of vector uu.
void QHessian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
#ifdef MFEM_USE_ADFORWARD
// use forward-forward mode
typedef codi::RealForwardGen<double> ADFType;
typedef TADVector<ADFType> ADFVector;
typedef TADDenseMatrix<ADFType> ADFDenseMatrix;
typedef codi::RealForwardGen<ADFType> ADSType;
typedef TADVector<ADSType> ADSVector;
typedef TADDenseMatrix<ADSType> ADSDenseMatrix;
CTD<ADSType, const Vector, ADSVector, state_size, param_size> tf;
jac.SetSize(state_size);
jac=0.0;
{
ADSVector aduu(state_size);
for(int ii = 0; ii < state_size; ii++)
{
aduu[ii].value().value()=uu[ii];
aduu[ii].value().gradient()=0.0;
aduu[ii].gradient().value()=0.0;
aduu[ii].gradient().gradient()=0.0;
}
for(int ii = 0; ii < state_size; ii++)
{
aduu[ii].value().gradient()=1.0;
for(int jj=0; jj<(ii+1); jj++)
{
aduu[jj].gradient().value()=1.0;
ADSType rez=tf(vparam,aduu);
jac(ii,jj)=rez.gradient().gradient();
jac(jj,ii)=jac(ii,jj);
aduu[jj].gradient().value()=0.0;
}
aduu[ii].value().gradient()=0.0;
}
}
#else
//use mixed forward and reverse mode
typedef codi::RealForwardGen<double> ADFType;
typedef TADVector<ADFType> ADFVector;
typedef TADDenseMatrix<ADFType> ADFDenseMatrix;
typedef codi::RealReverseGen<ADFType> ADSType;
typedef TADVector<ADSType> ADSVector;
typedef TADDenseMatrix<ADSType> ADSDenseMatrix;
CTD<ADSType, const Vector, ADSVector, state_size, param_size> tf;
jac.SetSize(state_size);
jac=0.0;
{
ADSVector aduu(state_size);
for(int ii=0;ii < state_size ; ii++)
{
aduu[ii].value().value()=uu[ii];
}
ADSType rez;
ADSType::TapeType& tape = ADSType::getGlobalTape();
typename ADSType::TapeType::Position pos;
for(int ii = 0; ii < state_size ; ii++)
{
pos=tape.getPosition();
tape.setActive();
for(int jj=0;jj < state_size; jj++) {
if(jj==ii) {aduu[jj].value().gradient()=1.0;}
else {aduu[jj].value().gradient()=0.0;}
tape.registerInput(aduu[jj]);
}
rez=tf(vparam,aduu);
tape.registerOutput(rez);
tape.setPassive();
rez.gradient().value()=1.0;
tape.evaluate();
for(int jj=0; jj<(ii+1); jj++)
{
jac(ii,jj)=aduu[jj].gradient().gradient();
jac(jj,ii)=jac(ii,jj);
}
tape.reset(pos);
}
}
#endif
}
};
}
#else //end MFEM_USE_CODIPACK
//USE NATIVE IMPLEMENTATION
namespace mfem {
namespace ad {
typedef FDual<double> ADFloatType;
typedef TADVector<ADFloatType> ADVectorType;
typedef TADDenseMatrix<ADFloatType> ADMatrixType;
}
template<int vector_size=1, int state_size=1, int param_size=0>
class VectorFuncAutoDiff
{
public:
VectorFuncAutoDiff(std::function<void(mfem::Vector&, ad::ADVectorType&, ad::ADVectorType&)> F_)
{
F=F_;
}
public:
void QJacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
jac.SetSize(vector_size, state_size);
jac = 0.0;
{
ad::ADVectorType aduu(uu); //all dual numbers are initialized to zero
ad::ADVectorType rr(vector_size);
for (int ii = 0; ii < state_size; ii++)
{
aduu[ii].dual(1.0);
F(vparam,aduu,rr);
for (int jj = 0; jj < vector_size; jj++)
{
jac(jj, ii) = rr[jj].dual();
}
aduu[ii].dual(0.0);
}
}
}
private:
std::function<void(mfem::Vector&, ad::ADVectorType&, ad::ADVectorType&)> F;
};
template<template<typename, typename, typename, int, int, int> class CTD
, int vector_size=1, int state_size=1, int param_size=0>
class QVectorFuncAutoDiff
{
private:
/// MFEM native forward AD-type
typedef ad::FDual<double> ADFType;
typedef TADVector<ADFType> ADFVector;
typedef TADDenseMatrix<ADFType> ADFDenseMatrix;
public:
/// Returns a vector valued function rr for supplied passive arguments
/// vparam and active arguments uu. The evaluation is based on the
/// user supplied CTD template class.
void QVectorFunc(const Vector &vparam, Vector &uu, Vector &rr)
{
CTD<double, const Vector, Vector,
vector_size, state_size, param_size> func;
func(vparam, uu, rr);
}
/// Returns the gradient of CTD(...) residual in the dense matrix jac.
/// The dimensions of jac are vector_size x state_size, where state_size is the
/// length of vector uu.
void QJacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
//use native AD package
jac.SetSize(vector_size, state_size);
jac = 0.0;
{
ADFVector aduu(uu); //all dual numbers are initialized to zero
ADFVector rr(vector_size);
for (int ii = 0; ii < state_size; ii++)
{
aduu[ii].dual(1.0);
QEval(vparam, aduu, rr);
for (int jj = 0; jj < vector_size; jj++)
{
jac(jj, ii) = rr[jj].dual();
}
aduu[ii].dual(0.0);
}
}
}
private:
/// Evaluates the residual from CTD(...).
/// Intended for internal use only.
void QEval(const Vector &vparam, ADFVector &uu, ADFVector &rr)
{
CTD<ADFType, const Vector, ADFVector,
vector_size, state_size, param_size> tf;
tf(vparam, uu, rr);
}
};
template<template<typename, typename, typename, int, int> class CTD
, int state_size=1, int param_size=0>
class QFunctionAutoDiff
{
private:
///MFEM native AD-type for first derivatives
typedef ad::FDual<double> ADFType;
typedef TADVector<ADFType> ADFVector;
typedef TADDenseMatrix<ADFType> ADFDenseMatrix;
///MFEM native AD-type for second derivatives
typedef ad::FDual<ADFType> ADSType;
typedef TADVector<ADSType> ADSVector;
typedef TADDenseMatrix<ADSType> ADSDenseMatrix;
public:
/// Evaluates a function for arguments vparam and uu.
/// The evaluatin is based on the operator() in the
/// user provided functor CTD.
double QEval(const Vector &vparam, Vector &uu)
{
CTD<double, const Vector, Vector, state_size, param_size> tf;
return tf(vparam,uu);
}
/// Provides the same functionality as QGrad.
void QVectorFunc(const Vector &vparam, Vector &uu, Vector &rr)
{
QGrad(vparam,uu,rr);
}
/// Returns the first derivative of CTD(...) with
/// respect to the active arguments proved in vector uu.
/// The length of rr is the same as for uu.
void QGrad(const Vector &vparam, Vector &uu, Vector &rr)
{
int n = uu.Size();
rr.SetSize(n);
ADFVector aduu(uu);
ADFType rez;
for (int ii = 0; ii < n; ii++)
{
aduu[ii].dual(1.0);
rez = QEval(vparam, aduu);
rr[ii] = rez.dual();
aduu[ii].dual(0.0);
}
}
/// Provides same functionality as QHessian.
void QJacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
QHessian(vparam,uu,jac);
}
/// Returns the Hessian of CTD(...) in the dense matrix hh.
/// The dimensions of jac are state_size x state_size, where state_size is the
/// length of vector uu.
void QHessian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
int n = uu.Size();
jac.SetSize(n);
jac = 0.0;
{
ADSVector aduu(n);
for (int ii = 0; ii < n; ii++)
{
aduu[ii].real(ADFType(uu[ii], 0.0));
aduu[ii].dual(ADFType(0.0, 0.0));
}
for (int ii = 0; ii < n; ii++)
{
aduu[ii].real(ADFType(uu[ii], 1.0));
for (int jj = 0; jj < (ii + 1); jj++)
{
aduu[jj].dual(ADFType(1.0, 0.0));
ADSType rez = QEval(vparam, aduu);
jac(ii, jj) = rez.dual().dual();
jac(jj, ii) = rez.dual().dual();
aduu[jj].dual(ADFType(0.0, 0.0));
}
aduu[ii].real(ADFType(uu[ii], 0.0));
}
}
}
private:
/// Evaluates the first derivative of CTD(...).
/// Intended for internal use only.
ADFType QEval(const Vector &vparam, ADFVector &uu)
{
CTD<ADFType, const Vector, ADFVector, state_size, param_size> tf;
return tf(vparam, uu);
}
/// Evaluates the second derivative of CTD(...).
/// Intended for internal use only.
ADSType QEval(const Vector &vparam, ADSVector &uu)
{
CTD<ADSType, const Vector, ADSVector, state_size, param_size> tf;
return tf(vparam, uu);
}
};
}//end namespace mfem
#endif // NATIVE
#endif // ADMFEM_HPP
+503
View File
@@ -0,0 +1,503 @@
// Copyright (c) 2020, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_ADNONLININTEG
#define MFEM_ADNONLININTEG
#include "mfem.hpp"
#include "tadvector.hpp"
#include "taddensemat.hpp"
#include "fdual.hpp"
#if defined MFEM_USE_ADEPT
#include <adept.h>
#elif defined MFEM_USE_FADBADPP
#include <fadiff.h>
#include <badiff.h>
#endif
namespace mfem
{
///Automatic differentiation class - for user coded functor CTD returning PDE's
/// energy and residual at a point, provides the derivative of the residual
/// with respect to the active arguments of the functor.
///
/**
The two templated parameters are
template<typename, typename> class CTD and int m.
The templated class CTD is a user-provided functor which implements
the function of interest. The CTD class should have the following signature:
template<typename DType, typename MVType>
class MyQFunctorJ
{
DType operator()(const Vector &vparam, MVType &uu)
{...}
void operator()(const Vector &vparam, MVType &uu, MVType &rr)
{...}
};
DType - represents the scalars in the functor, and MVType - represents
the vectors in the functor. In ADQFunctionTJ, DType will be replaced
either with double or AD-type.
The first operator()(const Vector &vparam, MVType &uu) takes as input
standard MFEM Vector vparam (passive arguments), which holds all parameters
supplied to the functor and MVType vector uu, which holds all active
arguments provided to the functor. The operator returns a scalar
representing the value of the function(energy) for passive parameters vparam
and active arguments uu.
The second void operator()(const Vector &vparam, MVType &uu, MVType &rr)
returns a vector-valued function rr with passive input arguments vparam
and active (AD) arguments supplied in vector uu. Since the size of the
return vector rr is not known in advance, it should be provided explicitly
to the ADQFunctionTJ class, i.e., the template parameter m in ADQFunctionTJ
represents the dimensions of the return vector rr.
For PDE discretized problem, the first operator should return the energy
evaluated a point, and the second operator should return the residual
evaluated at the point. The derivative of the residual vector rr with
respect to the active arguments uu is evaluated automatically by
ADQFunctionTJ and returned by the ADQFunctionTJ:: QFunctionDD(..) method.
*/
template<template<typename, typename> class CTD, int m>
class ADQFunctionTJ
{
// m - dimension of the residual vector
// the Jacobian will have dimensions [m,length(uu)]
protected:
#ifdef MFEM_USE_ADEPT
adept::Stack m_stack;
#endif
public:
#if defined MFEM_USE_ADEPT
/// AD-type based on the Adept AD library.
typedef adept::adouble ADFType;
typedef TADVector<ADFType> ADFVector;
typedef TADDenseMatrix<ADFType> ADFDenseMatrix;
#elif defined MFEM_USE_FADBADPP
#ifdef MFEM_USE_ADFORWARD
/// AD-type based on the FADBAD++ library -
/// used only for forward differentiation.
typedef fadbad::F<double> ADFType;
typedef TADVector<ADFType> ADFVector;
typedef TADDenseMatrix<ADFType> ADFDenseMatrix;
#else
/// AD-type based on the FADBAD++ library -
/// used only for reverse AD mode.
typedef fadbad::B<double> ADFType;
typedef TADVector<ADFType> ADFVector;
typedef TADDenseMatrix<ADFType> ADFDenseMatrix;
#endif
#else
/// MFEM native forward AD-type
typedef ad::FDual<double> ADFType;
typedef TADVector<ADFType> ADFVector;
typedef TADDenseMatrix<ADFType> ADFDenseMatrix;
#endif
#ifdef MFEM_USE_ADEPT
ADQFunctionTJ() : m_stack(false) {}
#else
ADQFunctionTJ() {}
#endif
~ADQFunctionTJ() {}
///Returns the energy for passive arguments vparam and
/// active arguments uu. The evaluation is based on the
/// first operator in the user-supplied CTD template class.
double QFunction(const Vector &vparam, Vector &uu)
{
CTD<double, Vector> func;
return func(vparam, uu);
}
///Returns vector valued function rr for passive input parametrs vparam
/// and active arguments provided in uu.
void QFunctionDU(const Vector &vparam, ADFVector &uu, ADFVector &rr)
{
CTD<ADFType, ADFVector> func;
func(vparam, uu, rr);
}
///Evaluates automatically the first derivative of
/// QFunction(const Vector &vparam, Vector &uu) with respect to
/// the active vector arguments uu. The dimension or rr is the same
/// as the dimension of uu. The method can be used for testing
/// the correctness of hand-coded gradients of QFunction(...).
void QFunctionAU(const Vector &vparam, Vector &uu, Vector &rr)
{
//the result is computed automatically by differentiating
//QFunction with respect to uu
CTD<ADFType, ADFVector> func;
int n = uu.Size();
rr.SetSize(n);
#if defined MFEM_USE_ADEPT
//use ADEPT package
adept::Stack *p_stack = adept::active_stack();
p_stack->deactivate();
m_stack.activate();
{
ADFVector aduu(uu);
ADFType rez;
m_stack.new_recording();
rez = func(vparam, aduu);
m_stack.independent(aduu.GetData(), n); //independent variables
m_stack.dependent(&rez, 1); //dependent variables
m_stack.jacobian(rr.GetData());
}
m_stack.deactivate();
#elif defined MFEM_USE_FADBADPP
//use FADBAD++
#ifdef MFEM_USE_ADFORWARD
{
ADFVector aduu(uu);
ADFType rez;
for (int ii = 0; ii < n; ii++)
{
aduu[ii].diff(ii, n);
}
rez = func(vparam, aduu);
for (int ii = 0; ii < n; ii++)
{
rr[ii] = rez.d(ii);
}
}
#else
{
ADFVector aduu(uu);
ADFType rez;
rez = func(vparam, aduu);
rez.diff(0, 1);
for (int ii = 0; ii < n; ii++)
{
rr[ii] = aduu[ii].d(0);
}
}
#endif
#else
//use native AD package
{
ADFVector aduu(uu); //all dual numbers are initialized to zero
ADFType rez;
for (int ii = 0; ii < n; ii++)
{
aduu[ii].dual(1.0);
rez = func(vparam, aduu);
rr[ii] = rez.dual();
aduu[ii].dual(0.0);
}
}
#endif
}
///Returns vector valued function rr for supplied passive arguments
/// vparam and active arguments uu. The evaluation is based on the
/// user supplied CTD template class (second operator).
void QFunctionDU(const Vector &vparam, Vector &uu, Vector &rr)
{
CTD<double, Vector> func;
func(vparam, uu, rr);
}
///Evaluates automatically the derivative or the vector function
/// QFunctionDU(...), i.e., the retuned vector rr, with respect to
/// the active arguments uu. The dimensions of the the dense matix
/// jac are [m,n] where m is the size of the vector rr and n is the
/// size of the active vector uu. The parameter m should be supplied as
/// template parameter int m in ADQFunctionTJ.
void QFunctionDD(const Vector &vparam, Vector &uu, DenseMatrix &jac)
{
#if defined MFEM_USE_ADEPT
//use ADEPT package
adept::Stack *p_stack = adept::active_stack();
p_stack->deactivate();
int n = uu.Size();
jac.SetSize(m, n);
jac = 0.0;
m_stack.activate();
{
ADFVector aduu(uu);
ADFVector rr(m); //residual vector
m_stack.new_recording();
QFunctionDU(vparam, aduu, rr);
m_stack.independent(aduu.GetData(), n); //independent variables
m_stack.dependent(rr.GetData(), m); //dependent variables
m_stack.jacobian(jac.Data());
}
m_stack.deactivate();
#elif defined MFEM_USE_FADBADPP
//use FADBAD++
#ifdef MFEM_USE_ADFORWARD
int n = uu.Size();
jac.SetSize(m, n);
jac = 0.0;
{
ADFVector aduu(uu);
ADFVector rr(m);
for (int ii = 0; ii < n; ii++)
{
aduu[ii].diff(ii, n);
}
QFunctionDU(vparam, aduu, rr);
for (int ii = 0; ii < n; ii++)
{
for (int jj = 0; jj < m; jj++)
{
jac(jj, ii) = rr[jj].d(ii);
}
}
}
#else
int n = uu.Size();
jac.SetSize(m, n);
jac = 0.0;
{
ADFVector aduu(uu);
ADFVector rr(m);
QFunctionDU(vparam, aduu, rr);
for (int ii = 0; ii < m; ii++)
{
rr[ii].diff(ii, m);
}
for (int ii = 0; ii < n; ii++)
{
for (int jj = 0; jj < m; jj++)
{
jac(jj, ii) = aduu[ii].d(jj);
}
}
}
#endif
#else
//use native AD package
int n = uu.Size();
jac.SetSize(m, n);
jac = 0.0;
{
ADFVector aduu(uu); //all dual numbers are initialized to zero
ADFVector rr(m);
for (int ii = 0; ii < n; ii++)
{
aduu[ii].dual(1.0);
QFunctionDU(vparam, aduu, rr);
for (int jj = 0; jj < m; jj++)
{
jac(jj, ii) = rr[jj].dual();
}
aduu[ii].dual(0.0);
}
}
#endif
}
};
//template class for differentiation; the function
//for differentiation is supplied as a functor
//the operator()(scalar,vector) defines the actual function
///ADQFunctionTH is a templated class evaluating the first and
/// the second derivatives of a user supplied function encoded
/// in a functor class CTD. The signature of CTD is as follows:
/**
template<typename DType, typename MVType> class CTD
{
DType operator()(const Vector &vparam, MVType &uu)
{...}
};
The operator returns a scalar representing the value of the
function for passive arguments vparam and active arguments uu.
The first derivative of the operator is evaluated automatically
by ADQFunctionTH::QFunctionDU(const Vector &vparam, Vector &uu, Vector &rr)
method. The length of the return vector rr is the same as for
the input vector uu. The second derivate (the Hessian) of the function
is evaluated again automatically by
QFunctionDD(const Vector &vparam, const Vector &uu, DenseMatrix &jac).
Hessian evaluation is an expensive process. The provided AD-functionality
is intended to be utilized for development purposes. The performance will
increase significantly by replacing the AD-provided derivatives with hand-coded.
*/
template<template<typename, typename> class CTD>
class ADQFunctionTH
{
public:
#if defined MFEM_USE_FADBADPP
///AD-type derived from FADBAD++
typedef fadbad::B<double> ADFType;
///AD vector type for evaluation of first derivatives
typedef TADVector<ADFType> ADFVector;
///AD dense matrix type for evaluation of first derivatives
typedef TADDenseMatrix<ADFType> ADFDenseMatrix;
///AD-type for the second derivatives derived from
///FADBAD++
typedef fadbad::B<fadbad::F<double>> ADSType;
/// AD vector type for evaluation of second derivatives
typedef TADVector<ADSType> ADSVector;
/// AD dense matrix type for evaluation of second derivatives
typedef TADDenseMatrix<ADSType> ADSDenseMatrix;
#else
///MFEM native AD-type for first derivatives
typedef ad::FDual<double> ADFType;
typedef TADVector<ADFType> ADFVector;
typedef TADDenseMatrix<ADFType> ADFDenseMatrix;
///MFEM native AD-type for second derivatives
typedef ad::FDual<ADFType> ADSType;
typedef TADVector<ADSType> ADSVector;
typedef TADDenseMatrix<ADSType> ADSDenseMatrix;
#endif
ADQFunctionTH() {}
~ADQFunctionTH() {}
///Evaluates a function for arguments vparam and uu.
/// The evaluatin is based on the operator() in the
/// user provided functor CTD.
double QFunction(const Vector &vparam, Vector &uu)
{
CTD<double, Vector> tf;
return tf(vparam, uu);
}
///Evaluates the first derivative of QFunction(...).
/// Intended for internal use only.
ADFType QFunction(const Vector &vparam, ADFVector &uu)
{
CTD<ADFType, ADFVector> tf;
return tf(vparam, uu);
}
///Evaluates the second derivative of QFunction(...).
/// Intended for internal use only.
ADSType QFunction(const Vector &vparam, ADSVector &uu)
{
CTD<ADSType, ADSVector> tf;
return tf(vparam, uu);
}
///Returns the first derivative of QFunction(...) with
/// respect to the active arguments proved in vector uu.
/// The length of rr is the same as for uu.
void QFunctionDU(const Vector &vparam, Vector &uu, Vector &rr)
{
#if defined MFEM_USE_FADBADPP
int n = uu.Size();
rr.SetSize(n);
ADFVector aduu(uu);
ADFType rez;
rez = QFunction(vparam, aduu);
rez.diff(0, 1);
for (int ii = 0; ii < n; ii++)
{
rr[ii] = aduu[ii].d(0);
}
#else
int n = uu.Size();
rr.SetSize(n);
ADFVector aduu(uu);
ADFType rez;
for (int ii = 0; ii < n; ii++)
{
aduu[ii].dual(1.0);
rez = QFunction(vparam, aduu);
rr[ii] = rez.dual();
aduu[ii].dual(0.0);
}
#endif
}
///Returns the Hessian of QFunction(...) in the dense matrix jac.
/// The dimensions of jac are m x m, where m is the length of vector uu.
void QFunctionDD(const Vector &vparam, const Vector &uu, DenseMatrix &jac)
{
#if defined MFEM_USE_FADBADPP
int n = uu.Size();
jac.SetSize(n);
jac = 0.0;
{
ADSVector aduu(n);
for (int ii = 0; ii < n; ii++)
{
aduu[ii] = uu[ii];
aduu[ii].x().diff(ii, n);
}
ADSType rez = QFunction(vparam, aduu);
rez.diff(0, 1);
for (int ii = 0; ii < n; ii++)
{
for (int jj = 0; jj < ii; jj++)
{
jac(ii, jj) = aduu[ii].d(0).d(jj);
jac(jj, ii) = aduu[jj].d(0).d(ii);
}
jac(ii, ii) = aduu[ii].d(0).d(ii);
}
}
#else
int n = uu.Size();
jac.SetSize(n);
jac = 0.0;
{
ADSVector aduu(n);
for (int ii = 0; ii < n; ii++)
{
aduu[ii].real(ADFType(uu[ii], 0.0));
aduu[ii].dual(ADFType(0.0, 0.0));
}
for (int ii = 0; ii < n; ii++)
{
aduu[ii].real(ADFType(uu[ii], 1.0));
for (int jj = 0; jj < (ii + 1); jj++)
{
aduu[jj].dual(ADFType(1.0, 0.0));
ADSType rez = QFunction(vparam, aduu);
jac(ii, jj) = rez.dual().dual();
jac(jj, ii) = rez.dual().dual();
aduu[jj].dual(ADFType(0.0, 0.0));
}
aduu[ii].real(ADFType(uu[ii], 0.0));
}
}
#endif
}
};
} // namespace mfem
#endif
+108
View File
@@ -0,0 +1,108 @@
#include "advdiff.hpp"
#include "petsc.h"
namespace mfem {
void AdvectionDiffusionMXSolver::DirectSolver(mfem::BlockOperator& A)
{
delete psol; psol=nullptr;
delete prec; prec=nullptr;
delete pmat; pmat=nullptr;
mfem::HypreParMatrix* A00=static_cast<mfem::HypreParMatrix*>(&(A.GetBlock(0,0)));
mfem::HypreParMatrix* A01=static_cast<mfem::HypreParMatrix*>(&(A.GetBlock(0,1)));
mfem::HypreParMatrix* A02=static_cast<mfem::HypreParMatrix*>(&(A.GetBlock(0,2)));
mfem::HypreParMatrix* A10=static_cast<mfem::HypreParMatrix*>(&(A.GetBlock(1,0)));
mfem::HypreParMatrix* A11=static_cast<mfem::HypreParMatrix*>(&(A.GetBlock(1,1)));
mfem::HypreParMatrix* A12=static_cast<mfem::HypreParMatrix*>(&(A.GetBlock(1,2)));
mfem::HypreParMatrix* A20=static_cast<mfem::HypreParMatrix*>(&(A.GetBlock(2,0)));
mfem::HypreParMatrix* A21=static_cast<mfem::HypreParMatrix*>(&(A.GetBlock(2,1)));
mfem::HypreParMatrix* A22=static_cast<mfem::HypreParMatrix*>(&(A.GetBlock(2,2)));
Array2D< HypreParMatrix * > bm(3,3);
bm(0,0)=A00; bm(0,1)=A01; bm(0,2)=A02;
bm(1,0)=A10; bm(1,1)=A11; bm(1,2)=A12;
bm(2,0)=A20; bm(2,1)=A21; bm(2,2)=A22;
HypreParMatrix* MM=mfem::HypreParMatrixFromBlocks(bm);
mfem::PetscParMatrix* pmat=new mfem::PetscParMatrix(MM,mfem::Operator::PETSC_MATAIJ);
mfem::PetscLinearSolver* psol=new mfem::PetscLinearSolver(pmesh->GetComm());
psol->SetOperator(*pmat);
psol->SetAbsTol(abs_tol);
psol->SetRelTol(rel_tol);
psol->SetMaxIter(max_iter);
psol->SetPrintLevel(print_level);
psol->Mult(rhs, sol);
delete MM;
}
void AdvectionDiffusionMXSolver::PETSCSolver(mfem::BlockOperator& A)
{
delete psol; psol=nullptr;
delete prec; prec=nullptr;
delete pmat; pmat=nullptr;
pmat= new mfem::PetscParMatrix(pmesh->GetComm(),&A, mfem::Operator::PETSC_MATAIJ);
// construct the preconditioner
prec = new mfem::PetscFieldSplitSolver(pmesh->GetComm(),*pmat,"prec_");
// construct the linear solver
psol = new mfem::PetscLinearSolver(pmesh->GetComm());
psol->SetOperator(*pmat);
psol->SetPreconditioner(*prec);
psol->SetAbsTol(abs_tol);
psol->SetRelTol(rel_tol);
psol->SetMaxIter(max_iter);
psol->SetPrintLevel(print_level);
psol->Mult(rhs, sol);
}
void AdvectionDiffusionMXSolver::FSolve()
{
sol=0.0;
if(nfin.size()==0)
{
//add the domain integrator
nfin.push_back(new mfem::AdvectionDiffusionMX(dicoef,vecoef,mucoef,incoef));
nf->AddDomainIntegrator(nfin[nfin.size()-1]);
//add BC face integrators
for(auto it=bcc.begin();it!=bcc.end();it++)
{
mfem::AdvectionDiffusionMX* iin=new mfem::AdvectionDiffusionMX(dicoef,vecoef,mucoef,incoef);
nfin.push_back(iin);
iin->SetDirichletBCCoeficient(it->first);
nf->AddBdrFaceIntegrator(iin,it->second);
}
nf->SetGradientType(mfem::Operator::Type::Hypre_ParCSR);
}
// set the RHS
nf->Mult(sol,rhs);
rhs.Neg();
mfem::BlockOperator& A=nf->GetGradient(sol);
DirectSolver(A);
}
void AdvectionDiffusionMXSolver::ASolve(BlockVector &rhs)
{
}
}
+981
View File
@@ -0,0 +1,981 @@
#ifndef ADVDIFF_H
#define ADVDIFF_H
#include<map>
#include<vector>
#include "mfem.hpp"
namespace mfem {
class AdvectionDiffusionMX:public mfem::BlockNonlinearFormIntegrator
{
public:
AdvectionDiffusionMX()
{
diffc=nullptr;
veloc=nullptr;
inpuc=nullptr;
mucoe=nullptr;
dbc=nullptr;
gamma=10;
}
AdvectionDiffusionMX(mfem::Coefficient* diffusion_, mfem::VectorCoefficient* veloc_,
mfem::Coefficient* mu_, mfem::Coefficient* inp_)
{
diffc=diffusion_;
veloc=veloc_;
inpuc=inp_;
mucoe=mu_;
dbc=nullptr;
gamma=10;
}
void SetDirichletBCCoeficient(mfem::Coefficient* bc)
{
dbc=bc;
}
void SetVelocity(mfem::VectorCoefficient* veloc_)
{
veloc=veloc_;
}
void SetDiffusion(mfem::Coefficient* diffusion_)
{
diffc=diffusion_;
}
void SetReactionCoefficient(mfem::Coefficient* mu_)
{
mucoe=mu_;
}
void SetVolInput(mfem::Coefficient* imp_)
{
inpuc=imp_;
}
void SetDirichletBCPenalization(double penal)
{
gamma=penal;
}
virtual ~AdvectionDiffusionMX(){}
virtual
double GetElementEnergy(const Array<const FiniteElement *> &el,
ElementTransformation &Tr,
const Array<const Vector *> &elfun) override
{
return 0.0;
}
virtual
void AssembleElementVector(const Array<const FiniteElement *> &el,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array<Vector *> &elvec) override
{
int dof_u = el[0]->GetDof();
int dof_p = el[1]->GetDof();
int dof_z = el[2]->GetDof();
int dim = el[0]->GetDim();
elvec[0]->SetSize(dof_u);
elvec[1]->SetSize(dof_p);
elvec[2]->SetSize(dof_z);
*(elvec[0])=0.0;
*(elvec[1])=0.0;
*(elvec[2])=0.0;
int spaceDim = Tr.GetDimension();
if (dim != spaceDim)
{
mfem::mfem_error("AdvectionDiffusionMX::AssembleElementVector"
" is not defined on manifold meshes");
}
mfem::DenseMatrix bsu;
mfem::DenseMatrix bsp;
mfem::DenseMatrix bsz;
//set B-matrices
bsu.SetSize(dof_u,4); // [u, ux,uy,uz]
bsp.SetSize(dof_p,4); // [px,py,pz, div(p)]
bsz.SetSize(dof_z,1); // [z]
bsu=0.0;
bsp=0.0;
bsz=0.0;
Vector sh;
DenseMatrix dh;
const IntegrationRule *ir = nullptr;
int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0]);
ir=&IntRules.Get(Tr.GetGeometryType(),order);
double mmu;
DenseMatrix kap(3,3); kap=0.0;
Vector vel(3); vel=0.0;
double rhc;
double w;
Vector ss(9);
Vector rr(9);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetIntPoint(&ip);
w=Tr.Weight();
w = ip.weight * w;
sh.SetDataAndSize(bsu.GetData(),dof_u);
el[0]->CalcPhysShape(Tr,sh);
dh.UseExternalData(bsu.GetData()+dof_u,dof_u,dim);
el[0]->CalcPhysDShape(Tr,dh);
sh.SetDataAndSize(ss.GetData(),4);
bsu.MultTranspose(*(elfun[0]),sh);
dh.UseExternalData(bsp.GetData(), dof_p, dim);
el[1]->CalcPhysVShape(Tr,dh);
sh.SetDataAndSize(bsp.GetData()+3*dof_p, dof_p);
el[1]->CalcPhysDivShape(Tr,sh);
sh.SetDataAndSize(ss.GetData()+4,4);
bsp.MultTranspose(*(elfun[1]),sh);
sh.SetDataAndSize(bsz.GetData(),dof_z);
el[2]->CalcPhysShape(Tr,sh);
sh.SetDataAndSize(ss.GetData()+8,1);
bsz.MultTranspose(*(elfun[2]),sh);
mmu=0.0;
if(mucoe!=nullptr)
{
mmu=mucoe->Eval(Tr,ip);
}
if(diffc!=nullptr)
{
kap(0,0)=diffc->Eval(Tr,ip);
kap(1,1)=kap(0,0);
kap(2,2)=kap(0,0);
}
vel=0.0;
if(veloc!=nullptr)
{
veloc->Eval(vel,Tr,ip);
}
rhc=0.0;
if(inpuc!=nullptr)
{
rhc=inpuc->Eval(Tr,ip);
}
EvalQRes(kap.GetData(), vel.GetData(), mmu, rhc, ss.GetData(), rr.GetData());
sh.SetDataAndSize(rr.GetData(),4);
bsu.AddMult_a(w,sh, *(elvec[0]));
sh.SetDataAndSize(rr.GetData()+4,4);
bsp.AddMult_a(w,sh, *(elvec[1]));
sh.SetDataAndSize(rr.GetData()+8,1);
bsz.AddMult_a(w,sh, *(elvec[2]));
}//end integration loop
}
virtual
void AssembleElementGrad(const Array<const FiniteElement *> &el,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array2D<DenseMatrix *> &elmats) override
{
int dof_u = el[0]->GetDof();
int dof_p = el[1]->GetDof();
int dof_z = el[2]->GetDof();
int dim = el[0]->GetDim();
int spaceDim = Tr.GetDimension();
if (dim != spaceDim)
{
mfem::mfem_error("AdvectionDiffusionMX::AssembleElementVector"
" is not defined on manifold meshes");
}
elmats(0,0)->SetSize(dof_u,dof_u);
elmats(0,1)->SetSize(dof_u,dof_p);
elmats(0,2)->SetSize(dof_u,dof_z);
elmats(1,0)->SetSize(dof_p,dof_u);
elmats(1,1)->SetSize(dof_p,dof_p);
elmats(1,2)->SetSize(dof_p,dof_z);
elmats(2,0)->SetSize(dof_z,dof_u);
elmats(2,1)->SetSize(dof_z,dof_p);
elmats(2,2)->SetSize(dof_z,dof_z);
(*elmats(0,0))=0.0;
(*elmats(0,1))=0.0;
(*elmats(0,2))=0.0;
(*elmats(1,0))=0.0;
(*elmats(1,1))=0.0;
(*elmats(1,2))=0.0;
(*elmats(2,0))=0.0;
(*elmats(2,1))=0.0;
(*elmats(2,2))=0.0;
mfem::DenseMatrix bsu;
mfem::DenseMatrix bsp;
mfem::DenseMatrix bsz;
//set B-matrices
bsu.SetSize(dof_u,4); // [u, ux,uy,uz]
bsp.SetSize(dof_p,4); // [px,py,pz, div(p)]
bsz.SetSize(dof_z,1); // [z]
bsu=0.0;
bsp=0.0;
bsz=0.0;
Vector sh;
DenseMatrix dh;
DenseMatrix th;
DenseMatrix mh;
DenseMatrix rh;
const IntegrationRule *ir = nullptr;
int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0]);
ir=&IntRules.Get(Tr.GetGeometryType(),order);
double mmu;
DenseMatrix kap(3,3); kap=0.0;
Vector vel(3); vel=0.0;
double w;
DenseMatrix mm; //state matrix
mm.SetSize(9,9); //set the size of the state matrix
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetIntPoint(&ip);
w=Tr.Weight();
w = ip.weight * w;
sh.SetDataAndSize(bsu.GetData(),dof_u);
el[0]->CalcPhysShape(Tr,sh);
dh.UseExternalData(bsu.GetData()+dof_u,dof_u,dim);
el[0]->CalcPhysDShape(Tr,dh);
dh.UseExternalData(bsp.GetData(), dof_p, dim);
el[1]->CalcPhysVShape(Tr,dh);
sh.SetDataAndSize(bsp.GetData()+3*dof_p, dof_p);
el[1]->CalcPhysDivShape(Tr,sh);
sh.SetDataAndSize(bsz.GetData(),dof_z);
el[2]->CalcPhysShape(Tr,sh);
mmu=0.0;
if(mucoe!=nullptr)
{
mmu=mucoe->Eval(Tr,ip);
}
if(diffc!=nullptr)
{
kap(0,0)=diffc->Eval(Tr,ip);
kap(1,1)=kap(0,0);
kap(2,2)=kap(0,0);
}
vel=0.0;
if(veloc!=nullptr)
{
veloc->Eval(vel,Tr,ip);
}
EvalQMat(kap.GetData(),vel.GetData(),mmu,mm.GetData());
mh.SetSize(4,4);
mh.CopyMN(mm,4,4,0,0);
mh.Transpose();
th.SetSize(dof_u,4);
rh.SetSize(dof_u,dof_u);
MultABt(bsu,mh,th);
MultABt(th,bsu,rh);
elmats(0,0)->AddMatrix(w,rh,0,0);
mh.SetSize(4,4);
mh.CopyMN(mm,4,4,0,4);
mh.Transpose();
th.SetSize(dof_u,4);
rh.SetSize(dof_u,dof_p);
MultABt(bsu,mh,th);
MultABt(th,bsp,rh);
elmats(0,1)->AddMatrix(w,rh,0,0);
mh.SetSize(4,1);
mh.CopyMN(mm,4,1,0,8);
mh.Transpose();
th.SetSize(dof_u,1);
rh.SetSize(dof_u,dof_z);
MultABt(bsu,mh,th);
MultABt(th,bsz,rh);
elmats(0,2)->AddMatrix(w,rh,0,0);
mh.SetSize(4,4);
mh.CopyMN(mm,4,4,4,0);
mh.Transpose();
th.SetSize(dof_p,4);
rh.SetSize(dof_p,dof_u);
MultABt(bsp,mh,th);
MultABt(th,bsu,rh);
elmats(1,0)->AddMatrix(w,rh,0,0);
mh.SetSize(4,4);
mh.CopyMN(mm,4,4,4,4);
mh.Transpose();
th.SetSize(dof_p,4);
rh.SetSize(dof_p,dof_p);
MultABt(bsp,mh,th);
MultABt(th,bsp,rh);
elmats(1,1)->AddMatrix(w,rh,0,0);
mh.SetSize(4,1);
mh.CopyMN(mm,4,1,4,8);
mh.Transpose();
th.SetSize(dof_p,1);
rh.SetSize(dof_p,dof_z);
MultABt(bsp,mh,th);
MultABt(th,bsz,rh);
elmats(1,2)->AddMatrix(w,rh,0,0);
mh.SetSize(1,4);
mh.CopyMN(mm,1,4,8,0);
mh.Transpose();
th.SetSize(dof_z,4);
rh.SetSize(dof_z,dof_u);
MultABt(bsz,mh,th);
MultABt(th,bsu,rh);
elmats(2,0)->AddMatrix(w,rh,0,0);
mh.SetSize(1,4);
mh.CopyMN(mm,1,4,8,4);
mh.Transpose();
th.SetSize(dof_z,4);
rh.SetSize(dof_z,dof_p);
MultABt(bsz,mh,th);
MultABt(th,bsp,rh);
elmats(2,1)->AddMatrix(w,rh,0,0);
mh.SetSize(1,1);
mh.CopyMN(mm,1,1,8,8);
mh.Transpose();
th.SetSize(dof_z,1);
rh.SetSize(dof_z,dof_z);
MultABt(bsz,mh,th);
MultABt(th,bsz,rh);
elmats(2,2)->AddMatrix(w,rh,0,0);
}
}
virtual
void AssembleFaceVector(const Array<const FiniteElement *> &el1,
const Array<const FiniteElement *> &el2,
FaceElementTransformations &Tr,
const Array<const Vector *> &elfun,
const Array<Vector *> &elvec)
{
int dom_id=Tr.Attribute;
int dof_u = el1[0]->GetDof();
int dof_p = el1[1]->GetDof();
int dof_z = el1[2]->GetDof();
int dim = el1[0]->GetDim();
elvec[0]->SetSize(dof_u);
elvec[1]->SetSize(dof_p);
elvec[2]->SetSize(dof_z);
*(elvec[0])=0.0;
*(elvec[1])=0.0;
*(elvec[2])=0.0;
mfem::Vector bsu;
bsu.SetSize(dof_u);
mfem::Vector nor; //normal vector
mfem::Vector nir; //unit normal vector
nor.SetSize(dim);
nir.SetSize(dim);
const IntegrationRule *ir = nullptr;
int order= 2 * el1[0]->GetOrder();
ir=&IntRules.Get(Tr.GetGeometryType(),order);
double ih=0.0; //inverse of the element characteristic length
double nr=0.0; //norm of the normal vector
double ek=0.0; //the smallest eigenvalue of the diffusion tensor
double gg=0.0; //boundary value
double bp=0.0;
double w;
mfem::Vector ev(3);
Vector vel(3); vel=0.0;
DenseMatrix kap(3,3); kap=0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ipg = ir->IntPoint(i);
Tr.SetAllIntPoints(&ipg);
const mfem::IntegrationPoint &ip=Tr.GetElement1IntPoint();
mfem::CalcOrtho(Tr.Jacobian(),nor);
w = Tr.Weight();
w = ipg.weight * w;
nr=nor.Norml2();
ih=nr/Tr.Elem1->Weight();
nir.Set(1.0/nr,nor);
if(diffc!=nullptr)
{
kap(0,0)=diffc->Eval(Tr,ip);
kap(1,1)=kap(0,0);
kap(2,2)=kap(0,0);
ek=kap(0,0);
//general case
//kap.Eigenvalues(ev);
//ek=std::min(ev(0),ev(1));
//ek=std::min(ek,ev(2));
}
if(veloc!=nullptr)
{
veloc->Eval(vel,Tr,ip);
}
if(dbc!=nullptr)
{
gg=dbc->Eval(Tr,ip);
}
el1[0]->CalcShape(ip,bsu);
bp=0.0;
for(int ii=0;ii<dim;ii++)
{
bp=bp+nir(ii)*vel(ii);
}
bp=std::min(0.0,bp);
w=w*gg*(bp*bp/ih+gamma*ek*ek*ih);
elvec[0]->Add(-w,bsu);
}
}
virtual
void AssembleFaceGrad(const Array<const FiniteElement *> &el1,
const Array<const FiniteElement *> &el2,
FaceElementTransformations &Tr,
const Array<const Vector *> &elfun,
const Array2D<DenseMatrix *> &elmats)
{
int dom_id=Tr.Attribute;
int dof_u = el1[0]->GetDof();
int dof_p = el1[1]->GetDof();
int dof_z = el1[2]->GetDof();
int dim = el1[0]->GetDim();
elmats(0,0)->SetSize(dof_u,dof_u);
elmats(0,1)->SetSize(dof_u,dof_p);
elmats(0,2)->SetSize(dof_u,dof_z);
elmats(1,0)->SetSize(dof_p,dof_u);
elmats(1,1)->SetSize(dof_p,dof_p);
elmats(1,2)->SetSize(dof_p,dof_z);
elmats(2,0)->SetSize(dof_z,dof_u);
elmats(2,1)->SetSize(dof_z,dof_p);
elmats(2,2)->SetSize(dof_z,dof_z);
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
(*elmats(i,j))=0.0;
}
}
mfem::Vector bsu;
bsu.SetSize(dof_u);
mfem::Vector nor; //normal vector
mfem::Vector nir; //unit normal vector
nor.SetSize(dim);
nir.SetSize(dim);
const IntegrationRule *ir = nullptr;
int order= 2 * el1[0]->GetOrder();
ir=&IntRules.Get(Tr.GetGeometryType(),order);
double ih=0.0; //inverse of the element characteristic length
double nr=0.0; //norm of the normal vector
double ek=0.0; //the smallest eigenvalue of the diffusion tensor
double bp;
double w;
mfem::Vector ev(3);
Vector vel(3); vel=0.0;
DenseMatrix kap(3,3); kap=0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ipg = ir->IntPoint(i);
Tr.SetAllIntPoints(&ipg);
const mfem::IntegrationPoint &ip=Tr.GetElement1IntPoint();
mfem::CalcOrtho(Tr.Jacobian(),nor);
w = Tr.Weight();
w = ipg.weight * w;
nr=nor.Norml2();
ih=nr/Tr.Elem1->Weight();
nir.Set(1.0/nr,nor);
if(diffc!=nullptr)
{
kap(0,0)=diffc->Eval(Tr,ip);
kap(1,1)=kap(0,0);
kap(2,2)=kap(0,0);
ek=kap(0,0);
//general case
//kap.Eigenvalues(ev);
//ek=std::min(ev(0),ev(1));
//ek=std::min(ek,ev(2));
}
if(veloc!=nullptr)
{
veloc->Eval(vel,Tr,ip);
}
el1[0]->CalcShape(ip,bsu);
bp=0.0;
for(int ii=0;ii<nir.Size();ii++)
{
bp=bp+nir(ii)*vel(ii);
}
bp=std::min(0.0,bp);
w=w*(bp*bp/ih+gamma*ek*ek*ih);
mfem::AddMult_a_VVt(w,bsu,*elmats(0,0));
}
}
private:
mfem::Coefficient* diffc;
mfem::VectorCoefficient* veloc;
mfem::Coefficient* inpuc;
mfem::Coefficient* mucoe;
//boundary faces
double gamma; //penalization for the Nitsche method
mfem::Coefficient* dbc; //Dirichlet BC
//aa[3,3] - diffusion matrix
//bb[3] - velocity
//mmu - reaction coeficient
void EvalQRes(double* kap, double* bb, double mmu, double inp, double* uu, double* rr)
{
double t5,t11,t17;
t5 = bb[0]*uu[0]-kap[0]*uu[1]-kap[3]*uu[2]-kap[6]*uu[3]-uu[4];
t11 = bb[1]*uu[0]-kap[1]*uu[1]-kap[4]*uu[2]-kap[7]*uu[3]-uu[5];
t17 = bb[2]*uu[0]-kap[2]*uu[1]-kap[5]*uu[2]-kap[8]*uu[3]-uu[6];
rr[0] = mmu*uu[8]+t11*bb[1]+t17*bb[2]+t5*bb[0];
rr[1] = -t11*kap[1]-t17*kap[2]-t5*kap[0];
rr[2] = -t11*kap[4]-t17*kap[5]-t5*kap[3];
rr[3] = -t11*kap[7]-t17*kap[8]-t5*kap[6];
rr[4] = -t5;
rr[5] = -t11;
rr[6] = -t17;
rr[7] = uu[8];
rr[8] = mmu*uu[0]-inp+uu[7];
}
void EvalQMat(double* kap, double* bb, double mmu, double* kmat)
{
double t1,t2,t3,t8,t12,t16,t17,t18,t19,t24,t28,t29,t30;
double t31,t36,t37,t38,t39;
t1 = bb[0]*bb[0];
t2 = bb[1]*bb[1];
t3 = bb[2]*bb[2];
t8 = -bb[0]*kap[0]-bb[1]*kap[1]-bb[2]*kap[2];
t12 = -bb[0]*kap[3]-bb[1]*kap[4]-bb[2]*kap[5];
t16 = -bb[0]*kap[6]-bb[1]*kap[7]-bb[2]*kap[8];
t17 = kap[0]*kap[0];
t18 = kap[1]*kap[1];
t19 = kap[2]*kap[2];
t24 = kap[0]*kap[3]+kap[1]*kap[4]+kap[2]*kap[5];
t28 = kap[0]*kap[6]+kap[1]*kap[7]+kap[2]*kap[8];
t29 = kap[3]*kap[3];
t30 = kap[4]*kap[4];
t31 = kap[5]*kap[5];
t36 = kap[3]*kap[6]+kap[4]*kap[7]+kap[5]*kap[8];
t37 = kap[6]*kap[6];
t38 = kap[7]*kap[7];
t39 = kap[8]*kap[8];
kmat[0] = t1+t2+t3;
kmat[1] = t8;
kmat[2] = t12;
kmat[3] = t16;
kmat[4] = -bb[0];
kmat[5] = -bb[1];
kmat[6] = -bb[2];
kmat[7] = 0.0;
kmat[8] = mmu;
kmat[9] = t8;
kmat[10] = t17+t18+t19;
kmat[11] = t24;
kmat[12] = t28;
kmat[13] = kap[0];
kmat[14] = kap[1];
kmat[15] = kap[2];
kmat[16] = 0.0;
kmat[17] = 0.0;
kmat[18] = t12;
kmat[19] = t24;
kmat[20] = t29+t30+t31;
kmat[21] = t36;
kmat[22] = kap[3];
kmat[23] = kap[4];
kmat[24] = kap[5];
kmat[25] = 0.0;
kmat[26] = 0.0;
kmat[27] = t16;
kmat[28] = t28;
kmat[29] = t36;
kmat[30] = t37+t38+t39;
kmat[31] = kap[6];
kmat[32] = kap[7];
kmat[33] = kap[8];
kmat[34] = 0.0;
kmat[35] = 0.0;
kmat[36] = -bb[0];
kmat[37] = kap[0];
kmat[38] = kap[3];
kmat[39] = kap[6];
kmat[40] = 1.0;
kmat[41] = 0.0;
kmat[42] = 0.0;
kmat[43] = 0.0;
kmat[44] = 0.0;
kmat[45] = -bb[1];
kmat[46] = kap[1];
kmat[47] = kap[4];
kmat[48] = kap[7];
kmat[49] = 0.0;
kmat[50] = 1.0;
kmat[51] = 0.0;
kmat[52] = 0.0;
kmat[53] = 0.0;
kmat[54] = -bb[2];
kmat[55] = kap[2];
kmat[56] = kap[5];
kmat[57] = kap[8];
kmat[58] = 0.0;
kmat[59] = 0.0;
kmat[60] = 1.0;
kmat[61] = 0.0;
kmat[62] = 0.0;
kmat[63] = 0.0;
kmat[64] = 0.0;
kmat[65] = 0.0;
kmat[66] = 0.0;
kmat[67] = 0.0;
kmat[68] = 0.0;
kmat[69] = 0.0;
kmat[70] = 0.0;
kmat[71] = 1.0;
kmat[72] = mmu;
kmat[73] = 0.0;
kmat[74] = 0.0;
kmat[75] = 0.0;
kmat[76] = 0.0;
kmat[77] = 0.0;
kmat[78] = 0.0;
kmat[79] = 1.0;
kmat[80] = 0.0;
}
};
class AdvectionDiffusionMXSolver
{
public:
AdvectionDiffusionMXSolver(mfem::ParMesh* pmesh_, int order_=1)
{
pmesh=pmesh_;
order=order_;
int dim=pmesh->Dimension();
ufec=new mfem::H1_FECollection(order,dim);
pfec=new mfem::RT_FECollection(order,dim);
zfec=new mfem::L2_FECollection(order,dim);
ufes=new mfem::ParFiniteElementSpace(pmesh,ufec);
pfes=new mfem::ParFiniteElementSpace(pmesh,pfec);
zfes=new mfem::ParFiniteElementSpace(pmesh,zfec);
sfes.Append(ufes);
sfes.Append(pfes);
sfes.Append(zfes);
nf=new mfem::ParBlockNonlinearForm(sfes);
rhs.Update(nf->GetBlockTrueOffsets()); rhs=0.0;
sol.Update(nf->GetBlockTrueOffsets()); sol=0.0;
adj.Update(nf->GetBlockTrueOffsets()); adj=0.0;
fprim.SetSpace(ufes);
fflux.SetSpace(pfes);
fmult.SetSpace(zfes);
SetSolver();
dicoef=nullptr;
mucoef=nullptr;
vecoef=nullptr;
incoef=nullptr;
pmat=nullptr;
prec=nullptr;
psol=nullptr;
}
~AdvectionDiffusionMXSolver()
{
delete psol;
delete prec;
delete pmat;
delete nf;
delete ufes;
delete pfes;
delete zfes;
delete ufec;
delete pfec;
delete zfec;
for(auto it=bc.begin();it!=bc.end();it++)
{
delete *it;
}
}
void SetSolver(double rtol=1e-8, double atol=1e-12,int miter=1000, int prt_level=1)
{
rel_tol=rtol;
abs_tol=atol;
max_iter=miter;
print_level=prt_level;
}
void SetDiffusion(mfem::Coefficient* coef_)
{
dicoef=coef_;
}
void SetReaction(mfem::Coefficient* coef_)
{
mucoef=coef_;
}
void SetVelocity(mfem::VectorCoefficient* coef_)
{
vecoef=coef_;
}
void SetLoad(mfem::Coefficient* coef_)
{
incoef=coef_;
}
/// Solves the forward problem.
void FSolve();
/// Solves the adjoint with the provided rhs.
void ASolve(mfem::BlockVector& rhs);
mfem::ParGridFunction& GetPrimField()
{
fprim.SetFromTrueDofs(sol.GetBlock(0));
return fprim;
}
mfem::ParGridFunction& GetFluxField()
{
fflux.SetFromTrueDofs(sol.GetBlock(1));
return fflux;
}
mfem::ParGridFunction& GetMultField()
{
fmult.SetFromTrueDofs(sol.GetBlock(2));
return fmult;
}
void AddDirichletBC(int mark, double val)
{
int ni=bc.size();
bc.push_back(new mfem::ConstantCoefficient(val));
mfem::Array<int> markers(pmesh->bdr_attributes.Max());
markers=0;
markers[mark-1]=1;
bcc[bc[ni]]=markers;
}
void AddDirichletBC(int mark, mfem::Coefficient* cc)
{
//check if cc is already in
auto it=bcc.find(cc);
if(it!=bcc.end())
{
(it->second)[mark-1]=1;
}
else{
mfem::Array<int> markers(pmesh->bdr_attributes.Max());
markers=0;
markers[mark-1]=1;
bcc[cc]=markers;
}
}
private:
mfem::Coefficient* dicoef; //diffusion
mfem::Coefficient* mucoef; //reaction coefficient
mfem::VectorCoefficient* vecoef; //velocity
mfem::Coefficient* incoef; //input
mfem::ParMesh* pmesh;
int order;
std::vector<mfem::ConstantCoefficient*> bc;
std::map<mfem::Coefficient*, mfem::Array<int>> bcc;
mfem::ParFiniteElementSpace* ufes;
mfem::ParFiniteElementSpace* pfes;
mfem::ParFiniteElementSpace* zfes;
mfem::Array<mfem::ParFiniteElementSpace*> sfes;
mfem::FiniteElementCollection* ufec;
mfem::FiniteElementCollection* pfec;
mfem::FiniteElementCollection* zfec;
std::vector<mfem::AdvectionDiffusionMX*> nfin;
mfem::ParBlockNonlinearForm* nf;
mfem::BlockVector rhs;
mfem::BlockVector sol;
mfem::BlockVector adj;
// forward fields
mfem::ParGridFunction fprim;
mfem::ParGridFunction fflux;
mfem::ParGridFunction fmult;
// adjoint fields
mfem::ParGridFunction aprim;
mfem::ParGridFunction agrad;
mfem::ParGridFunction amult;
/// The PETSc objects are allocated once the problem is
/// assembled. They are utilized in computing the adjoint
/// solutions.
mfem::PetscParMatrix* pmat;
mfem::PetscPreconditioner* prec;
mfem::PetscLinearSolver* psol;
double abs_tol;
double rel_tol;
int print_level;
int max_iter;
void DirectSolver(mfem::BlockOperator& A);
void PETSCSolver(mfem::BlockOperator& A);
};
}
#endif
+162
View File
@@ -0,0 +1,162 @@
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "advdiff.hpp"
int main(int argc, char* argv[])
{
// Initialize MPI.
int nprocs, myrank;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
// Parse command-line options.
const char *mesh_file = "../../data/star.mesh";
int order = 1;
bool static_cond = false;
int ser_ref_levels = 1;
int par_ref_levels = 1;
double newton_rel_tol = 1e-7;
double newton_abs_tol = 1e-12;
int newton_iter = 10;
int print_level = 1;
bool visualization = false;
const char *petscrc_file = "advdiff_fieldsplit";
mfem::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",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&visualization,
"-vis",
"--visualization",
"-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&newton_rel_tol,
"-rel",
"--relative-tolerance",
"Relative tolerance for the Newton solve.");
args.AddOption(&newton_abs_tol,
"-abs",
"--absolute-tolerance",
"Absolute tolerance for the Newton solve.");
args.AddOption(&newton_iter,
"-it",
"--newton-iterations",
"Maximum iterations for the Newton solve.");
args.AddOption(&petscrc_file, "-petscopts", "--petscopts",
"PetscOptions file to use.");
args.Parse();
if (!args.Good())
{
if (myrank == 0)
{
args.PrintUsage(std::cout);
}
MPI_Finalize();
return 1;
}
if (myrank == 0)
{
args.PrintOptions(std::cout);
}
mfem::MFEMInitializePetsc(NULL,NULL,petscrc_file,NULL);
// Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
mfem::Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
// Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 10,000 elements.
{
int ref_levels =
(int)floor(log(100./mesh.GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh.UniformRefinement();
}
}
// 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.
mfem::ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
/*
{
for (int l = 0; l < par_ref_levels; l++)
{
pmesh.UniformRefinement();
}
}*/
mfem::AdvectionDiffusionMXSolver* solver=new mfem::AdvectionDiffusionMXSolver(&pmesh,order);
mfem::ConstantCoefficient dicoef(1.0);
mfem::ConstantCoefficient mucoef(0.0);
mfem::ConstantCoefficient incoef(1.0);
mfem::Vector veloc(3); veloc=10.0; veloc(2)=0.0;
mfem::VectorConstantCoefficient vecoef(veloc);
solver->AddDirichletBC(1,0.0);
solver->AddDirichletBC(2,3.0);
solver->SetDiffusion(&dicoef);
solver->SetLoad(&incoef);
solver->SetReaction(&mucoef);
solver->SetVelocity(&vecoef);
solver->FSolve();
{
mfem::ParGridFunction& fprim=solver->GetPrimField();
mfem::ParGridFunction& fflux=solver->GetFluxField();
mfem::ParGridFunction& fmult=solver->GetMultField();
mfem::ParaViewDataCollection paraview_dc("AdvectionDiffusion", &pmesh);
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(order);
paraview_dc.SetDataFormat(mfem::VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.SetCycle(0);
paraview_dc.SetTime(0.0);
paraview_dc.RegisterField("prim",&fprim);
paraview_dc.RegisterField("flux",&fflux);
paraview_dc.RegisterField("mult",&fmult);
paraview_dc.Save();
}
delete solver;
mfem::MFEMFinalizePetsc();
MPI_Finalize();
return 0;
}
+844
View File
@@ -0,0 +1,844 @@
// Shared implementation ex71p/ex71 for the AD integrands and the manually
// implemented integrators
#ifndef ADEXAMPLE_HPP
#define ADEXAMPLE_HPP
#include "mfem.hpp"
#include "admfem.hpp"
#include <memory>
#include <iostream>
#include <fstream>
namespace mfem
{
///Example: Implementation of the energy and the residual
/// for p-Laplacian problem. Both, the energy and the residual
/// are evaluated at the integration points for PDE parameters
/// vparam and state fields (derivatives with respect to x,y,z
/// and primal field) stored in vector uu.
template<typename TDataType, typename TParamVector, typename TStateVector,
int residual_size, int state_size, int param_size>
class MyVFunctor
{
public:
///The operator returns the first derivative of the energy with respect
/// to all state variables. These are set in vector uu and consist of the
/// derivatives with respect to x,y,z and the primal field. The derivative
/// is stored in vector rr with length equal to the length of vector uu.
void operator()(TParamVector &vparam, TStateVector &uu, TStateVector &rr)
{
MFEM_ASSERT(residual_size==4,"PLaplacianResidual residual_size should be equal to 4!")
double pp = vparam[0];
double ee = vparam[1];
double ff = vparam[2];
TDataType norm2 = uu[0] * uu[0] + uu[1] * uu[1] + uu[2] * uu[2];
TDataType tvar = pow(ee * ee + norm2, (pp - 2.0) / 2.0);
rr[0] = tvar * uu[0];
rr[1] = tvar * uu[1];
rr[2] = tvar * uu[2];
rr[3] = -ff;
}
};
///Defines template class (functor) for evaluating the energy
/// of the p-Laplacian problem. The input parameters vparam are:
/// vparam[0] - the p-Laplacian power, vparam[1] small value
/// ensuring exsitance of an unique solution, and vparam[2] -
/// the distributed extenal input to the PDE.
template<typename TDataType, typename TParamVector, typename TStateVector
, int state_size, int param_size>
class MyQFunctor
{
public:
///Returns the energy of a p-Laplacian for state field input
/// provided in vector uu and parameters provided in vector
/// vparam.
TDataType operator()(TParamVector &vparam, TStateVector &uu)
{
MFEM_ASSERT(state_size==4,"MyQFunctor state_size should be equal to 4!");
MFEM_ASSERT(param_size==3,"MyQFunctor param_size should be equal to 3!");
double pp = vparam[0];
double ee = vparam[1];
double ff = vparam[2];
TDataType u = uu[3];
TDataType norm2 = uu[0] * uu[0] + uu[1] * uu[1] + uu[2] * uu[2];
TDataType rez = pow(ee * ee + norm2, pp / 2.0) / pp - ff * u;
return rez;
}
};
///Implements integrator for a p-Laplacian problem.
/// The integrator is based on a class QFunction utilized for
/// evaluating the energy, the first derivative (residual) and
/// the Hessian of the energy (the Jacobian of the residual).
template<class CQVectAutoDiff>
class pLaplaceAD : public NonlinearFormIntegrator
{
protected:
Coefficient *pp;
Coefficient *coeff;
Coefficient *load;
CQVectAutoDiff rdf;
public:
pLaplaceAD()
{
coeff = nullptr;
pp = nullptr;
}
pLaplaceAD(Coefficient &pp_) : pp(&pp_), coeff(nullptr), load(nullptr) {}
pLaplaceAD(Coefficient &pp_, Coefficient &q, Coefficient &ld_)
: pp(&pp_), coeff(&q), load(&ld_)
{}
virtual ~pLaplaceAD() {}
virtual double GetElementEnergy(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun) override
{
double energy = 0.0;
int ndof = el.GetDof();
int ndim = el.GetDim();
int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
const IntegrationRule *ir = NULL;
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
ir = &IntRules.Get(el.GetGeomType(), order);
Vector shapef(ndof);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector grad(spaceDim);
Vector vparam(3); //[power, epsilon, load]
Vector uu(4); //[diff_x,diff_y,diff_z,u]
uu = 0.0;
vparam[0] = 2.0; //default power
vparam[1] = 1e-8; //default epsilon
vparam[2] = 1.0; //default load
double w;
double detJ;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// calculate the gradient
dshape_xyz.MultTranspose(elfun, grad);
// set the power
if (pp != nullptr)
{
vparam[0] = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
vparam[1] = coeff->Eval(trans, ip);
}
// add the contribution from the load
if (load != nullptr)
{
vparam[2] = load->Eval(trans, ip);
}
// fill the values of vector uu
for (int jj = 0; jj < spaceDim; jj++)
{
uu[jj] = grad[jj] / detJ;
}
uu[3] = shapef * elfun;
// the energy is taken directly the the templated function
MyQFunctor<double,Vector,Vector,4,3> qfunc;
energy = energy + w * qfunc(vparam,uu);
}
return energy;
}
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun,
Vector &elvect) override
{
MFEM_PERF_BEGIN("AssembleElementVector");
int ndof = el.GetDof();
int ndim = el.GetDim();
int spaceDim = trans.GetSpaceDim();
const IntegrationRule *ir = NULL;
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
ir = &IntRules.Get(el.GetGeomType(), order);
Vector shapef(ndof);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector lvec(ndof);
elvect.SetSize(ndof);
elvect = 0.0;
DenseMatrix B(ndof, 4); //[diff_x,diff_y,diff_z, shape]
Vector vparam(3); //[power, epsilon, load]
Vector uu(4); //[diff_x,diff_y,diff_z,u]
Vector du(4);
B = 0.0;
uu = 0.0;
//initialize the parameters - keep the same order
//utilized in the pLapIntegrator definition
vparam[0] = 2.0; //default power
vparam[1] = 1e-8; //default epsilon
vparam[2] = 1.0; //default load
double w;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
//detJ = (square ? w : w*w);
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
Mult(dshape_iso, trans.InverseJacobian(), dshape_xyz);
// set the matrix B
for (int jj = 0; jj < spaceDim; jj++)
{
B.SetCol(jj, dshape_xyz.GetColumn(jj));
}
B.SetCol(3, shapef);
// set the power
if (pp != nullptr)
{
vparam[0] = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
vparam[1] = coeff->Eval(trans, ip);
}
// add the contribution from the load
if (load != nullptr)
{
vparam[2] = load->Eval(trans, ip);
}
// calculate uu
B.MultTranspose(elfun, uu);
// calculate derivative of the energy with respect to uu
rdf.QVectorFunc(vparam,uu,du);
B.Mult(du, lvec);
elvect.Add(w, lvec);
} // end integration loop
MFEM_PERF_END("AssembleElementVector");
}
virtual void AssembleElementGrad(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun,
DenseMatrix &elmat) override
{
MFEM_PERF_BEGIN("AssembleElementGrad");
int ndof = el.GetDof();
int ndim = el.GetDim();
int spaceDim = trans.GetSpaceDim();
const IntegrationRule *ir = NULL;
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
ir = &IntRules.Get(el.GetGeomType(), order);
Vector shapef(ndof);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
elmat.SetSize(ndof, ndof);
elmat = 0.0;
DenseMatrix B(ndof, 4); // [diff_x,diff_y,diff_z, shape]
DenseMatrix A(ndof, 4);
Vector vparam(3); // [power, epsilon, load]
Vector uu(4); // [diff_x,diff_y,diff_z,u]
DenseMatrix duu(4, 4);
B = 0.0;
uu = 0.0;
// initialize the parameters - keep the same order
// utilized in the pLapIntegrator definition
vparam[0] = 2.0; // default power
vparam[1] = 1e-8; // default epsilon
vparam[2] = 1.0; // default load
double w;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
Mult(dshape_iso, trans.InverseJacobian(), dshape_xyz);
// set the matrix B
for (int jj = 0; jj < spaceDim; jj++)
{
B.SetCol(jj, dshape_xyz.GetColumn(jj));
}
B.SetCol(3, shapef);
// set the power
if (pp != nullptr)
{
vparam[0] = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
vparam[1] = coeff->Eval(trans, ip);
}
// add the contribution from the load
if (load != nullptr)
{
vparam[2] = load->Eval(trans, ip);
}
// calculate uu
B.MultTranspose(elfun, uu);
// calculate derivative of the energy with respect to uu
rdf.QJacobian(vparam,uu,duu);
Mult(B, duu, A);
AddMult_a_ABt(w, A, B, elmat);
} // end integration loop
MFEM_PERF_END("AssembleElementGrad");
}
};
///Implements hand-coded integrator for a p-Laplacian problem.
/// Utilized as alternative for the pLaplaceAD class based on
/// automatic differentiation.
class pLaplace : public NonlinearFormIntegrator
{
protected:
Coefficient *pp;
Coefficient *coeff;
Coefficient *load;
public:
pLaplace()
{
coeff = nullptr;
pp = nullptr;
}
pLaplace(Coefficient &pp_) : pp(&pp_), coeff(nullptr), load(nullptr) {}
pLaplace(Coefficient &pp_, Coefficient &q, Coefficient &ld_)
: pp(&pp_), coeff(&q), load(&ld_)
{}
virtual ~pLaplace() {}
virtual double GetElementEnergy(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun) override
{
double energy = 0.0;
int ndof = el.GetDof();
int ndim = el.GetDim();
int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
const IntegrationRule *ir = NULL;
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
ir = &IntRules.Get(el.GetGeomType(), order);
Vector shapef(ndof);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector grad(spaceDim);
double w;
double detJ;
double nrgrad2;
double ppp = 2.0;
double eee = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// calculate the gradient
dshape_xyz.MultTranspose(elfun, grad);
nrgrad2 = grad * grad / (detJ * detJ);
// set the power
if (pp != nullptr)
{
ppp = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
eee = coeff->Eval(trans, ip);
}
energy = energy + w * std::pow(nrgrad2 + eee * eee, ppp / 2.0) / ppp;
// add the contribution from the load
if (load != nullptr)
{
energy = energy - w * (shapef * elfun) * load->Eval(trans, ip);
}
}
return energy;
}
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun,
Vector &elvect) override
{
MFEM_PERF_BEGIN("AssembleElementVector");
int ndof = el.GetDof();
int ndim = el.GetDim();
int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
const IntegrationRule *ir = NULL;
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
ir = &IntRules.Get(el.GetGeomType(), order);
Vector shapef(ndof);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector grad(spaceDim);
Vector lvec(ndof);
elvect.SetSize(ndof);
elvect = 0.0;
double w;
double detJ;
double nrgrad;
double aa;
double ppp = 2.0;
double eee = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w; //w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// calculate the gradient
dshape_xyz.MultTranspose(elfun, grad);
nrgrad = grad.Norml2() / detJ;
// grad is not scaled so far, i.e., grad=grad/detJ
// set the power
if (pp != nullptr)
{
ppp = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
eee = coeff->Eval(trans, ip);
}
aa = nrgrad * nrgrad + eee * eee;
aa = std::pow(aa, (ppp - 2.0) / 2.0);
dshape_xyz.Mult(grad, lvec);
elvect.Add(w * aa / (detJ * detJ), lvec);
// add loading
if (load != nullptr)
{
elvect.Add(-w * load->Eval(trans, ip), shapef);
}
} // end integration loop
MFEM_PERF_END("AssembleElementVector");
}
virtual void AssembleElementGrad(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun,
DenseMatrix &elmat) override
{
MFEM_PERF_BEGIN("AssembleElementGrad");
int ndof = el.GetDof();
int ndim = el.GetDim();
int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
const IntegrationRule *ir = NULL;
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
ir = &IntRules.Get(el.GetGeomType(), order);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector grad(spaceDim);
Vector lvec(ndof);
elmat.SetSize(ndof, ndof);
elmat = 0.0;
double w;
double detJ;
double nrgrad;
double aa0;
double aa1;
double ppp = 2.0;
double eee = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// grad is not scaled so far,i.e., grad=grad/detJ
//set the power
if (pp != nullptr)
{
ppp = pp->Eval(trans, ip);
}
//set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
eee = coeff->Eval(trans, ip);
}
//calculate the gradient
dshape_xyz.MultTranspose(elfun, grad);
nrgrad = grad.Norml2() / detJ;
aa0 = nrgrad * nrgrad + eee * eee;
aa1 = std::pow(aa0, (ppp - 2.0) / 2.0);
aa0 = (ppp - 2.0) * std::pow(aa0, (ppp - 4.0) / 2.0);
dshape_xyz.Mult(grad, lvec);
w = w / (detJ * detJ);
AddMult_a_VVt(w * aa0 / (detJ * detJ), lvec, elmat);
AddMult_a_AAt(w * aa1, dshape_xyz, elmat);
} // end integration loop
MFEM_PERF_END("AssembleElementGrad");
}
};
/// Implements AD enabled integrator for a p-Laplacian problem.
/// The tangent matrix is computed using the residual of the
/// element.
template<int sizeres=10>
class pLaplaceSL : public NonlinearFormIntegrator
{
protected:
Coefficient *pp;
Coefficient *coeff;
Coefficient *load;
public:
pLaplaceSL()
{
coeff = nullptr;
pp = nullptr;
}
pLaplaceSL(Coefficient &pp_) : pp(&pp_), coeff(nullptr), load(nullptr) {}
pLaplaceSL(Coefficient &pp_, Coefficient &q, Coefficient &ld_)
: pp(&pp_), coeff(&q), load(&ld_)
{}
virtual ~pLaplaceSL() {}
virtual double GetElementEnergy(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun) override
{
double energy = 0.0;
int ndof = el.GetDof();
int ndim = el.GetDim();
int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
const IntegrationRule *ir = NULL;
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
ir = &IntRules.Get(el.GetGeomType(), order);
Vector shapef(ndof);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector grad(spaceDim);
double w;
double detJ;
double nrgrad2;
double ppp = 2.0;
double eee = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// calculate the gradient
dshape_xyz.MultTranspose(elfun, grad);
nrgrad2 = grad * grad / (detJ * detJ);
// set the power
if (pp != nullptr)
{
ppp = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
eee = coeff->Eval(trans, ip);
}
energy = energy + w * std::pow(nrgrad2 + eee * eee, ppp / 2.0) / ppp;
// add the contribution from the load
if (load != nullptr)
{
energy = energy - w * (shapef * elfun) * load->Eval(trans, ip);
}
}
return energy;
}
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun,
Vector &elvect) override
{
MFEM_PERF_BEGIN("AssembleElementVector");
int ndof = el.GetDof();
int ndim = el.GetDim();
int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
const IntegrationRule *ir = NULL;
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
ir = &IntRules.Get(el.GetGeomType(), order);
Vector shapef(ndof);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector grad(spaceDim);
Vector lvec(ndof);
elvect.SetSize(ndof);
elvect = 0.0;
double w;
double detJ;
double nrgrad;
double aa;
double ppp = 2.0;
double eee = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w; //w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// calculate the gradient
dshape_xyz.MultTranspose(elfun, grad);
nrgrad = grad.Norml2() / detJ;
// grad is not scaled so far, i.e., grad=grad/detJ
// set the power
if (pp != nullptr)
{
ppp = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
eee = coeff->Eval(trans, ip);
}
aa = nrgrad * nrgrad + eee * eee;
aa = std::pow(aa, (ppp - 2.0) / 2.0);
dshape_xyz.Mult(grad, lvec);
elvect.Add(w * aa / (detJ * detJ), lvec);
// add loading
if (load != nullptr)
{
elvect.Add(-w * load->Eval(trans, ip), shapef);
}
} // end integration loop
MFEM_PERF_END("AssembleElementVector");
}
virtual void AssembleElementGrad(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun,
DenseMatrix &elmat) override
{
MFEM_PERF_BEGIN("AssembleElementGrad");
int ndof = el.GetDof();
int ndim = el.GetDim();
int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
const IntegrationRule *ir = NULL;
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
ir = &IntRules.Get(el.GetGeomType(), order);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
elmat.SetSize(ndof, ndof);
elmat = 0.0;
double w;
double detJ;
double ppp = 2.0;
double eee = 0.0;
mfem::Vector param(3); param=0.0;
// Computes the residual at an integration point.
// The implementation is a copy of the integration loop
// in AssembleElementVector.
auto resfun = [&](mfem::Vector& vparam, mfem::ad::ADVectorType& uu, mfem::ad::ADVectorType& vres){
vres.SetSize(uu.Size()); vres=0.0;
mfem::ad::ADVectorType grad(spaceDim);
mfem::ad::ADFloatType nrgrad;
mfem::ad::ADFloatType aa;
mfem::ad::ADVectorType lvec(ndof);
for (int i = 0; i < ir->GetNPoints(); i++)
{
lvec=0.0;
const IntegrationPoint &ip = ir->IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// grad is not scaled so far,i.e., grad=grad/detJ
//set the power
if (pp != nullptr)
{
ppp = pp->Eval(trans, ip);
}
//set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
eee = coeff->Eval(trans, ip);
}
grad=0.0;
//calculate the gradient
for(int i=0;i<spaceDim;i++){
for(int j=0;j<ndof;j++){
grad[i]= grad[i]+ dshape_xyz(j,i)*uu[j];
}}
nrgrad= (grad*grad)/(detJ*detJ);
aa = nrgrad + eee * eee;
aa = pow(aa, (ppp - 2.0) / 2.0);
for(int i=0;i<spaceDim;i++){
for(int j=0;j<ndof;j++){
lvec[j] = lvec[j] + dshape_xyz(j,i) * grad[i];
}}
for(int j=0;j<ndof;j++)
{
vres[j]=vres[j] + lvec[j] * (w*aa/(detJ*detJ));
}
}
};
mfem::Vector bla(elfun);
//calculate the gradient - only for a fixed ndof
mfem::VectorFuncAutoDiff<sizeres,sizeres,3> fdr(resfun);
fdr.QJacobian(param, bla, elmat);
MFEM_PERF_END("AssembleElementGrad");
}
};
} // namespace mfem
#endif
+548
View File
@@ -0,0 +1,548 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef FDUAL_H
#define FDUAL_H
#include <cmath>
#include <type_traits>
namespace mfem
{
namespace ad
{
/** The FDual template class provides forward automatic differentiation
implementation based on dual numbers. The derivative of an arbitrary
function double f(double a) can be obtained by replacing the double
type for the return value and the argument a with FDual<double>, i.e.,
FDual<double> f(FDual<double> a). The derivative is evaluated automatically
by calling the function r=f(a). The value of the function is stored in r.pr
and the derivative in r.du. These can be extracted by the corresponding
methods real()/prim() and dual(). Internally, the function f can be
composed of standard functions predefined for FDual type. These consist
of a large set of functions replicating the functionality of the standard
math library, i.e., sin, cos, exp, log, ...
New functions (non-member) can be eaily added to the class. Example:
template<typename tbase>
inline FDual<tbase> cos(const FDual<tbase> &f)
{
return FDual<tbase>(cos(f.real()), -f.dual() * sin(f.real()));
}
The real part of the return value consists of the standard real value
of the function, i.e., cos(f.real()).
The dual part of the return value consists of the first derivative of
the function with respect to the real part of the argument -sin(f.reaf)
multiplied with the dual part of the argument f.dual().
*/
template<typename tbase>
class FDual
{
private:
/// Real value
tbase pr;
/// Dual value holding derivative information
tbase du;
public:
/// Standard contructor - both values are set to zero.
FDual() : pr(0), du(0) {}
/// The constructor utilized in nested definition of dual numbers.
/// It is used for second and higher order derivatives.
template<class fltyp,
class = typename std::enable_if<std::is_arithmetic<fltyp>::value>::type>
FDual(fltyp &f) : pr(f), du(0)
{}
template<class fltyp,
class = typename std::enable_if<std::is_arithmetic<fltyp>::value>::type>
FDual(const fltyp &f) : pr(f), du(0)
{}
/// Standard constructor with user supplied inpur for both parts of the dual number.
FDual(tbase &pr_, tbase &du_) : pr(pr_), du(du_) {}
FDual(const tbase &pr_, const tbase &du_) : pr(pr_), du(du_) {}
FDual(FDual<tbase> &nm) : pr(nm.pr), du(nm.du) {}
FDual(const FDual<tbase> &nm) : pr(nm.pr), du(nm.du) {}
/// Return the real value of the dual number.
tbase prim() const { return pr; }
/// Same as prim(). Return the real value of the dual number.
tbase real() const { return pr; }
/// Return the dual value of the dual number.
tbase dual() const { return du; }
/// Set the primal and the dual values.
void set(const tbase &pr_, const tbase &du_)
{
pr = pr_;
du = du_;
}
void prim(const tbase &pr_) { pr = pr_; }
void real(const tbase &pr_) { pr = pr_; }
void dual(const tbase &du_) { du = du_; }
FDual<tbase> &operator=(tbase sc_)
{
pr = sc_;
du = tbase(0);
return *this;
}
FDual<tbase> &operator+=(tbase sc_)
{
pr = pr + sc_;
return *this;
}
FDual<tbase> &operator-=(tbase sc_)
{
pr = pr - sc_;
return *this;
}
FDual<tbase> &operator*=(tbase sc_)
{
pr = pr * sc_;
du = du * sc_;
return *this;
}
FDual<tbase> &operator/=(tbase sc_)
{
pr = pr / sc_;
du = du / sc_;
return *this;
}
FDual<tbase> &operator=(const FDual<tbase> &f)
{
pr = f.real();
du = f.dual();
return *this;
}
FDual<tbase> &operator+=(const FDual<tbase> &f)
{
pr += f.real();
du += f.dual();
return *this;
}
FDual<tbase> &operator-=(const FDual<tbase> &f)
{
pr -= f.real();
du -= f.dual();
return *this;
}
FDual<tbase> &operator*=(const FDual<tbase> &f)
{
du = du * f.real();
du = du + pr * f.dual();
pr = pr * f.real();
return *this;
}
FDual<tbase> &operator/=(const FDual<tbase> &f_)
{
pr = pr / f_.real();
du = du - pr * f_.dual();
du = du / f_.real();
return *this;
}
};
// non-member functions
// boolean operations
template<typename tbase>
inline bool operator==(const FDual<tbase> &a1, const FDual<tbase> &a2)
{
return a1.real() == a2.real();
}
template<typename tbase>
inline bool operator==(tbase a, const FDual<tbase> &f_)
{
return a == f_.real();
}
template<typename tbase>
inline bool operator==(const FDual<tbase> &a, tbase b)
{
return a.real() == b;
}
template<typename tbase>
inline bool operator<(const FDual<tbase> &f1, const FDual<tbase> &f2)
{
return f1.real() < f2.real();
}
template<typename tbase>
inline bool operator<(const FDual<tbase> &f, tbase a)
{
return f.real() < a;
}
template<typename tbase>
inline bool operator<(tbase a, const FDual<tbase> &f)
{
return a < f.real();
}
template<typename tbase>
inline bool operator>(const FDual<tbase> &f1, const FDual<tbase> &f2)
{
return f1.real() > f2.real();
}
template<typename tbase>
inline bool operator>(const FDual<tbase> &f, tbase a)
{
return f.real() > a;
}
template<typename tbase>
inline bool operator>(tbase a, const FDual<tbase> &f)
{
return (a > f.real());
}
template<typename tbase>
inline FDual<tbase> operator-(const FDual<tbase> &f)
{
return FDual<tbase>(-f.real(), -f.dual());
}
template<typename tbase>
inline FDual<tbase> operator-(const FDual<tbase> &f, tbase a)
{
return FDual<tbase>(f.real() - a, f.dual());
}
template<typename tbase>
inline FDual<FDual<tbase>> operator-(const FDual<FDual<tbase>> &f, tbase a)
{
return FDual<FDual<tbase>>(f.real() - a, f.dual());
}
template<typename tbase>
inline FDual<tbase> operator+(const FDual<tbase> &f, tbase a)
{
return FDual<tbase>(f.real() + a, f.dual());
}
template<typename tbase>
inline FDual<FDual<tbase>> operator+(const FDual<FDual<tbase>> &f, tbase a)
{
return FDual<FDual<tbase>>(f.real() + a, f.dual());
}
template<typename tbase>
inline FDual<tbase> operator*(const FDual<tbase> &f, tbase a)
{
return FDual<tbase>(f.real() * a, f.dual() * a);
}
template<typename tbase>
inline FDual<tbase> operator/(const FDual<tbase> &f, tbase a)
{
return FDual<tbase>(f.real() / a, f.dual() / a);
}
template<typename tbase>
inline FDual<FDual<tbase>> operator/(const FDual<FDual<tbase>> &f, tbase a)
{
return FDual<FDual<tbase>>(f.real() / a, f.dual() / a);
}
template<typename tbase>
inline FDual<tbase> operator+(tbase a, const FDual<tbase> &f)
{
return FDual<tbase>(a + f.real(), f.dual());
}
template<typename tbase>
inline FDual<FDual<tbase>> operator+(tbase a, const FDual<FDual<tbase>> &f)
{
return FDual<FDual<tbase>>(a + f.real(), f.dual());
}
template<typename tbase>
inline FDual<tbase> operator-(tbase a, const FDual<tbase> &f)
{
return FDual<tbase>(a - f.real(), -f.dual());
}
template<typename tbase>
inline FDual<FDual<tbase>> operator-(tbase a, const FDual<FDual<tbase>> &f)
{
return FDual<FDual<tbase>>(a - f.real(), -f.dual());
}
template<typename tbase>
inline FDual<tbase> operator*(tbase a, const FDual<tbase> &f)
{
return FDual<tbase>(f.real() * a, f.dual() * a);
}
template<typename tbase>
inline FDual<FDual<tbase>> operator*(tbase a, const FDual<FDual<tbase>> &f)
{
return FDual<FDual<tbase>>(f.real() * a, f.dual() * a);
}
template<typename tbase>
inline FDual<tbase> operator/(tbase a, const FDual<tbase> &f)
{
a = a / f.real();
return FDual<tbase>(a, -a * f.dual() / f.real());
}
template<typename tbase>
inline FDual<tbase> operator+(const FDual<tbase> &f1, const FDual<tbase> &f2)
{
return FDual<tbase>(f1.real() + f2.real(), f1.dual() + f2.dual());
}
template<typename tbase>
inline FDual<tbase> operator-(const FDual<tbase> &f1, const FDual<tbase> &f2)
{
return FDual<tbase>(f1.real() - f2.real(), f1.dual() - f2.dual());
}
template<typename tbase>
inline FDual<tbase> operator*(const FDual<tbase> &f1, const FDual<tbase> &f2)
{
return FDual<tbase>(f1.real() * f2.real(),
f1.real() * f2.dual() + f1.dual() * f2.real());
}
template<typename tbase>
inline FDual<tbase> operator/(const FDual<tbase> &f1, const FDual<tbase> &f2)
{
tbase a = tbase(1) / f2.real();
tbase b = f1.real() * a;
return FDual<tbase>(b, (f1.dual() - f2.dual() * b) * a);
}
template<typename tbase>
inline FDual<tbase> acos(const FDual<tbase> &f)
{
return FDual<tbase>(acos(f.real()),
-f.dual() / sqrt(tbase(1) - f.real() * f.real()));
}
template<>
inline FDual<double> acos(const FDual<double> &f)
{
return FDual<double>(std::acos(f.real()),
-f.dual() / std::sqrt(double(1) - f.real() * f.real()));
}
template<typename tbase>
inline FDual<tbase> asin(const FDual<tbase> &f)
{
return FDual<tbase>(asin(f.real()),
f.dual() / sqrt(tbase(1) - f.real() * f.real()));
}
template<>
inline FDual<double> asin(const FDual<double> &f)
{
return FDual<double>(std::asin(f.real()),
f.dual() / std::sqrt(double(1) - f.real() * f.real()));
}
template<typename tbase>
inline FDual<tbase> atan(const FDual<tbase> &f)
{
return FDual<tbase>(atan(f.real()),
f.dual() / (tbase(1) + f.real() * f.real()));
}
template<>
inline FDual<double> atan(const FDual<double> &f)
{
return FDual<double>(std::atan(f.real()),
f.dual() / (double(1) + f.real() * f.real()));
}
template<typename tbase>
inline FDual<tbase> cos(const FDual<tbase> &f)
{
return FDual<tbase>(cos(f.real()), -f.dual() * sin(f.real()));
}
template<>
inline FDual<double> cos(const FDual<double> &f)
{
return FDual<double>(std::cos(f.real()), -f.dual() * std::sin(f.real()));
}
template<typename tbase>
inline FDual<tbase> cosh(const FDual<tbase> &f)
{
return FDual<tbase>(cosh(f.real()), f.dual() * sinh(f.real()));
}
template<>
inline FDual<double> cosh(const FDual<double> &f)
{
return FDual<double>(std::cosh(f.real()), f.dual() * std::sinh(f.real()));
}
template<typename tbase>
inline FDual<tbase> exp(const FDual<tbase> &f)
{
tbase x = exp(f.real());
return FDual<tbase>(x, f.dual() * x);
}
template<>
inline FDual<double> exp(const FDual<double> &f)
{
double x = std::exp(f.real());
return FDual<double>(x, f.dual() * x);
}
template<typename tbase>
inline FDual<tbase> log(const FDual<tbase> &f)
{
return FDual<tbase>(log(f.real()), f.dual() / f.real());
}
template<>
inline FDual<double> log(const FDual<double> &f)
{
return FDual<double>(std::log(f.real()), f.dual() / f.real());
}
template<typename tbase>
inline FDual<tbase> log10(const FDual<tbase> &f)
{
return log(f) / log(tbase(10));
}
template<>
inline FDual<double> log10(const FDual<double> &f)
{
return log(f) / std::log(double(10));
}
template<typename tbase>
inline FDual<tbase> pow(const FDual<tbase> &a, const FDual<tbase> &b)
{
return exp(log(a) * b);
}
template<typename tbase, typename tbase1>
inline FDual<tbase> pow(const FDual<tbase> &a, const tbase1 &b)
{
return exp(log(a) * tbase(b));
}
template<typename tbase, typename tbase1>
inline FDual<tbase> pow(const tbase1 &a, const FDual<tbase> &b)
{
return exp(log(tbase(a)) * b);
}
template<>
inline FDual<double> pow(const double &a, const FDual<double> &b)
{
return exp(std::log(a) * b);
}
template<typename tbase>
inline FDual<tbase> sin(const FDual<tbase> &f)
{
return FDual<tbase>(sin(f.real()), f.dual() * cos(f.real()));
}
template<>
inline FDual<double> sin(const FDual<double> &f)
{
return FDual<double>(std::sin(f.real()), f.dual() * std::cos(f.real()));
}
template<typename tbase>
inline FDual<tbase> sinh(const FDual<tbase> &f)
{
return FDual<tbase>(sinh(f.real()), f.dual() * cosh(f.real()));
}
template<>
inline FDual<double> sinh(const FDual<double> &f)
{
return FDual<double>(std::sinh(f.real()), f.dual() * std::cosh(f.real()));
}
template<typename tbase>
inline FDual<tbase> sqrt(const FDual<tbase> &f)
{
tbase a = sqrt(f.real());
return FDual<tbase>(a, f.dual() / (tbase(2) * a));
}
template<>
inline FDual<double> sqrt(const FDual<double> &f)
{
double a = std::sqrt(f.real());
return FDual<double>(a, f.dual() / (double(2) * a));
}
template<typename tbase>
inline FDual<tbase> tan(const FDual<tbase> &f)
{
tbase a = tan(f.real());
return FDual<tbase>(a, f.dual() * (tbase(1) + a * a));
}
template<>
inline FDual<double> tan(const FDual<double> &f)
{
double a = std::tan(f.real());
return FDual<double>(a, f.dual() * (double(1) + a * a));
}
template<typename tbase>
inline FDual<tbase> tanh(const FDual<tbase> &f)
{
tbase a = tanh(f.real());
return FDual<tbase>(a, f.dual() * (tbase(1) - a * a));
}
template<>
inline FDual<double> tanh(const FDual<double> &f)
{
double a = std::tanh(f.real());
return FDual<double>(a, f.dual() * (double(1) - a * a));
}
} // namespace ad
} // namespace mfem
#endif
+524
View File
@@ -0,0 +1,524 @@
// MFEM Example 71 - Parallel Version
//
// Compile with: make ex71p
//
// Sample runs:
// mpirun -np 2 ex71p -m ../data/beam-quad.mesh -pp 3.8
// mpirun -np 2 ex71p -m ../data/beam-tri.mesh -pp 7.2
// mpirun -np 2 ex71p -m ../data/beam-hex.mesh
// mpirun -np 2 ex71p -m ../data/beam-tet.mesh
// mpirun -np 2 ex71p -m ../data/beam-wedge.mesh
//
// Description: This examples solves a quasi-static nonlinear
// p-Laplacian problem with zero Dirichlet boundary
// conditions applied on all defined boundaries
//
// The example demonstrates the use of nonlinear operators combined
// with automatic differentiation (AD). The definitions of the
// integrators are written in the ex71.hpp. Selecting integrator=0
// will use the manually implemented integrator. Selecting
// integrator=1,2 will utilize the AD integrator.
//
// The AD integrators are implemented in ex71.hpp (pLaplaceAD).
// The integrand qint is a function which is evaluated at every
// integration point. For implementations utilizing ADQFunctionTJ,
// the user has to implement the function and the residual
// evaluation. The Jacobian of the residual is evaluated using AD
// For implementations utilizing ADQFunctionTH, the user has to
// implement only the function evaluation (as a template) and the
// first derivative (the residual) and the second derivatives (the
// Hessian) are evaluated using AD.
//
// We recommend viewing examples 1 and 19, before viewing this
// example.
#include "example.hpp"
using namespace mfem;
///Non-linear solver for the p-Laplacian problem.
class ParNLSolverPLaplacian
{
public:
///Constructor Input: imesh - FE mesh, finite element space,
/// power for the p-Laplacian, external load (source, input),
/// regularization parameter
ParNLSolverPLaplacian(MPI_Comm comm, ParMesh& imesh,
ParFiniteElementSpace& ifespace,
double powerp=2,
Coefficient* load=nullptr,
double regularizationp=1e-7)
{
lcomm = comm;
//default parameters for
//the Newton solver
newton_rtol = 1e-4;
newton_atol = 1e-8;
newton_iter = 10;
//linear solver
linear_rtol = 1e-7;
linear_atol = 1e-15;
linear_iter = 500;
print_level = 0;
//set the mesh
mesh=&imesh;
//set the fespace
fespace=&ifespace;
//set the parameters
plap_epsilon=new ConstantCoefficient(regularizationp);
plap_power=new ConstantCoefficient(powerp);
if (load==nullptr)
{
plap_input=new ConstantCoefficient(1.0);
input_ownership=true;
}
else
{
plap_input=load;
input_ownership=false;
}
nf=nullptr;
ns=nullptr;
gmres=nullptr;
prec=nullptr;
//set the default integrator
integ=0; //hand coded
}
~ParNLSolverPLaplacian()
{
if (nf!=nullptr) { delete nf;}
if (ns!=nullptr) { delete ns;}
if (prec!=nullptr) { delete prec;}
if (gmres!=nullptr) { delete gmres;}
if (input_ownership) { delete plap_input;}
delete plap_epsilon;
delete plap_power;
}
///Set the integrator.
/// 0 - hand coded, 1 - AD based (compute only Heassian by AD),
/// 2 - AD based (compute residual and Hessian by AD)
void SetIntegrator(int intr)
{
integ=intr;
}
//set relative tolerance for the Newton solver
void SetNRRTol(double rtol)
{
newton_rtol=rtol;
}
//set absolute tolerance for the Newton solver
void SetNRATol(double atol)
{
newton_atol=atol;
}
//set max iterations for the NR solver
void SetMaxNRIter(int miter)
{
newton_iter=miter;
}
void SetLSRTol(double rtol)
{
linear_rtol=rtol;
}
void SetLSATol(double atol)
{
linear_atol=atol;
}
//set max iterations for the linear solver
void SetMaxLSIter(int miter)
{
linear_iter=miter;
}
//set the print level
void SetPrintLevel(int plev)
{
print_level=plev;
}
///The state vector is used as initial condition for the NR solver.
/// On return the statev holds the solution to the problem.
void Solve(Vector& statev)
{
if (nf==nullptr)
{
AllocSolvers();
}
Vector b; //RHS is zero
ns->Mult(b, statev);
}
///Compute the energy
double GetEnergy(Vector& statev)
{
if (nf==nullptr)
{
//allocate the solvers
AllocSolvers();
}
return nf->GetEnergy(statev);
}
private:
void AllocSolvers()
{
if (nf!=nullptr) { delete nf;}
if (ns!=nullptr) { delete ns;}
if (gmres!=nullptr) { delete gmres;}
if (prec!=nullptr) { delete prec;}
// Define the essential boundary attributes
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
nf = new ParNonlinearForm(fespace);
if (integ==0)
{
nf->AddDomainIntegrator(new pLaplace(*plap_power,*plap_epsilon,*plap_input));
}
else if (integ==1)
{
nf->AddDomainIntegrator(new pLaplaceAD<mfem::QVectorFuncAutoDiff<MyVFunctor,4,4,3>>(*plap_power,*plap_epsilon,*plap_input));
}
else if (integ==2)
{
nf->AddDomainIntegrator(new pLaplaceAD<mfem::QFunctionAutoDiff<MyQFunctor,4,3>>(*plap_power,*plap_epsilon,*plap_input));
}
else{
nf->AddDomainIntegrator(new pLaplaceSL<56>(*plap_power,*plap_epsilon,*plap_input));
}
nf->SetEssentialBC(ess_bdr);
prec = new HypreBoomerAMG();
prec->SetPrintLevel(print_level);
gmres = new GMRESSolver(lcomm);
gmres->SetAbsTol(linear_atol);
gmres->SetRelTol(linear_rtol);
gmres->SetMaxIter(linear_iter);
gmres->SetPrintLevel(print_level);
gmres->SetPreconditioner(*prec);
ns = new NewtonSolver(lcomm);
ns->iterative_mode = true;
ns->SetSolver(*gmres);
ns->SetOperator(*nf);
ns->SetPrintLevel(print_level);
ns->SetRelTol(newton_rtol);
ns->SetAbsTol(newton_atol);
ns->SetMaxIter(newton_iter);
}
double newton_rtol;
double newton_atol;
int newton_iter;
double linear_rtol;
double linear_atol;
int linear_iter;
int print_level;
//power of the p-laplacian
Coefficient* plap_power;
//regularization parammeter
Coefficient* plap_epsilon;
//load(input) paramater
Coefficient* plap_input;
bool input_ownership;
MPI_Comm lcomm;
ParMesh *mesh;
ParFiniteElementSpace *fespace;
ParNonlinearForm *nf;
HypreBoomerAMG *prec;
GMRESSolver *gmres;
NewtonSolver *ns;
int integ;
};
int main(int argc, char *argv[])
{
// 1. Initialize MPI
int num_procs, myrank;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
// Define Caliper ConfigManager
#ifdef MFEM_USE_CALIPER
cali::ConfigManager mgr;
#endif
// Caliper instrumentation
MFEM_PERF_FUNCTION;
// 2. Parse command-line options
const char *mesh_file = "../../data/beam-tet.mesh";
int ser_ref_levels = 3;
int par_ref_levels = 1;
int order = 1;
bool visualization = true;
double newton_rel_tol = 1e-4;
double newton_abs_tol = 1e-6;
int newton_iter = 10;
int print_level = 0;
double pp = 2.0;
int integrator = 2; //use AD
const char* cali_config = "runtime-report";
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(&visualization,
"-vis",
"--visualization",
"-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&newton_rel_tol,
"-rel",
"--relative-tolerance",
"Relative tolerance for the Newton solve.");
args.AddOption(&newton_abs_tol,
"-abs",
"--absolute-tolerance",
"Absolute tolerance for the Newton solve.");
args.AddOption(&newton_iter,
"-it",
"--newton-iterations",
"Maximum iterations for the Newton solve.");
args.AddOption(&pp,
"-pp",
"--power-parameter",
"Power parameter (>=2.0) for the p-Laplacian.");
args.AddOption((&print_level), "-prt", "--print-level", "Print level.");
args.AddOption(&integrator,
"-int",
"--integrator",
"Integrator 0: standard; 1: AD for Hessian; 2: AD for residual and Hessian");
args.AddOption(&cali_config, "-p", "--caliper",
"Caliper configuration string.");
args.Parse();
if (!args.Good())
{
if (myrank == 0)
{
args.PrintUsage(std::cout);
}
MPI_Finalize();
return 1;
}
if (myrank == 0)
{
args.PrintOptions(std::cout);
}
StopWatch *timer = new StopWatch();
// Caliper configuration
#ifdef MFEM_USE_CALIPER
mgr.add(cali_config);
mgr.start();
#endif
// 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 load for the p-Laplacian
ConstantCoefficient load(1.00);
// 7. Define the finite element spaces for the solution
H1_FECollection fec(order, dim);
ParFiniteElementSpace fespace(pmesh, &fec, 1, Ordering::byVDIM);
HYPRE_Int glob_size = fespace.GlobalTrueVSize();
if (myrank == 0)
{
std::cout << "Number of finite element unknowns: " << glob_size
<< std::endl;
}
// 8. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
ParGridFunction x(&fespace);
x = 0.0;
HypreParVector *sv = x.GetTrueDofs();
// 9. Define ParaView DataCollection
ParaViewDataCollection *dacol = new ParaViewDataCollection("Example71",
pmesh);
dacol->SetLevelsOfDetail(order);
dacol->RegisterField("sol", &x);
// 10. Define the NR solver
ParNLSolverPLaplacian* nr;
// 11. Start with linear diffusion - solvable for any initial guess
nr=new ParNLSolverPLaplacian(MPI_COMM_WORLD,*pmesh, fespace, 2.0, &load);
nr->SetIntegrator(integrator);
nr->SetMaxNRIter(newton_iter);
nr->SetNRATol(newton_abs_tol);
nr->SetNRRTol(newton_rel_tol);
nr->SetPrintLevel(print_level);
timer->Clear();
timer->Start();
nr->Solve(*sv);
timer->Stop();
if (myrank==0)
{
std::cout << "[pp=2] The solution time is: " << timer->RealTime()
<< std::endl;
}
// Compute the energy
double energy = nr->GetEnergy(*sv);
if (myrank==0)
{
std::cout << "[pp=2] The total energy of the system is E=" << energy
<< std::endl;
}
delete nr;
x.SetFromTrueDofs(*sv);
dacol->SetTime(2.0);
dacol->SetCycle(2);
dacol->Save();
// 12. Continue with powers higher than 2
for (int i = 3; i < pp; i++)
{
nr=new ParNLSolverPLaplacian(MPI_COMM_WORLD,*pmesh, fespace, (double)i, &load);
nr->SetIntegrator(integrator);
nr->SetMaxNRIter(newton_iter);
nr->SetNRATol(newton_abs_tol);
nr->SetNRRTol(newton_rel_tol);
nr->SetPrintLevel(print_level);
timer->Clear();
timer->Start();
nr->Solve(*sv);
timer->Stop();
if (myrank==0)
{
std::cout << "[pp="<<i<<"] The solution time is: " << timer->RealTime()
<< std::endl;
}
// Compute the energy
double energy = nr->GetEnergy(*sv);
if (myrank==0)
{
std::cout << "[pp="<<i<<"] The total energy of the system is E=" << energy
<< std::endl;
}
delete nr;
x.SetFromTrueDofs(*sv);
dacol->SetTime((double)i);
dacol->SetCycle(i);
dacol->Save();
}
// 13. Continue with the final power
if (std::abs(pp - 2.0) > std::numeric_limits<double>::epsilon())
{
nr=new ParNLSolverPLaplacian(MPI_COMM_WORLD,*pmesh, fespace, pp, &load);
nr->SetIntegrator(integrator);
nr->SetMaxNRIter(newton_iter);
nr->SetNRATol(newton_abs_tol);
nr->SetNRRTol(newton_rel_tol);
nr->SetPrintLevel(print_level);
timer->Clear();
timer->Start();
nr->Solve(*sv);
timer->Stop();
if (myrank==0)
{
std::cout << "[pp="<<pp<<"] The solution time is: " << timer->RealTime()
<< std::endl;
}
// Compute the energy
double energy = nr->GetEnergy(*sv);
if (myrank==0)
{
std::cout << "[pp="<<pp<<"] The total energy of the system is E=" << energy
<< std::endl;
}
delete nr;
x.SetFromTrueDofs(*sv);
dacol->SetTime(pp);
if (pp < 2.0)
{
dacol->SetCycle(std::floor(pp));
}
else
{
dacol->SetCycle(std::ceil(pp));
}
dacol->Save();
}
// 14. Free the used memory
delete dacol;
delete sv;
delete pmesh;
delete timer;
// Flush output before MPI_finalize
#ifdef MFEM_USE_CALIPER
mgr.flush();
#endif
MPI_Finalize();
return 0;
}
+454
View File
@@ -0,0 +1,454 @@
// MFEM Example 71 - Serial Version
//
// Compile with: make ex71
//
// Sample runs:
// ex71 -m ../data/beam-quad.mesh -pp 3.5
// ex71 -m ../data/beam-tri.mesh -pp 4.6
// ex71 -m ../data/beam-hex.mesh
// ex71 -m ../data/beam-tet.mesh
// ex71 -m ../data/beam-wedge.mesh
//
// Description: This examples solves a quasi-static nonlinear
// p-Laplacian problem with zero Dirichlet boundary
// conditions applied on all defined boundaries
//
// The example demonstrates the use of nonlinear operators combined
// with automatic differentiation (AD). The definitions of the
// integrators are written in the ex71.hpp. Selecting integrator=0
// will use the manually implemented integrator. Selecting
// integrator=1,2 will utilize one of the AD integrators.
//
// The AD integrators are implemented in ex71.hpp (pLaplaceAD).
// The integrand qint is a function which is evaluated at every
// integration point. For implementations utilizing ADQFunctionTJ,
// the user has to implement the function and the residual
// evaluation. The Jacobian of the residual is evaluated using AD
//
// For implementations utilizing ADQFunctionTH, the user has to
// implement only the function evaluation (as a template) and the
// first derivative (the residual) and the second derivatives (the
// Hessian) are evaluated using AD.
//
// We recommend viewing examples 1 and 19, before viewing this
// example.
#include "example.hpp"
using namespace mfem;
///Non-linear solver for the p-Laplacian problem.
class NLSolverPLaplacian
{
public:
///Constructor Input: imesh - FE mesh, finite element space,
/// power for the p-Laplacian, external load (source, input),
/// regularization parameter
NLSolverPLaplacian(Mesh& imesh, FiniteElementSpace& ifespace,
double powerp=2,
Coefficient* load=nullptr,
double regularizationp=1e-7)
{
//default parameters for
//the Newton solver
newton_rtol = 1e-4;
newton_atol = 1e-8;
newton_iter = 10;
//linear solver
linear_rtol = 1e-7;
linear_atol = 1e-15;
linear_iter = 500;
print_level = 0;
//set the mesh
mesh=&imesh;
//set the fespace
fespace=&ifespace;
//set the parameters
plap_epsilon=new ConstantCoefficient(regularizationp);
plap_power=new ConstantCoefficient(powerp);
if (load==nullptr)
{
plap_input=new ConstantCoefficient(1.0);
input_ownership=true;
}
else
{
plap_input=load;
input_ownership=false;
}
//set the nonlinear form
nf=nullptr;
lsolver=nullptr;
prec=nullptr;
ns=nullptr;
//set the default integrator
integ=0; //hand coded
}
~NLSolverPLaplacian()
{
if (nf!=nullptr) { delete nf;}
if (ns!=nullptr) { delete ns;}
if (prec!=nullptr) { delete prec;}
if (lsolver!=nullptr) { delete lsolver;}
if (input_ownership) { delete plap_input;}
delete plap_epsilon;
delete plap_power;
}
///Set the integrator.
/// 0 - hand coded, 1 - AD based (compute only Heassian by AD),
/// 2 - AD based (compute residual and Hessian by AD)
void SetIntegrator(int intr)
{
integ=intr;
}
//set relative tolerance for the Newton solver
void SetNRRTol(double rtol)
{
newton_rtol=rtol;
}
//set absolute tolerance for the Newton solver
void SetNRATol(double atol)
{
newton_atol=atol;
}
//set max iterations for the NR solver
void SetMaxNRIter(int miter)
{
newton_iter=miter;
}
void SetLSRTol(double rtol)
{
linear_rtol=rtol;
}
void SetLSATol(double atol)
{
linear_atol=atol;
}
//set max iterations for the linear solver
void SetMaxLSIter(int miter)
{
linear_iter=miter;
}
//set the print level
void SetPrintLevel(int plev)
{
print_level=plev;
}
///The state vector is used as initial condition for the NR solver.
/// On return the statev holds the solution to the problem.
void Solve(Vector& statev)
{
if (nf==nullptr)
{
AllocSolvers();
}
Vector b; //RHS is zero
ns->Mult(b, statev);
}
///Compute the energy
double GetEnergy(Vector& statev)
{
if (nf==nullptr)
{
//allocate the solvers
AllocSolvers();
}
return nf->GetEnergy(statev);
}
private:
void AllocSolvers()
{
if (nf!=nullptr) { delete nf;}
if (ns!=nullptr) {delete ns;}
if (prec!=nullptr) {delete prec;}
if (lsolver!=nullptr) { delete lsolver;}
// Define the essential boundary attributes
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
nf = new NonlinearForm(fespace);
if (integ==0)
{
nf->AddDomainIntegrator(new pLaplace(*plap_power,*plap_epsilon,*plap_input));
}
else if (integ==1)
{
nf->AddDomainIntegrator(new pLaplaceAD<mfem::QVectorFuncAutoDiff<MyVFunctor,4,4,3>>(*plap_power,*plap_epsilon,*plap_input));
}
else
{
nf->AddDomainIntegrator(new pLaplaceAD<mfem::QFunctionAutoDiff<MyQFunctor,4,3>>(*plap_power,*plap_epsilon,*plap_input));
}
nf->SetEssentialBC(ess_bdr);
#ifdef MFEM_USE_SUITESPARSE
prec = new UMFPackSolver();
#else
prec = new GSSmoother();
#endif
//allocate the linear solver
lsolver=new CGSolver();
lsolver->SetRelTol(linear_rtol);
lsolver->SetAbsTol(linear_atol);
lsolver->SetMaxIter(linear_iter);
lsolver->SetPrintLevel(print_level);
lsolver->SetPreconditioner(*prec);
//allocate the NR solver
ns = new NewtonSolver();
ns->iterative_mode = true;
ns->SetSolver(*lsolver);
ns->SetOperator(*nf);
ns->SetPrintLevel(print_level);
ns->SetRelTol(newton_rtol);
ns->SetAbsTol(newton_atol);
ns->SetMaxIter(newton_iter);
}
double newton_rtol;
double newton_atol;
int newton_iter;
double linear_rtol;
double linear_atol;
int linear_iter;
int print_level;
//reference to the mesh
Mesh* mesh;
//reference to the fespace
FiniteElementSpace *fespace;
//nonlinear form for the p-laplacian
NonlinearForm *nf;
CGSolver *lsolver; //linear solver
Solver *prec; //preconditioner for the linear solver
NewtonSolver *ns; //NR solver
int integ;
//power of the p-laplacian
Coefficient* plap_power;
//regularization parammeter
Coefficient* plap_epsilon;
//load(input) paramater
Coefficient* plap_input;
bool input_ownership;
};
int main(int argc, char *argv[])
{
// 1. Parse command-line options
const char *mesh_file = "../../data/beam-tet.mesh";
int ser_ref_levels = 3;
int order = 1;
bool visualization = true;
double newton_rel_tol = 1e-4;
double newton_abs_tol = 1e-6;
int newton_iter = 10;
int print_level = 0;
double pp = 2.0; // p-Lapalacian power
int integrator = 2; // 2 - use AD for Residual and Hessian
// 1 - use AD for Hessian only
// 0 - do not use AD (hand coded)
StopWatch *timer = new StopWatch();
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(&order,
"-o",
"--order",
"Order (degree) of the finite elements.");
args.AddOption(&visualization,
"-vis",
"--visualization",
"-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&newton_rel_tol,
"-rel",
"--relative-tolerance",
"Relative tolerance for the Newton solve.");
args.AddOption(&newton_abs_tol,
"-abs",
"--absolute-tolerance",
"Absolute tolerance for the Newton solve.");
args.AddOption(&newton_iter,
"-it",
"--newton-iterations",
"Maximum iterations for the Newton solve.");
args.AddOption(&pp,
"-pp",
"--power-parameter",
"Power parameter (>=2.0) for the p-Laplacian.");
args.AddOption((&print_level), "-prt", "--print-level", "Print level.");
args.AddOption(&integrator,
"-int",
"--integrator",
"Integrator 0: standard; 1: AD for Hessian; 2: AD for residual and Hessian");
args.Parse();
if (!args.Good())
{
args.PrintUsage(std::cout);
return 1;
}
args.PrintOptions(std::cout);
// 2. Read the (serial) mesh from the given mesh file.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 3. 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();
}
// 4. Define the load parameter for the p-Laplacian
ConstantCoefficient load(1.00);
// 5. Define the finite element spaces for the solution
H1_FECollection fec(order, dim);
FiniteElementSpace fespace(mesh, &fec, 1, Ordering::byVDIM);
int glob_size = fespace.GetTrueVSize();
std::cout << "Number of finite element unknowns: " << glob_size << std::endl;
// 6. Define the solution grid function
GridFunction x(&fespace);
x = 0.0;
// 7. Define the solution true vector
Vector sv(fespace.GetTrueVSize());
sv = 0.0;
// 8. Define ParaView DataCollection
ParaViewDataCollection *dacol = new ParaViewDataCollection("Example71", mesh);
dacol->SetLevelsOfDetail(order);
dacol->RegisterField("sol", &x);
// 9. Define the NR solver
NLSolverPLaplacian* nr;
// 10. Start with linear diffusion - solvable for any initial guess
nr=new NLSolverPLaplacian(*mesh, fespace, 2.0, &load);
nr->SetIntegrator(integrator);
nr->SetMaxNRIter(newton_iter);
nr->SetNRATol(newton_abs_tol);
nr->SetNRRTol(newton_rel_tol);
timer->Clear();
timer->Start();
nr->Solve(sv);
timer->Stop();
std::cout << "[pp=2] The solution time is: " << timer->RealTime()
<< std::endl;
// Compute the energy
double energy = nr->GetEnergy(sv);
std::cout << "[pp=2] The total energy of the system is E=" << energy
<< std::endl;
delete nr;
x.SetFromTrueDofs(sv);
dacol->SetTime(2.0);
dacol->SetCycle(2);
dacol->Save();
// 11. Continue with powers higher than 2
for (int i = 3; i < pp; i++)
{
nr=new NLSolverPLaplacian(*mesh, fespace, (double)i, &load);
nr->SetIntegrator(integrator);
nr->SetMaxNRIter(newton_iter);
nr->SetNRATol(newton_abs_tol);
nr->SetNRRTol(newton_rel_tol);
timer->Clear();
timer->Start();
nr->Solve(sv);
timer->Stop();
std::cout << "[pp=" << i
<< "] The solution time is: " << timer->RealTime() << std::endl;
energy = nr->GetEnergy(sv);
std::cout << "[pp="<< i<<"] The total energy of the system is E=" << energy
<< std::endl;
delete nr;
x.SetFromTrueDofs(sv);
dacol->SetTime(i);
dacol->SetCycle(i);
dacol->Save();
}
// 12. Continue with the final power
if (std::abs(pp - 2.0) > std::numeric_limits<double>::epsilon())
{
nr=new NLSolverPLaplacian(*mesh, fespace, pp, &load);
nr->SetIntegrator(integrator);
nr->SetMaxNRIter(newton_iter);
nr->SetNRATol(newton_abs_tol);
nr->SetNRRTol(newton_rel_tol);
timer->Clear();
timer->Start();
nr->Solve(sv);
timer->Stop();
std::cout << "[pp=" << pp
<< "] The solution time is: " << timer->RealTime() << std::endl;
energy = nr->GetEnergy(sv);
std::cout << "[pp="<<pp<<"] The total energy of the system is E=" << energy
<< std::endl;
delete nr;
x.SetFromTrueDofs(sv);
dacol->SetTime(pp);
if (pp < 2.0)
{
dacol->SetCycle(std::floor(pp));
}
else
{
dacol->SetCycle(std::ceil(pp));
}
dacol->Save();
}
// 13. Free the memory
delete dacol;
delete mesh;
delete timer;
return 0;
}
+141
View File
@@ -0,0 +1,141 @@
#include "admfem.hpp"
#include "mfem.hpp"
template<typename TDataType, typename TParamVector, typename TStateVector
, int state_size, int param_size>
class DiffusionFunctional
{
public:
TDataType operator() (TParamVector& vparam, TStateVector& uu)
{
MFEM_ASSERT(state_size==4,"ExampleFunctor state_size should be equal to 4!");
MFEM_ASSERT(param_size==2,"ExampleFunctor param_size should be equal to 2!");
auto kapa = vparam[0]; //diffusion coefficient
auto load = vparam[1]; //volumetric influx
TDataType rez = kapa*(uu[0]*uu[0]+uu[1]*uu[1]+uu[2]*uu[2])/2.0 - load*uu[3];
return rez;
}
};
template<typename TDataType, typename TParamVector, typename TStateVector,
int residual_size, int state_size, int param_size>
class DiffusionResidual
{
public:
void operator ()(TParamVector& vparam, TStateVector& uu, TStateVector& rr)
{
MFEM_ASSERT(residual_size==4,"DiffusionResidual residual_size should be equal to 4!")
MFEM_ASSERT(state_size==4,"ExampleFunctor state_size should be equal to 4!");
MFEM_ASSERT(param_size==2,"ExampleFunctor param_size should be equal to 2!");
auto kapa = vparam[0]; //diffusion coefficient
auto load = vparam[1]; //volumetric influx
rr[0] = kapa * uu[0];
rr[1] = kapa * uu[1];
rr[2] = kapa * uu[2];
rr[3] = -load;
}
};
int main(int argc, char *argv[])
{
#ifdef MFEM_USE_ADFORWARD
std::cout<<"MFEM_USE_ADFORWARD == true"<<std::endl;
#else
std::cout<<"MFEM_USE_ADFORWARD == false"<<std::endl;
#endif
#ifdef MFEM_USE_CALIPER
cali::ConfigManager mgr;
#endif
// Caliper instrumentation
MFEM_PERF_FUNCTION;
const char* cali_config = "runtime-report";
#ifdef MFEM_USE_CALIPER
mgr.add(cali_config);
mgr.start();
#endif
mfem::Vector param(2);
param[0]=3.0; //diffusion coefficient
param[1]=2.0; //volumetric influx
mfem::Vector state(4);
state[0]=1.0; // grad_x
state[1]=2.0; // grad_y
state[2]=3.0; // grad_z
state[3]=4.0; // state value
mfem::QFunctionAutoDiff<DiffusionFunctional,4,2> adf;
mfem::QVectorFuncAutoDiff<DiffusionResidual,4,4,2> rdf;
mfem::Vector rr0(4);
mfem::DenseMatrix hh0(4,4);
mfem::Vector rr1(4);
mfem::DenseMatrix hh1(4,4);
MFEM_PERF_BEGIN("QGrad");
adf.QGrad(param,state,rr0);
MFEM_PERF_END("QGrad");
MFEM_PERF_BEGIN("QHessian");
adf.QHessian(param, state, hh0);
MFEM_PERF_END("QHessian");
// dump out the results
std::cout<<"FunctionAutoDiff"<<std::endl;
std::cout<< adf.QEval(param,state)<<std::endl;
rr0.Print(std::cout);
hh0.Print(std::cout);
MFEM_PERF_BEGIN("QJacobian");
rdf.QJacobian(param, state, hh1);
MFEM_PERF_END("QJacobian");
std::cout<<"ResidualAutoDiff"<<std::endl;
hh1.Print(std::cout);
//using lambda expression
auto func = [](mfem::Vector& vparam, mfem::ad::ADVectorType& uu, mfem::ad::ADVectorType& vres) {
//auto func = [](auto& vparam, auto& uu, auto& vres) { //c++14
auto kappa = vparam[0]; //diffusion coefficient
auto load = vparam[1]; //volumetric influx
vres[0] = kappa * uu[0];
vres[1] = kappa * uu[1];
vres[2] = kappa * uu[2];
vres[3] = -load;
};
mfem::VectorFuncAutoDiff<4,4,2> fdr(func);
MFEM_PERF_BEGIN("QJacobianV");
fdr.QJacobian(param,state,hh1); //computes the gradient of func and stores the result in hh1
MFEM_PERF_END("QJacobianV");
std::cout<<"LambdaAutoDiff"<<std::endl;
hh1.Print(std::cout);
double kappa = param[0];
double load = param[1];
//using lambda expression
auto func01 = [&kappa,&load](mfem::Vector& vparam, mfem::ad::ADVectorType& uu, mfem::ad::ADVectorType& vres) {
//auto func = [](auto& vparam, auto& uu, auto& vres) { //c++14
vres[0] = kappa * uu[0];
vres[1] = kappa * uu[1];
vres[2] = kappa * uu[2];
vres[3] = -load;
};
mfem::VectorFuncAutoDiff<4,4,2> fdr01(func01);
MFEM_PERF_BEGIN("QJacobian1");
fdr01.QJacobian(param,state,hh1);
MFEM_PERF_END("QJacobian1");
std::cout<<"LambdaAutoDiff 01"<<std::endl;
hh1.Print(std::cout);
#ifdef MFEM_USE_CALIPER
mgr.flush();
#endif
}
+208
View File
@@ -0,0 +1,208 @@
#include "stokes.hpp"
#include "petsc.h"
namespace mfem {
void StokesSolver::FSolve()
{
if(pmat!=nullptr)
{
delete psol;
delete prec;
delete pmat;
ess_tdofv.DeleteAll();
}
sol=0.0;
// Set the BC
ess_tdofv.DeleteAll();
Array<int> ess_tdofx;
Array<int> ess_tdofy;
Array<int> ess_tdofz;
Array<int> ess_tdofp;
int dim=pmesh->Dimension();
{
for(auto it=bccx.begin();it!=bccx.end();it++)
{
mfem::Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr=0;
ess_bdr[it->first -1]=1;
mfem::Array<int> ess_tdof_list;
vfes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list,0);
ess_tdofx.Append(ess_tdof_list);
mfem::VectorArrayCoefficient pcoeff(dim);
pcoeff.Set(0, it->second, false);
fvelocity.ProjectBdrCoefficient(pcoeff, ess_bdr);
}
//copy tdofsx from velocity grid function
{
fvelocity.GetTrueDofs(rhs.GetBlock(0)); // use the rhs vector as a tmp vector
for(int ii=0;ii<ess_tdofx.Size();ii++)
{
sol.GetBlock(0)[ess_tdofx[ii]]=rhs.GetBlock(0)[ess_tdofx[ii]];
}
}
ess_tdofv.Append(ess_tdofx);
for(auto it=bccy.begin();it!=bccy.end();it++)
{
mfem::Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr=0;
ess_bdr[it->first -1]=1;
mfem::Array<int> ess_tdof_list;
vfes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list,1);
ess_tdofy.Append(ess_tdof_list);
mfem::VectorArrayCoefficient pcoeff(dim);
pcoeff.Set(1, it->second, false);
fvelocity.ProjectBdrCoefficient(pcoeff, ess_bdr);
}
//copy tdofsy from velocity grid function
{
fvelocity.GetTrueDofs(rhs.GetBlock(0)); // use the rhs vector as a tmp vector
for(int ii=0;ii<ess_tdofy.Size();ii++)
{
sol.GetBlock(0)[ess_tdofy[ii]]=rhs.GetBlock(0)[ess_tdofy[ii]];
}
}
ess_tdofv.Append(ess_tdofy);
for(auto it=bccz.begin();it!=bccz.end();it++)
{
mfem::Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr=0;
ess_bdr[it->first -1]=1;
mfem::Array<int> ess_tdof_list;
vfes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list,2);
ess_tdofz.Append(ess_tdof_list);
mfem::VectorArrayCoefficient pcoeff(dim);
pcoeff.Set(2, it->second, false);
fvelocity.ProjectBdrCoefficient(pcoeff, ess_bdr);
}
//copy tdofsz from velocity grid function
{
fvelocity.GetTrueDofs(rhs.GetBlock(0)); // use the rhs vector as a tmp vector
for(int ii=0;ii<ess_tdofz.Size();ii++)
{
sol.GetBlock(0)[ess_tdofz[ii]]=rhs.GetBlock(0)[ess_tdofz[ii]];
}
}
ess_tdofv.Append(ess_tdofz);
}
if(nfin==nullptr)
{
nfin=new StokesIntegratorTH(viscosity,bpenal,load);
nf->AddDomainIntegrator(nfin);
nf->SetGradientType(mfem::Operator::Type::Hypre_ParCSR);
}
// set the RHS
nf->Mult(sol,rhs);
rhs.Neg();
mfem::BlockOperator& A=nf->GetGradient(sol);
mfem::HypreParMatrix* A00=static_cast<mfem::HypreParMatrix*>(&(A.GetBlock(0,0)));
mfem::HypreParMatrix* A01=static_cast<mfem::HypreParMatrix*>(&(A.GetBlock(0,1)));
mfem::HypreParMatrix* A10=static_cast<mfem::HypreParMatrix*>(&(A.GetBlock(1,0)));
mfem::HypreParMatrix* A11=static_cast<mfem::HypreParMatrix*>(&(A.GetBlock(1,1)));
mfem::HypreParMatrix* A00elim=A00->EliminateRowsCols(ess_tdofv);
mfem::HypreParMatrix* A11elim=A11->EliminateRowsCols(ess_tdofp);
mfem::HypreParMatrix* A01elim=A01->EliminateCols(ess_tdofp); A01->EliminateRows(ess_tdofv);
mfem::HypreParMatrix* A10elim=A10->EliminateCols(ess_tdofv); A10->EliminateRows(ess_tdofp);
//copy BC to RHS
for(int ii=0;ii<ess_tdofv.Size();ii++)
{
rhs.GetBlock(0)[ess_tdofv[ii]]=sol.GetBlock(0)[ess_tdofv[ii]];
}
delete psol;
delete prec;
delete pmat;
pmat= new mfem::PetscParMatrix(pmesh->GetComm(),&A, mfem::Operator::PETSC_MATAIJ);
//set the local block size of the matrix
Mat sub;
MatNestGetSubMat(pmat->operator petsc::Mat(),0,0,&sub);
MatSetBlockSize(sub,dim);
// construct the preconditioner
prec = new mfem::PetscFieldSplitSolver(pmesh->GetComm(),*pmat,"prec_");
// construct the linear solver
psol = new mfem::PetscLinearSolver(pmesh->GetComm());
/*
{
std::fstream out("full.mat",std::ios::out);
pmat->PrintMatlab(out);
out.close();
}
*/
psol->SetOperator(*pmat);
psol->SetPreconditioner(*prec);
psol->SetAbsTol(abs_tol);
psol->SetRelTol(rel_tol);
psol->SetMaxIter(max_iter);
psol->SetPrintLevel(print_level);
psol->Mult(rhs, sol);
//psol->GetConverged();
delete A11elim;
delete A01elim;
delete A10elim;
delete A00elim;
}
void StokesSolver::ASolve(BlockVector& arhs)
{
if(pmat==nullptr)
{
MFEM_ABORT("StokesSolve::Adjoint - The forward solver should be called first!!!")
}
//set BC
rhs=arhs;
for(int ii=0;ii<ess_tdofv.Size();ii++)
{
adj.GetBlock(0)[ess_tdofv[ii]]=0.0;
rhs.GetBlock(0)[ess_tdofv[ii]]=0.0;
}
psol->Mult(rhs, adj);
}
void StokesSolver::GradD(Vector &grad)
{
if(dfes==nullptr)
{
MFEM_ABORT("StokesSolve::GradD - The design space in not set!!!")
}
//set vector size
grad.SetSize(dfes->GetTrueVSize());
grad=0.0;
fvelocity.SetFromTrueDofs(sol.GetBlock(0));
avelocity.SetFromTrueDofs(adj.GetBlock(0));
mfem::ParLinearForm lf(dfes);
lf.AddDomainIntegrator(new StokesGradIntergrator(fvelocity, avelocity
,*(ltopopt.fcoef),
vfes->GetOrder(0)));
lf.ParallelAssemble(grad);
}
}
+813
View File
@@ -0,0 +1,813 @@
#ifndef STOKES_HPP
#define STOKES_HPP
#include "mfem.hpp"
namespace mfem {
namespace PointwiseTrans
{
/* Standrd "Heaviside" projection in topology optimization with threhold eta
* and steepness of the projection beta.
* */
inline
double HProject(double rho, double eta, double beta)
{
// tanh projection - Wang&Lazarov&Sigmund2011
double a=std::tanh(eta*beta);
double b=std::tanh(beta*(1.0-eta));
double c=std::tanh(beta*(rho-eta));
double rez=(a+c)/(a+b);
return rez;
}
/// Gradient of the "Heaviside" projection with respect to rho.
inline
double HGrad(double rho, double eta, double beta)
{
double c=std::tanh(beta*(rho-eta));
double a=std::tanh(eta*beta);
double b=std::tanh(beta*(1.0-eta));
double rez=beta*(1.0-c*c)/(a+b);
return rez;
}
/// Second derivative of the "Heaviside" projection with respect to rho.
inline
double HHess(double rho,double eta, double beta)
{
double c=std::tanh(beta*(rho-eta));
double a=std::tanh(eta*beta);
double b=std::tanh(beta*(1.0-eta));
double rez=-2.0*beta*beta*c*(1.0-c*c)/(a+b);
return rez;
}
inline
double FluidInterpolation(double rho,double q)
{
return q*(1.0-rho)/(q+rho);
}
inline
double GradFluidInterpolation(double rho, double q)
{
double tt=q+rho;
return -q/tt-q*(1.0-rho)/(tt*tt);
}
}
// Taylor-Hood finite elements
class StokesIntegratorTH:public mfem::BlockNonlinearFormIntegrator
{
public:
StokesIntegratorTH()
{
mu=nullptr;
bc=nullptr;
ff=nullptr;
ss.SetSize(13);
rr.SetSize(13);
mm.SetSize(13);
}
StokesIntegratorTH(mfem::Coefficient* mu_, mfem::Coefficient* bc_, mfem::VectorCoefficient* ff_)
{
mu=mu_;
bc=bc_;
ff=ff_;
ss.SetSize(13);
rr.SetSize(13);
mm.SetSize(13);
}
virtual
double GetElementEnergy(const Array<const FiniteElement *> &el,
ElementTransformation &Tr,
const Array<const Vector *> &elfun)
{
return 0.0;
}
virtual
void AssembleElementVector(const Array<const FiniteElement *> &el,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array<Vector *> &elvec)
{
int dof_u = el[0]->GetDof();
int dof_p = el[1]->GetDof();
int dim = el[0]->GetDim();
elvec[0]->SetSize(dim*dof_u);
elvec[1]->SetSize(dof_p);
int spaceDim = Tr.GetDimension();
if (dim != spaceDim)
{
mfem::mfem_error("StokesIntegrator::AssembleElementVector"
" is not defined on manifold meshes");
}
// gradients
bsu.SetSize(dof_u,4);
bsp.SetSize(dof_p,1);
Vector uu(elfun[0]->GetData()+0*dof_u, dof_u);
Vector vv(elfun[0]->GetData()+1*dof_u, dof_u);
Vector ru(elvec[0]->GetData()+0*dof_u, dof_u); ru=0.0;
Vector rv(elvec[0]->GetData()+1*dof_u, dof_u); rv=0.0;
Vector ww;
Vector rw;
if(dim==2){
ww.SetSize(dof_u); ww=0.0;
}
else{
ww.SetDataAndSize(elfun[0]->GetData()+2*dof_u, dof_u);
rw.SetDataAndSize(elvec[0]->GetData()+2*dof_u, dof_u); rw=0.0;
}
Vector pp(elfun[1]->GetData(), dof_p);
Vector rp(elvec[1]->GetData(), dof_p); rp=0.0;
// temp storages for vectors and matrices
Vector sh;
DenseMatrix dh;
const IntegrationRule *ir = nullptr;
int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0]);
ir=&IntRules.Get(Tr.GetGeometryType(),order);
double bpenal; // Brinkmann penalization
double mmu;
Vector fv(3); fv=0.0;
double w;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetIntPoint(&ip);
w=Tr.Weight();
w = ip.weight * w;
sh.SetDataAndSize(bsu.GetData(),dof_u);
el[0]->CalcPhysShape(Tr,sh);
sh.SetDataAndSize(bsp.GetData(),dof_p);
el[1]->CalcPhysShape(Tr,sh);
dh.UseExternalData(bsu.GetData()+dof_u,dof_u,dim);
el[0]->CalcPhysDShape(Tr,dh);
if(dim=2){
sh.SetDataAndSize(bsu.GetData()+3*dof_u,dof_u);
sh=0.0;
}
sh.SetDataAndSize(ss.GetData(),4);
bsu.MultTranspose(uu,sh);
sh.SetDataAndSize(ss.GetData()+4,4);
bsu.MultTranspose(vv,sh);
sh.SetDataAndSize(ss.GetData()+8,4);
if(dim==3){
bsu.MultTranspose(ww,sh);}
else{
sh=0.0;}
sh.SetDataAndSize(ss.GetData()+12,1);
bsp.MultTranspose(pp,sh);
mmu=1.0;
if(mu!=nullptr){
mmu=mu->Eval(Tr,ip);}
bpenal=0.0;
if(bc!=nullptr){
bpenal=bc->Eval(Tr,ip);}
fv=0.0;
if(ff!=nullptr){
ff->Eval(fv,Tr,ip);}
EvalQres(mmu,bpenal,fv[0],fv[1],fv[2],ss.GetData(),rr.GetData());
sh.SetDataAndSize(rr.GetData(),4);
bsu.AddMult_a(w,sh,ru);
sh.SetDataAndSize(rr.GetData()+4,4);
bsu.AddMult_a(w,sh,rv);
if(dim==3){
sh.SetDataAndSize(rr.GetData()+8,4);
bsu.AddMult_a(w,sh,rw);
}
sh.SetDataAndSize(rr.GetData()+12,1);
bsp.AddMult_a(w,sh,rp);
}
}
virtual
void AssembleElementGrad(const Array<const FiniteElement *> &el,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array2D<DenseMatrix *> &elmats)
{
int dof_u = el[0]->GetDof();
int dof_p = el[1]->GetDof();
int dim = el[0]->GetDim();
int spaceDim = Tr.GetDimension();
if (dim != spaceDim)
{
mfem::mfem_error("StokesIntegrator::AssembleElementVector"
" is not defined on manifold meshes");
}
elmats(0,0)->SetSize(dof_u*dim);
elmats(0,1)->SetSize(dof_u*dim,dof_p);
elmats(1,0)->SetSize(dof_p,dim*dof_u);
elmats(1,1)->SetSize(dof_p,dof_p);
(*elmats(0,0))=0.0;
(*elmats(0,1))=0.0;
(*elmats(1,0))=0.0;
(*elmats(1,1))=0.0;
// gradients
DenseMatrix bsu(dof_u,4);
DenseMatrix bsp(dof_p,1);
mm.SetSize(13,13); // state matrix
// temp storages for vectors and matrices
Vector sh;
DenseMatrix mh;
DenseMatrix th;
DenseMatrix rh;
DenseMatrix dh;
const IntegrationRule *ir = nullptr;
int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0]);
ir=&IntRules.Get(Tr.GetGeometryType(),order);
double mmu;
double bpenal; // Brinkmann penalization
double w;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetIntPoint(&ip);
w=Tr.Weight();
// Primal
sh.SetDataAndSize(bsu.GetData(),dof_u);
el[0]->CalcPhysShape(Tr,sh);
sh.SetDataAndSize(bsp.GetData(),dof_p);
el[1]->CalcPhysShape(Tr,sh);
// Gradients
dh.UseExternalData(bsu.GetData()+dof_u,dof_u,dim);
el[0]->CalcPhysDShape(Tr,dh);
if(dim=2){
sh.SetDataAndSize(bsu.GetData()+3*dof_u,dof_u);
sh=0.0;
}
mmu=1.0;
if(mu!=nullptr)
{
mmu=mu->Eval(Tr,ip);
}
bpenal=0.0;
if(bc!=nullptr){
bpenal=bc->Eval(Tr,ip);}
EvalQMat(mmu,bpenal,mm.GetData());
w = ip.weight * w;
th.SetSize(dof_u,4);
rh.SetSize(dof_u);
mh.SetSize(4,4);
for(int ii=0;ii<dim;ii++){
for(int jj=0;jj<dim;jj++){
mh.CopyMN(mm,4,4,ii*4,jj*4);
mh.Transpose();
MultABt(bsu,mh,th);
MultABt(th,bsu,rh);
elmats(0,0)->AddMatrix(w,rh,ii*dof_u,jj*dof_u);
}}
th.SetSize(dof_u,1);
rh.SetSize(dof_u,dof_p);
mh.SetSize(4,1);
for(int jj=0;jj<dim;jj++){
mh.CopyMN(mm,4,1,jj*4,12);
mh.Transpose();
MultABt(bsu,mh,th);
MultABt(th,bsp,rh);
elmats(0,1)->AddMatrix(w,rh,jj*dof_u,0);
}
th.SetSize(dof_p,1);
rh.SetSize(dof_p,dof_p);
mh.SetSize(1,1);
mh.CopyMN(mm,1,1,12,12);
mh.Transpose();
MultABt(bsp,mh,th);
MultABt(th,bsp,rh);
elmats(1,1)->AddMatrix(w,rh,0,0);
}
elmats(1,0)->CopyMNt(*elmats(0,1),0,0);
}
private:
mfem::Coefficient* mu;
mfem::Coefficient* bc;
mfem::VectorCoefficient* ff;
mfem::DenseMatrix bsu;
mfem::DenseMatrix bsp;
DenseMatrix mm;
Vector rr;
Vector ss;
void EvalQres(double mmu, double bpenal,
double fx, double fy, double fz,
double* uu, double* rr)
{
double t7,t9,t16;
t7 = mmu*(uu[2]+uu[5]);
t9 = mmu*(uu[3]+uu[9]);
t16 = mmu*(uu[7]+uu[10]);
rr[0] = bpenal*uu[0]-fx;
rr[1] = 2.0*mmu*uu[1]-uu[12];
rr[2] = t7;
rr[3] = t9;
rr[4] = bpenal*uu[4]-fy;
rr[5] = t7;
rr[6] = 2.0*mmu*uu[6]-uu[12];
rr[7] = t16;
rr[8] = bpenal*uu[8]-fz;
rr[9] = t9;
rr[10] = t16;
rr[11] = 2.0*mmu*uu[11]-uu[12];
rr[12] = -uu[1]-uu[6]-uu[11];
}
void EvalQMat(double mmu, double bpenal, double* kmat)
{
for(int i=0;i<169;i++){
kmat[i]=0.0;
}
double t1 = 2.0*mmu;
kmat[0] = bpenal;
kmat[14] = t1;
kmat[25] = -1.0;
kmat[28] = mmu;
kmat[31] = mmu;
kmat[42] = mmu;
kmat[48] = mmu;
kmat[56] = bpenal;
kmat[67] = mmu;
kmat[70] = mmu;
kmat[84] = t1;
kmat[90] = -1.0;
kmat[98] = mmu;
kmat[101] = mmu;
kmat[112] = bpenal;
kmat[120] = mmu;
kmat[126] = mmu;
kmat[137] = mmu;
kmat[140] = mmu;
kmat[154] = t1;
kmat[155] = -1.0;
kmat[157] = -1.0;
kmat[162] = -1.0;
kmat[167] = -1.0;
}
};
class FluidInterpolationCoefficient: public mfem::Coefficient
{
public:
FluidInterpolationCoefficient()
{
eta=0.5;
beta=8.0;
q=1.0;
lambda=100;
}
FluidInterpolationCoefficient(double eta_, double beta_,
double q_, double lambda_, mfem::GridFunction& gf):gfc(&gf)
{
eta=eta_;
beta=beta_;
q=q_;
lambda=lambda_;
}
virtual
double Eval(ElementTransformation &T, const IntegrationPoint &ip)
{
double rhop=PointwiseTrans::HProject(gfc.Eval(T,ip),eta,beta);
return lambda*PointwiseTrans::FluidInterpolation(rhop,q);
}
virtual
double GradEval(ElementTransformation &T, const IntegrationPoint &ip)
{
double rhoo=gfc.Eval(T,ip);
double rhop=PointwiseTrans::HProject(rhoo,eta,beta);
double g1=lambda*PointwiseTrans::GradFluidInterpolation(rhop,q);
double g2=PointwiseTrans::HGrad(rhoo,eta,beta);
return g1*g2;
}
void SetGridFunction(mfem::GridFunction* gf)
{
gfc.SetGridFunction(gf);
}
void SetGridFunction(mfem::GridFunction& gf)
{
gfc.SetGridFunction(&gf);
}
private:
mfem::GridFunctionCoefficient gfc;
double eta; // threshold level
double beta; // steepness of the projection
double q; // interpolation parameter
double lambda; // penalization
};
class StokesGradIntergrator: public mfem::LinearFormIntegrator
{
public:
StokesGradIntergrator(mfem::GridFunction& velocity,
mfem::GridFunction& adjoint,
mfem::FluidInterpolationCoefficient& bcoef,
int intorder_)
:vel(velocity), adj(adjoint), brm(bcoef)
{
intorder=intorder_; // should be equal to the order of the velocity field
}
virtual
void AssembleRHSElementVect(const mfem::FiniteElement &el, mfem::ElementTransformation &Tr, mfem::Vector &elvect)
{
int dof = el.GetDof();
int dim = el.GetDim();
elvect.SetSize(dof);
elvect=0.0;
const mfem::IntegrationRule *ir = nullptr;
int order= 2 * intorder + Tr.OrderGrad(&el);
ir=&IntRules.Get(Tr.GetGeometryType(),order);
mfem::Vector sh(dof);
mfem::Vector vv(dim);
mfem::Vector aa(dim);
double gbpenal; //gradient of the Brinkmann penalization
double dp;
double w;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetIntPoint(&ip);
w=Tr.Weight();
w = ip.weight * w;
gbpenal=brm.GradEval(Tr,ip);
vel.GetVectorValue(Tr,ip,vv);
adj.GetVectorValue(Tr,ip,aa);
el.CalcPhysShape(Tr,sh);
for(int i=0;i<dof;i++)
{
dp=vv*aa;
elvect[i]=elvect[i]-w*sh[i]*gbpenal*dp;
}
}
}
private:
mfem::GridFunction& vel;
mfem::GridFunction& adj;
mfem::FluidInterpolationCoefficient& brm;
int intorder;
};
class StokesSolver
{
public:
StokesSolver(mfem::ParMesh* pmesh_, int vorder=2)
{
if(vorder<2){vorder=2;}
int porder=vorder-1;
pmesh=pmesh_;
int dim=pmesh->Dimension();
vfec=new H1_FECollection(vorder,dim);
pfec=new H1_FECollection(porder,dim);
vfes=new mfem::ParFiniteElementSpace(pmesh,vfec,dim, Ordering::byVDIM);
pfes=new mfem::ParFiniteElementSpace(pmesh,pfec);
sfes.Append(vfes);
sfes.Append(pfes);
bpenal=nullptr;
load=nullptr;
viscosity=nullptr;
mfem::Array<mfem::ParFiniteElementSpace*> pf;
pf.Append(vfes);
pf.Append(pfes);
nf=new mfem::ParBlockNonlinearForm(pf);
nfin=nullptr;
rhs.Update(nf->GetBlockTrueOffsets()); rhs=0.0;
sol.Update(nf->GetBlockTrueOffsets()); sol=0.0;
adj.Update(nf->GetBlockTrueOffsets()); adj=0.0;
fvelocity.SetSpace(vfes); fvelocity=0.0;
fpressure.SetSpace(pfes); fpressure=0.0;
avelocity.SetSpace(vfes); avelocity=0.0;
pmat=nullptr;
prec=nullptr;
psol=nullptr;
dfes=nullptr;
ltopopt.fcoef=nullptr;
SetDesignParameters();
SetSolver();
}
~StokesSolver()
{
delete ltopopt.fcoef;
delete psol;
delete prec;
delete pmat;
delete nf;
delete vfes;
delete pfes;
delete vfec;
delete pfec;
}
void SetViscosity(mfem::Coefficient& coef_)
{
viscosity=&coef_;
}
// Set the penalization field to coef_.
void SetBrinkmanPenal(mfem::Coefficient& coef_)
{
bpenal=&coef_;
}
void SetVolForces(mfem::VectorCoefficient& load_)
{
load=&load_;
}
void SetSolver(double rtol=1e-8, double atol=1e-12,int miter=1000, int prt_level=1)
{
rel_tol=rtol;
abs_tol=atol;
max_iter=miter;
print_level=prt_level;
}
/// Solves the forward problem.
void FSolve();
/// Solves the adjoint with the provided rhs.
void ASolve(mfem::BlockVector& rhs);
/// Return adj*d(residual(sol))/d(design). The dimension
/// of the vector grad is the save as the dimension of the
/// true design vector.
void GradD(mfem::Vector& grad);
mfem::ParGridFunction& GetVelocity()
{
fvelocity.SetFromTrueDofs(sol.GetBlock(0));
return fvelocity;
}
mfem::ParGridFunction& GetPressure()
{
fpressure.SetFromTrueDofs(sol.GetBlock(1));
return fpressure;
}
void AddVelocityBC(int id, int dir, double val)
{
if(dir==0){
bcx[id]=mfem::ConstantCoefficient(val);
AddVelocityBC(id,dir,bcx[id]);
}
if(dir==1){
bcy[id]=mfem::ConstantCoefficient(val);
AddVelocityBC(id,dir,bcy[id]);
}
if(dir==2){
bcz[id]=mfem::ConstantCoefficient(val);
AddVelocityBC(id,dir,bcz[id]);
}
if(dir==4){
bcx[id]=mfem::ConstantCoefficient(val);
bcy[id]=mfem::ConstantCoefficient(val);
bcz[id]=mfem::ConstantCoefficient(val);
AddVelocityBC(id,0,bcx[id]);
AddVelocityBC(id,1,bcy[id]);
AddVelocityBC(id,2,bcz[id]);
}
}
void AddVelocityBC(int id, int dir, mfem::Coefficient& val)
{
if(dir==0){ bccx[id]=&val; }
if(dir==1){ bccy[id]=&val; }
if(dir==2){ bccz[id]=&val; }
if(dir==4){ bccx[id]=&val; bccy[id]=&val; bccz[id]=&val;}
if(pmesh->Dimension()==2)
{
bccz.clear();
}
}
mfem::BlockVector& GetSol(){return sol;}
mfem::BlockVector& GetAdj(){return adj;}
mfem::ParFiniteElementSpace* GetVelocityFES(){return vfes;}
mfem::ParFiniteElementSpace* GetPressureFES(){return pfes;}
///Get state spaces
mfem::Array<mfem::ParFiniteElementSpace*>& GetStateFES()
{
return sfes;
}
void SetDesignSpace(mfem::ParFiniteElementSpace* dfes_)
{
dfes=dfes_;
density.SetSpace(dfes);
density=0.0;
if(ltopopt.fcoef!=nullptr)
{
delete ltopopt.fcoef;
}
ltopopt.fcoef=new FluidInterpolationCoefficient(ltopopt.eta,ltopopt.beta,
ltopopt.q,ltopopt.lambda,density);
bpenal=ltopopt.fcoef;
}
// Sets the design field using true vector designvec
// and set the penalization field
void SetDesign(mfem::Vector& designvec)
{
density.SetFromTrueDofs(designvec);
if(ltopopt.fcoef!=nullptr)
{
delete ltopopt.fcoef;
}
ltopopt.fcoef=new FluidInterpolationCoefficient(ltopopt.eta,ltopopt.beta,
ltopopt.q,ltopopt.lambda,density);
bpenal=ltopopt.fcoef;
}
void SetDesignParameters(double eta_=0.5, double beta_=8.0, double q_=1, double lambda_=100)
{
ltopopt.eta=eta_;
ltopopt.beta=beta_;
ltopopt.lambda=lambda_;
ltopopt.q=q_;
}
private:
double mu;
double alpha;
mfem::Coefficient* viscosity;
mfem::Coefficient* bpenal;
mfem::VectorCoefficient* load;
mfem::ParMesh* pmesh;
mfem::ParFiniteElementSpace* vfes;
mfem::ParFiniteElementSpace* pfes;
mfem::FiniteElementCollection* vfec;
mfem::FiniteElementCollection* pfec;
mfem::Array<mfem::ParFiniteElementSpace*> sfes;
// boundary conditions
std::map<int, mfem::ConstantCoefficient> bcx;
std::map<int, mfem::ConstantCoefficient> bcy;
std::map<int, mfem::ConstantCoefficient> bcz;
std::map<int, mfem::Coefficient*> bccx;
std::map<int, mfem::Coefficient*> bccy;
std::map<int, mfem::Coefficient*> bccz;
mfem::Array<int> ess_tdofv;
mfem::BlockNonlinearFormIntegrator* nfin;
mfem::ParBlockNonlinearForm* nf;
mfem::BlockVector rhs;
mfem::BlockVector sol;
mfem::BlockVector adj;
mfem::ParGridFunction fvelocity;
mfem::ParGridFunction avelocity;
mfem::ParGridFunction fpressure;
/// The PETSc objects are allocated once the problem is
/// assembled. They are utilized in computing the adjoint
/// solutions.
mfem::PetscParMatrix* pmat;
mfem::PetscPreconditioner* prec;
mfem::PetscLinearSolver* psol;
double abs_tol;
double rel_tol;
int print_level;
int max_iter;
mfem::ParFiniteElementSpace* dfes; //design space
mfem::ParGridFunction density;
struct{
double eta;
double beta;
double lambda;
double q;
mfem::FluidInterpolationCoefficient* fcoef;
} ltopopt;
};
}
#endif
+179
View File
@@ -0,0 +1,179 @@
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "stokes.hpp"
// example runs
// mpirun -np 4 ./stokes -m ./ball2D.msh -petscopts ./stokes_fieldsplit
// mpirun -np 4 ./stokes -m ./ball2D.msh -petscopts ./stokes_fieldsplit_01
double bpenal(const mfem::Vector &x)
{
double nx=(x[0]-1.5)*(x[0]-1.5)+(x[1]-0.5)*(x[1]-0.5);
if(std::sqrt(nx)<0.1){ return 1e6;}
return 0.0;
}
int main(int argc, char *argv[])
{
// Initialize MPI.
int nprocs, myrank;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
// Parse command-line options.
const char *mesh_file = "../../data/star.mesh";
int order = 1;
bool static_cond = false;
int ser_ref_levels = 1;
int par_ref_levels = 1;
double newton_rel_tol = 1e-7;
double newton_abs_tol = 1e-12;
int newton_iter = 10;
int print_level = 1;
bool visualization = false;
const char *petscrc_file = "stokes_fieldsplit";
mfem::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",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&visualization,
"-vis",
"--visualization",
"-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&newton_rel_tol,
"-rel",
"--relative-tolerance",
"Relative tolerance for the Newton solve.");
args.AddOption(&newton_abs_tol,
"-abs",
"--absolute-tolerance",
"Absolute tolerance for the Newton solve.");
args.AddOption(&newton_iter,
"-it",
"--newton-iterations",
"Maximum iterations for the Newton solve.");
args.AddOption(&petscrc_file, "-petscopts", "--petscopts",
"PetscOptions file to use.");
args.Parse();
if (!args.Good())
{
if (myrank == 0)
{
args.PrintUsage(std::cout);
}
MPI_Finalize();
return 1;
}
if (myrank == 0)
{
args.PrintOptions(std::cout);
}
mfem::MFEMInitializePetsc(NULL,NULL,petscrc_file,NULL);
// Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
mfem::Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
// Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 10,000 elements.
{
int ref_levels =
(int)floor(log(100./mesh.GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh.UniformRefinement();
}
}
// 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.
mfem::ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
/*
{
for (int l = 0; l < par_ref_levels; l++)
{
pmesh.UniformRefinement();
}
}*/
mfem::StokesSolver* solver=new mfem::StokesSolver(&pmesh,2);
mfem::ConstantCoefficient viscosity(1);
solver->SetViscosity(viscosity);
solver->AddVelocityBC(2,0,1.0);
solver->AddVelocityBC(1,4,0.0);
mfem::Vector vload(2); vload(0)=1.0; vload(1)=0.0; //vload(2)=0.0;
mfem::VectorConstantCoefficient load(vload);
solver->SetVolForces(load);
mfem::FunctionCoefficient brink(bpenal);
solver->SetBrinkmanPenal(brink);
//solver->AddVelocityBC(3,4,0.0);
//solver->AddVelocityBC(1,4,0.0);
//mfem::ConstantCoefficient bcvelx(2.0);
//mfem::ConstantCoefficient bcvely(1.5);
//solver->AddVelocityBC(1,0,bcvelx);
//solver->AddVelocityBC(2,1,bcvely);
solver->FSolve();
{
mfem::ParGridFunction& veloc=solver->GetVelocity();
mfem::ParGridFunction& press=solver->GetPressure();
mfem::ParaViewDataCollection paraview_dc("Stokes", &pmesh);
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(order);
paraview_dc.SetDataFormat(mfem::VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.SetCycle(0);
paraview_dc.SetTime(0.0);
paraview_dc.RegisterField("velocity",&veloc);
paraview_dc.RegisterField("pressure",&press);
paraview_dc.Save();
}
delete solver;
mfem::MFEMFinalizePetsc();
MPI_Finalize();
return 0;
}
+513
View File
@@ -0,0 +1,513 @@
// Copyright (c) 2020, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef TADDENSEMATRIX_H
#define TADDENSEMATRIX_H
#include "mfem.hpp"
#include "tadvector.hpp"
namespace mfem
{
/// Templated dense matrix data type.
/** The main goal of the TADDenseMatrix class is to serve as a data
container for representing dense matrices in classes, methods, and
functions utilized with automatic differentiation (AD). The
functionality/interface is copied from the standard MFEM dense
matrix mfem::DenseMatrix. The basic idea is to utilize the templated
vector class in combination with AD during the development phase.
The AD parts can be replaced with optimized code once the initial
development of the application is complete. The common interface
between TADDenseMatrix and DenseMatrix will ease the transition
from AD to hand-optimized code as it does not require a change
in the interface or the code structure. TADDenseMatrix is intended
to be utilized for dense serial matrices. The objects can be combined
with TADVector or standard Vector.*/
template<typename dtype>
class TADDenseMatrix
{
private:
int height; ///< Dimension of the output / number of rows in the matrix.
int width; ///< Dimension of the input / number of columns in the matrix.
dtype *data;
int capacity; // zero or negative capacity means we do not own the data.
public:
/// Get the height (size of output) of the Operator. Synonym with NumRows().
inline int Height() const { return height; }
/** @brief Get the number of rows (size of output) of the Operator. Synonym
with Height(). */
inline int NumRows() const { return height; }
/// Get the width (size of input) of the Operator. Synonym with NumCols().
inline int Width() const { return width; }
/** @brief Get the number of columns (size of input) of the Operator. Synonym
with Width(). */
inline int NumCols() const { return width; }
/** Default constructor for TADDenseMatrix.
Sets data = NULL and height = width = 0. */
TADDenseMatrix()
{
data = nullptr;
capacity = 0;
height = 0;
width = 0;
}
/// Copy constructor
template<typename idtype>
TADDenseMatrix(const TADDenseMatrix<idtype> &m)
{
height = m.GetHeight();
width = m.GetWidth();
const int hw = height * width;
if (hw > 0)
{
idtype *mdata = m.Data();
MFEM_ASSERT(mdata, "invalid source matrix");
data = new dtype[hw];
capacity = hw;
for (int i = 0; i < hw; i++)
{
data[i] = mdata[i];
}
}
else
{
data = nullptr;
capacity = 0;
width = 0;
height = 0;
}
}
/// Copy constructor using standard DenseMatrix
TADDenseMatrix(const DenseMatrix &m)
{
height = m.Height();
width = m.Width();
const int hw = height * width;
if (hw > 0)
{
double *mdata = m.Data();
MFEM_ASSERT(mdata, "invalid source matrix");
data = new dtype[hw];
capacity = hw;
for (int i = 0; i < hw; i++)
{
data[i] = mdata[i];
}
}
else
{
data = nullptr;
capacity = 0;
width = 0;
height = 0;
}
}
/// Creates square matrix of size s.
explicit TADDenseMatrix(int s)
{
MFEM_ASSERT(s >= 0, "invalid DenseMatrix size: " << s);
height = s;
width = s;
capacity = s * s;
if (capacity > 0)
{
data = new dtype[capacity](); // init with zeroes
}
else
{
data = NULL;
}
}
/// Creates rectangular matrix of size m x n.
TADDenseMatrix(int m, int n)
{
MFEM_ASSERT(m >= 0 && n >= 0,
"invalid DenseMatrix size: " << m << " x " << n);
height = m;
width = n;
capacity = m * n;
if (capacity > 0)
{
data = new dtype[capacity](); // init with zeroes
}
else
{
data = NULL;
}
}
TADDenseMatrix(const TADDenseMatrix<dtype> &mat, char ch)
{
height = mat.Width();
width = mat.Height();
capacity = height * width;
if (capacity > 0)
{
data = new dtype[capacity];
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
(*this)(i, j) = mat(j, i);
}
}
}
else
{
data = NULL;
}
}
/// Change the size of the DenseMatrix to s x s.
void SetSize(int s) { SetSize(s, s); }
/// Change the size of the DenseMatrix to h x w.
void SetSize(int h, int w)
{
MFEM_ASSERT(h >= 0 && w >= 0,
"invalid DenseMatrix size: " << h << " x " << w);
if (Height() == h && Width() == w)
{
return;
}
height = h;
width = w;
const int hw = h * w;
if (hw > std::abs(capacity))
{
if (capacity > 0)
{
delete[] data;
}
capacity = hw;
data = new dtype[hw](); // init with zeroes
}
}
/// Returns the matrix data array.
inline dtype *Data() const { return data; }
/// Returns the matrix data array.
inline dtype *GetData() const { return data; }
inline bool OwnsData() const { return (capacity > 0); }
/// Returns reference to a_{ij}.
dtype &operator()(int i, int j)
{
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
return data[i + j * height];
}
const dtype &operator()(int i, int j) const
{
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
return data[i + j * height];
}
dtype &Elem(int i, int j) { return (*this)(i, j); }
const dtype &Elem(int i, int j) const { return (*this)(i, j); }
void Mult(const dtype *x, dtype *y) const
{
if (width == 0)
{
for (int row = 0; row < height; row++)
{
y[row] = 0.0;
}
return;
}
dtype *d_col = data;
dtype x_col = x[0];
for (int row = 0; row < height; row++)
{
y[row] = x_col * d_col[row];
}
d_col += height;
for (int col = 1; col < width; col++)
{
x_col = x[col];
for (int row = 0; row < height; row++)
{
y[row] += x_col * d_col[row];
}
d_col += height;
}
}
void Mult(const TADVector<dtype> &x, TADVector<dtype> &y) const
{
MFEM_ASSERT(height == y.Size() && width == x.Size(),
"incompatible dimensions");
Mult((const dtype *) x, (dtype *) y);
}
dtype operator*(const TADDenseMatrix<dtype> &m) const
{
MFEM_ASSERT(Height() == m.Height() && Width() == m.Width(),
"incompatible dimensions");
const int hw = height * width;
dtype a = 0.0;
for (int i = 0; i < hw; i++)
{
a += data[i] * m.data[i];
}
return a;
}
void MultTranspose(const dtype *x, dtype *y) const
{
dtype *d_col = data;
for (int col = 0; col < width; col++)
{
double y_col = 0.0;
for (int row = 0; row < height; row++)
{
y_col += x[row] * d_col[row];
}
y[col] = y_col;
d_col += height;
}
}
void MultTranspose(const TADVector<dtype> &x, TADVector<dtype> &y) const
{
MFEM_ASSERT(height == x.Size() && width == y.Size(),
"incompatible dimensions");
MultTranspose((const dtype *) x, (dtype *) y);
}
void Randomize(int seed)
{
// static unsigned int seed = time(0);
const double max = (double) (RAND_MAX) + 1.;
if (seed == 0)
{
seed = (int) time(0);
}
// srand(seed++);
srand((unsigned) seed);
for (int i = 0; i < capacity; i++)
{
data[i] = (dtype)(std::abs(rand() / max));
}
}
void RandomizeDiag(int seed)
{
// static unsigned int seed = time(0);
const double max = (double) (RAND_MAX) + 1.;
if (seed == 0)
{
seed = (int) time(0);
}
// srand(seed++);
srand((unsigned) seed);
for (int i = 0; i < std::min(height, width); i++)
{
Elem(i, i) = (dtype)(std::abs(rand() / max));
}
}
/// Creates n x n diagonal matrix with diagonal elements c
void Diag(dtype c, int n)
{
SetSize(n);
const int N = n * n;
for (int i = 0; i < N; i++)
{
data[i] = (dtype) 0.0;
}
for (int i = 0; i < n; i++)
{
data[i * (n + 1)] = c;
}
}
/// Creates n x n diagonal matrix with diagonal given by diag
template<typename itype>
void Diag(itype *diag, int n)
{
SetSize(n);
int i, N = n * n;
for (i = 0; i < N; i++)
{
data[i] = 0.0;
}
for (i = 0; i < n; i++)
{
data[i * (n + 1)] = (dtype) diag[i];
}
}
/// (*this) = (*this)^t
void Transpose()
{
int i, j;
dtype t;
if (Width() == Height())
{
for (i = 0; i < Height(); i++)
for (j = i + 1; j < Width(); j++)
{
t = (*this)(i, j);
(*this)(i, j) = (*this)(j, i);
(*this)(j, i) = t;
}
}
else
{
TADDenseMatrix<dtype> T(*this, 't');
(*this) = T;
}
}
/// (*this) = A^t
template<typename itype>
void Transpose(const TADDenseMatrix<itype> &A)
{
SetSize(A.Width(), A.Height());
for (int i = 0; i < Height(); i++)
for (int j = 0; j < Width(); j++)
{
(*this)(i, j) = (dtype) A(j, i);
}
}
/// (*this) = 1/2 ((*this) + (*this)^t)
void Symmetrize()
{
#ifdef MFEM_DEBUG
if (Width() != Height())
{
mfem_error("DenseMatrix::Symmetrize() : not a square matrix!");
}
#endif
for (int i = 0; i < Height(); i++)
for (int j = 0; j < i; j++)
{
dtype a = 0.5 * ((*this)(i, j) + (*this)(j, i));
(*this)(j, i) = (*this)(i, j) = a;
}
}
void Lump()
{
for (int i = 0; i < Height(); i++)
{
dtype L = 0.0;
for (int j = 0; j < Width(); j++)
{
L += (*this)(i, j);
(*this)(i, j) = (dtype) 0.0;
}
(*this)(i, i) = L;
}
}
};
template<typename dtype>
void CalcAdjugate(const TADDenseMatrix<dtype> &a, TADDenseMatrix<dtype> &adja)
{
#ifdef MFEM_DEBUG
if (a.Width() > a.Height() || a.Width() < 1 || a.Height() > 3)
{
mfem_error("CalcAdjugate(...)");
}
if (a.Width() != adja.Height() || a.Height() != adja.Width())
{
mfem_error("CalcAdjugate(...)");
}
#endif
if (a.Width() < a.Height())
{
const dtype *d = a.Data();
dtype *ad = adja.Data();
if (a.Width() == 1)
{
// N x 1, N = 2,3
ad[0] = d[0];
ad[1] = d[1];
if (a.Height() == 3)
{
ad[2] = d[2];
}
}
else
{
// 3 x 2
double e, g, f;
e = d[0] * d[0] + d[1] * d[1] + d[2] * d[2];
g = d[3] * d[3] + d[4] * d[4] + d[5] * d[5];
f = d[0] * d[3] + d[1] * d[4] + d[2] * d[5];
ad[0] = d[0] * g - d[3] * f;
ad[1] = d[3] * e - d[0] * f;
ad[2] = d[1] * g - d[4] * f;
ad[3] = d[4] * e - d[1] * f;
ad[4] = d[2] * g - d[5] * f;
ad[5] = d[5] * e - d[2] * f;
}
return;
}
if (a.Width() == 1)
{
adja(0, 0) = (dtype) 1.0;
}
else if (a.Width() == 2)
{
adja(0, 0) = a(1, 1);
adja(0, 1) = -a(0, 1);
adja(1, 0) = -a(1, 0);
adja(1, 1) = a(0, 0);
}
else
{
adja(0, 0) = a(1, 1) * a(2, 2) - a(1, 2) * a(2, 1);
adja(0, 1) = a(0, 2) * a(2, 1) - a(0, 1) * a(2, 2);
adja(0, 2) = a(0, 1) * a(1, 2) - a(0, 2) * a(1, 1);
adja(1, 0) = a(1, 2) * a(2, 0) - a(1, 0) * a(2, 2);
adja(1, 1) = a(0, 0) * a(2, 2) - a(0, 2) * a(2, 0);
adja(1, 2) = a(0, 2) * a(1, 0) - a(0, 0) * a(1, 2);
adja(2, 0) = a(1, 0) * a(2, 1) - a(1, 1) * a(2, 0);
adja(2, 1) = a(0, 1) * a(2, 0) - a(0, 0) * a(2, 1);
adja(2, 2) = a(0, 0) * a(1, 1) - a(0, 1) * a(1, 0);
}
}
} // namespace mfem
#endif
+699
View File
@@ -0,0 +1,699 @@
// Copyright (c) 2020, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_TADVECTOR
#define MFEM_TADVECTOR
#include "mfem.hpp"
#include <cmath>
#include <iostream>
#include <limits>
#if defined(_MSC_VER) && (_MSC_VER < 1800)
#include <float.h>
#define isfinite _finite
#endif
namespace mfem
{
/// Templated vector data type.
/** The main goal of the TADVector class is to serve as a data
container for representing vectors in classes, methods, and
functions utilized with automatic differentiation (AD). The
functionality/interface is copied from the standard MFEM dense
vector mfem::Vector. The basic idea is to utilize the templated
vector class in combination with AD during the development phase.
The AD parts can be replaced with optimized code once the initial
development of the application is complete. The common interface
between TADVector and Vector will ease the transition from AD to
hand-optimized code as it does not require a change in the
interface or the code structure. TADVector is intended to be
utilized for dense serial vectors. */
template<typename dtype>
class TADVector
{
protected:
dtype *data;
int size;
int capacity;
public:
/// Default constructor for Vector. Sets size = 0 and data = NULL.
TADVector()
{
data = nullptr;
size = 0;
capacity = 0;
}
/// Copy constructor. Allocates a new data array and copies the data.
TADVector(const TADVector<dtype> &v)
{
const int s = v.Size();
if (s > 0)
{
size = s;
data = new dtype[s];
capacity = s;
for (int i = 0; i < s; i++)
{
data[i] = v[i];
}
}
else
{
size = 0;
capacity = 0;
data = nullptr;
}
}
TADVector(const Vector &v)
{
const int s = v.Size();
if (s > 0)
{
size = s;
capacity = s;
data = new dtype[s];
for (int i = 0; i < s; i++)
{
data[i] = v[i];
}
}
else
{
size = 0;
capacity = 0;
data = nullptr;
}
}
/// @brief Creates vector of size s.
/// @warning Entries are not initialized to zero!
explicit TADVector(int s)
{
if (s > 0)
{
size = s;
capacity = s;
data = new dtype[size];
}
else
{
size = 0;
capacity = 0;
data = nullptr;
}
}
/// Creates a vector referencing an array of doubles, owned by someone else.
/** The pointer @a _data can be NULL. The data array can be replaced later
with SetData(). */
TADVector(dtype *_data, int _size)
{
if (capacity > 0)
{
delete[] data;
capacity = 0;
}
size = _size;
data = _data;
}
/// Reads a vector from multiple files
void Load(std::istream **in, int np, int *dim)
{
int i, j, s;
s = 0;
for (i = 0; i < np; i++)
{
s += dim[i];
}
SetSize(s);
int p = 0;
double tmpd;
for (i = 0; i < np; i++)
{
for (j = 0; j < dim[i]; j++)
{
*in[i] >> tmpd;
data[p++] = dtype(tmpd);
}
}
}
/// Load a vector from an input stream.
void Load(std::istream &in, int Size)
{
SetSize(Size);
double tmpd;
for (int i = 0; i < size; i++)
{
in >> tmpd;
data[i] = dtype(tmpd);
}
}
/// Load a vector from an input stream, reading the size from the stream.
void Load(std::istream &in)
{
int s;
in >> s;
Load(in, s);
}
/// @brief Resize the vector to size @a s.
/** If the new size is less than or equal to Capacity() then the internal
data array remains the same. Otherwise, the old array is deleted, if
owned, and a new array of size @a s is allocated without copying the
previous content of the Vector.
@warning In the second case above (new size greater than current one),
the vector will allocate new data array, even if it did not own the
original data! Also, new entries are not initialized! */
void SetSize(int s)
{
if (s == size)
{
return;
}
if (s <= capacity)
{
size = s;
return;
}
delete[] data;
data = new dtype[s];
size = s;
capacity = s;
}
/// Set the Vector data and size.
/** The Vector does not assume ownership of the new data. The new size is
@warning This method should be called only when OwnsData() is false.
@sa NewDataAndSize(). */
void SetDataAndSize(dtype *d, int s)
{
if (OwnsData())
{
delete[] data;
capacity = 0;
}
data = d;
size = s;
}
/// Set the Vector data and size, deleting the old data, if owned.
/** The Vector does not assume ownership of the new data. The new size is
also used as the new Capacity().
@sa SetDataAndSize(). */
void NewDataAndSize(dtype *d, int s) { SetDataAndSize(d, s); }
/// Reset the Vector to be a reference to a sub-vector of @a base.
inline void MakeRef(TADVector<dtype> &base, int offset, int size_)
{
NewDataAndSize(base.GetData() + offset, size_);
}
/** @brief Reset the Vector to be a reference to a sub-vector of @a base
without changing its current size. */
inline void MakeRef(TADVector<dtype> &base, int offset)
{
int tsiz = size;
NewDataAndSize(base.GetData() + offset, tsiz);
}
/// Destroy a vector
void Destroy()
{
size = 0;
capacity = 0;
delete[] data;
}
/// Returns the size of the vector.
inline int Size() const { return size; }
/// Return the size of the currently allocated data array.
/** It is always true that Capacity() >= Size(). */
inline int Capacity() const { return capacity; }
/// Return a pointer to the beginning of the Vector data.
/** @warning This method should be used with caution as it gives write access
to the data of const-qualified Vector%s. */
inline dtype *GetData() const
{
return const_cast<dtype *>((const dtype *) data);
}
/// Conversion to `double *`.
/** @note This conversion function makes it possible to use [] for indexing
in addition to the overloaded operator()(int). */
inline operator dtype *() { return data; }
/// Conversion to `const double *`.
/** @note This conversion function makes it possible to use [] for indexing
in addition to the overloaded operator()(int). */
inline operator const dtype *() const { return data; }
/// Read the Vector data (host pointer) ownership flag.
inline bool OwnsData() const { return (capacity > 0); }
/// Changes the ownership of the data; after the call the Vector is empty
inline void StealData(dtype **p)
{
*p = data;
delete[] data;
size = 0;
capacity = 0;
}
/// Changes the ownership of the data; after the call the Vector is empty
inline dtype *StealData()
{
dtype *p;
StealData(&p);
return p;
}
/// Access Vector entries. Index i = 0 .. size-1.
dtype &Elem(int i) { return operator()(i); }
/// Read only access to Vector entries. Index i = 0 .. size-1.
const dtype &Elem(int i) const { return operator()(i); }
/// Access Vector entries using () for 0-based indexing.
/** @note If MFEM_DEBUG is enabled, bounds checking is performed. */
inline dtype &operator()(int i)
{
MFEM_ASSERT(data && i >= 0 && i < size,
"index [" << i << "] is out of range [0," << size << ")");
return data[i];
}
/// Read only access to Vector entries using () for 0-based indexing.
/** @note If MFEM_DEBUG is enabled, bounds checking is performed. */
inline const dtype &operator()(int i) const
{
MFEM_ASSERT(data && i >= 0 && i < size,
"index [" << i << "] is out of range [0," << size << ")");
return data[i];
}
/// Dot product with a `dtype *` array.
dtype operator*(const dtype *v) const
{
dtype dot = 0.0;
for (int i = 0; i < size; i++)
{
dot += data[i] * v[i];
}
return dot;
}
/// Return the inner-product.
dtype operator*(const TADVector<dtype> &v) const
{
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
dtype dot = 0.0;
for (int i = 0; i < size; i++)
{
dot += data[i] * v[i];
}
return dot;
}
dtype operator*(const Vector &v) const
{
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
dtype dot = 0.0;
for (int i = 0; i < size; i++)
{
dot += data[i] * v[i];
}
return dot;
}
/// Copy Size() entries from @a v.
TADVector<dtype> &operator=(const dtype *v)
{
for (int i = 0; i < size; i++)
{
data[i] = v[i];
}
return *this;
}
/// Copy assignment.
/** @note Defining this method overwrites the implicitly defined copy
assignemnt operator. */
TADVector<dtype> &operator=(const TADVector<dtype> &v)
{
SetSize(v.Size());
for (int i = 0; i < size; i++)
{
data[i] = v[i];
}
return *this;
}
TADVector<dtype> &operator=(const Vector &v)
{
SetSize(v.Size());
for (int i = 0; i < size; i++)
{
data[i] = v[i];
}
return *this;
}
/// Redefine '=' for vector = constant.
template<typename ivtype>
TADVector &operator=(ivtype value)
{
for (int i = 0; i < size; i++)
{
data[i] = (dtype) value;
}
return *this;
}
template<typename ivtype>
TADVector &operator*=(ivtype c)
{
for (int i = 0; i < size; i++)
{
data[i] = data[i] * c;
}
return *this;
}
template<typename ivtype>
TADVector &operator/=(ivtype c)
{
for (int i = 0; i < size; i++)
{
data[i] = data[i] / c;
}
return *this;
}
TADVector &operator-=(const TADVector<dtype> &v)
{
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
for (int i = 0; i < size; i++)
{
data[i] = data[i] - v[i];
}
return *this;
}
template<typename ivtype>
TADVector &operator-=(ivtype v)
{
for (int i = 0; i < size; i++)
{
data[i] = data[i] - v;
}
return *this;
}
TADVector &operator+=(const TADVector<dtype> &v)
{
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
for (int i = 0; i < size; i++)
{
data[i] = data[i] + v[i];
}
return *this;
}
template<typename ivtype>
TADVector &operator+=(ivtype v)
{
for (int i = 0; i < size; i++)
{
data[i] = data[i] + v;
}
return *this;
}
/// (*this) += a * Va
template<typename ivtype, typename vtype>
TADVector &Add(const ivtype a, const vtype &v)
{
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
for (int i = 0; i < size; i++)
{
data[i] = data[i] + a * v[i];
}
return *this;
}
/// (*this) = a * x
template<typename ivtype, typename vtype>
TADVector &Set(const ivtype a, const vtype &v)
{
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
for (int i = 0; i < size; i++)
{
data[i] = a * v[i];
}
return *this;
}
template<typename vtype>
void SetVector(const vtype &v, int offset)
{
MFEM_ASSERT(v.Size() + offset <= size, "invalid sub-vector");
for (int i = 0; i < size; i++)
{
data[i + offset] = v[i];
}
}
/// (*this) = -(*this)
void Neg()
{
for (int i = 0; i < size; i++)
{
data[i] = -data[i];
}
}
/// Swap the contents of two Vectors
inline void Swap(TADVector<dtype> &other)
{
Swap(data, other.data);
Swap(size, other.size);
Swap(capacity, other.capacity);
}
/// Set v = v1 + v2.
template<typename vtype1, typename vtype2>
friend void add(const vtype1 &v1, const vtype2 &v2, TADVector<dtype> &v)
{
MFEM_ASSERT(v1.Size() == v.Size(), "incompatible Vectors!");
MFEM_ASSERT(v2.Size() == v.Size(), "incompatible Vectors!");
for (int i = 0; i < v.Size(); i++)
{
v[i] = v1[i] + v2[i];
}
}
/// Set v = v1 + alpha * v2.
template<typename vtype1, typename vtype2>
friend void add(const vtype1 &v1,
dtype alpha,
const vtype2 &v2,
TADVector<dtype> &v)
{
MFEM_ASSERT(v1.Size() == v.Size(), "incompatible Vectors!");
MFEM_ASSERT(v2.Size() == v.Size(), "incompatible Vectors!");
for (int i = 0; i < v.Size(); i++)
{
v[i] = v1[i] + alpha * v2[i];
}
}
template<typename vtype1, typename vtype2>
friend void add(const dtype a,
const vtype1 &x,
const dtype b,
const vtype2 &y,
TADVector<dtype> &z)
{
MFEM_ASSERT(x.Size() == y.Size() && x.Size() == z.Size(),
"incompatible Vectors!");
for (int i = 0; i < z.Size(); i++)
{
z[i] = a * x[i] + b * y[i];
}
}
template<typename vtype1, typename vtype2>
friend void add(const dtype a,
const vtype1 &x,
const vtype2 &y,
TADVector<dtype> &z)
{
MFEM_ASSERT(x.Size() == y.Size() && x.Size() == z.Size(),
"incompatible Vectors!");
for (int i = 0; i < z.Size(); i++)
{
z[i] = a * x[i] + y[i];
}
}
template<typename vtype1, typename vtype2>
friend void subtract(const vtype1 &x, const vtype2 &y, TADVector<dtype> &z)
{
MFEM_ASSERT(x.Size() == y.Size() && x.Size() == z.Size(),
"incompatible Vectors!");
for (int i = 0; i < z.Size(); i++)
{
z[i] = x[i] - y[i];
}
}
template<typename ivtype, typename vtype1, typename vtype2>
friend void subtract(const ivtype a,
const vtype1 &x,
const vtype2 &y,
TADVector<dtype> &z)
{
MFEM_ASSERT(x.Size() == y.Size() && x.Size() == z.Size(),
"incompatible Vectors!");
for (int i = 0; i < z.Size(); i++)
{
z[i] = a * (x[i] - y[i]);
}
}
/// Destroys vector.
~TADVector() { delete[] data; }
/// Prints vector to stream out.
void Print(std::ostream &out = mfem::out, int width = 8) const
{
if (!size)
{
return;
}
for (int i = 0; 1;)
{
out << data[i];
i++;
if (i == size)
{
break;
}
if (i % width == 0)
{
out << '\n';
}
else
{
out << ' ';
}
}
out << '\n';
}
/// Set random values in the vector.
void Randomize(int seed = 0)
{
// static unsigned int seed = time(0);
const double max = (double) (RAND_MAX) + 1.;
if (seed == 0)
{
seed = (int) time(0);
}
// srand(seed++);
srand((unsigned) seed);
for (int i = 0; i < size; i++)
{
data[i] = std::abs(rand() / max);
}
}
/// Returns the l2 norm of the vector.
dtype Norml2() const
{
// Scale entries of Vector on the fly, using algorithms from
// std::hypot() and LAPACK's drm2. This scaling ensures that the
// argument of each call to std::pow is <= 1 to avoid overflow.
if (0 == size)
{
return 0.0;
} // end if 0 == size
if (1 == size)
{
return abs(data[0]);
} // end if 1 == size
dtype scale = 0.0;
dtype sum = 0.0;
for (int i = 0; i < size; i++)
{
if (data[i] != 0.0)
{
const dtype absdata = abs(data[i]);
if (scale <= absdata)
{
const dtype sqr_arg = scale / absdata;
sum = 1.0 + sum * (sqr_arg * sqr_arg);
scale = absdata;
continue;
} // end if scale <= absdata
const dtype sqr_arg = absdata / scale;
sum += (sqr_arg * sqr_arg); // else scale > absdata
} // end if data[i] != 0
}
return scale * sqrt(sum);
}
/// Returns the l_infinity norm of the vector.
dtype Normlinf() const
{
dtype max = 0.0;
for (int i = 0; i < size; i++)
{
max = max(abs(data[i]), max);
}
return max;
}
/// Returns the l_1 norm of the vector.
dtype Norml1() const
{
dtype sum = 0.0;
for (int i = 0; i < size; i++)
{
sum += abs(data[i]);
}
return sum;
}
};
} // namespace mfem
#endif