Compare commits
314
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8d0ba6715 | ||
|
|
0328a5c117 | ||
|
|
5333e1fc7e | ||
|
|
64c8725594 | ||
|
|
b57486e027 | ||
|
|
5e0140e877 | ||
|
|
1ccb31fde6 | ||
|
|
a279b4592b | ||
|
|
40b61f789e | ||
|
|
0933014721 | ||
|
|
c9e63c6292 | ||
|
|
05442e17dc | ||
|
|
27235c38ef | ||
|
|
5fc6ae6201 | ||
|
|
315065b727 | ||
|
|
51adfe2785 | ||
|
|
7c6af8cab8 | ||
|
|
397abc4190 | ||
|
|
680ccf0f2d | ||
|
|
e42f2cc32e | ||
|
|
e4c0b956e8 | ||
|
|
1844c93b14 | ||
|
|
1d1443cb1a | ||
|
|
a15866e212 | ||
|
|
9baadbe00e | ||
|
|
4222287b02 | ||
|
|
d46c2cd5a7 | ||
|
|
5a2d286e0c | ||
|
|
d15f9136c5 | ||
|
|
5d50d96a7b | ||
|
|
edf209a78c | ||
|
|
eb1a95acfd | ||
|
|
bee28f59f5 | ||
|
|
cbad53d9f9 | ||
|
|
bebdead740 | ||
|
|
1a5207f856 | ||
|
|
be82bb8e6b | ||
|
|
0c7fee782f | ||
|
|
0ba4fdc591 | ||
|
|
300af9c231 | ||
|
|
4a4b0062f8 | ||
|
|
aee4575404 | ||
|
|
834205ac04 | ||
|
|
df8b9928f8 | ||
|
|
3ad69e8e65 | ||
|
|
057e25657e | ||
|
|
94fb0f94c5 | ||
|
|
f986b2022b | ||
|
|
8806eec449 | ||
|
|
c1779a49cb | ||
|
|
472f2b83d2 | ||
|
|
d4d1eadb1a | ||
|
|
1a10217ba4 | ||
|
|
65921a4dda | ||
|
|
1cf0ffbe03 | ||
|
|
32432c2c19 | ||
|
|
2fc92344bc | ||
|
|
3e1a24c35c | ||
|
|
aeacd50a40 | ||
|
|
edc138ebd5 | ||
|
|
134af6f08f | ||
|
|
c2ed725739 | ||
|
|
971eb7bb5f | ||
|
|
c4dc57ffd2 | ||
|
|
89f7d276ef | ||
|
|
cddbd24df3 | ||
|
|
39714f039a | ||
|
|
e42147f3a7 | ||
|
|
62dc04c5ca | ||
|
|
fa101bcb05 | ||
|
|
1eb679b9e1 | ||
|
|
c770c80bf7 | ||
|
|
100de86cfa | ||
|
|
7d2e402a08 | ||
|
|
c0dab30375 | ||
|
|
fdd1c6c8b4 | ||
|
|
d70cb175e7 | ||
|
|
7c62434ef3 | ||
|
|
e18279782d | ||
|
|
90c13f0634 | ||
|
|
21889e22e4 | ||
|
|
b9a3d1fffe | ||
|
|
b77c106d72 | ||
|
|
994cdacff4 | ||
|
|
c52da3dd57 | ||
|
|
7c9f9b262d | ||
|
|
a759f32692 | ||
|
|
79b2967464 | ||
|
|
d6aab14ec1 | ||
|
|
69302c3ce7 | ||
|
|
75e5bb2a7f | ||
|
|
56eef31eb8 | ||
|
|
abe702f5b8 | ||
|
|
8a5b30ff71 | ||
|
|
a9eb6ecc1a | ||
|
|
98d96e5c99 | ||
|
|
f121373467 | ||
|
|
428f051e1c | ||
|
|
bc441dab14 | ||
|
|
d919149e5f | ||
|
|
1ee6e88934 | ||
|
|
76905b7496 | ||
|
|
1b76a2ee5e | ||
|
|
5d2f112d1e | ||
|
|
a35c335bd7 | ||
|
|
5848987cf7 | ||
|
|
2aa15a7ef0 | ||
|
|
7417766c5e | ||
|
|
9ef9f3c271 | ||
|
|
037031bae6 | ||
|
|
fc13314a10 | ||
|
|
f0a5e74bab | ||
|
|
91199ccb6c | ||
|
|
72bfdbc906 | ||
|
|
a93c20c5ad | ||
|
|
2103b7ea8c | ||
|
|
c2deaeed46 | ||
|
|
3883d47caa | ||
|
|
fb41dc55a8 | ||
|
|
e07747c246 | ||
|
|
75e6cfc574 | ||
|
|
575c63a564 | ||
|
|
40fefd264e | ||
|
|
340f0b8d51 | ||
|
|
c2d4f28f87 | ||
|
|
3418bd94d3 | ||
|
|
e92d16f165 | ||
|
|
3e2de8dfbe | ||
|
|
84a2de8dec | ||
|
|
b5e65b187d | ||
|
|
3b57334da0 | ||
|
|
acf49d4035 | ||
|
|
864677cdcb | ||
|
|
ba91fb564b | ||
|
|
d9d674b82e | ||
|
|
c17092c5c7 | ||
|
|
e7fb674fa3 | ||
|
|
adcc8a53b8 | ||
|
|
f9167a6752 | ||
|
|
b3d4f575b1 | ||
|
|
fa81db96ba | ||
|
|
3e1bb914c5 | ||
|
|
0ca88cb295 | ||
|
|
2419e6211e | ||
|
|
7f4cce4a0a | ||
|
|
4016cceb6a | ||
|
|
c4336e8c6c | ||
|
|
228e4e5564 | ||
|
|
1a6a3ac5d7 | ||
|
|
d22c68dbd8 | ||
|
|
c068471192 | ||
|
|
502ad09e67 | ||
|
|
e2963ad817 | ||
|
|
a389cd5257 | ||
|
|
6d0b9d81c2 | ||
|
|
06daad4e03 | ||
|
|
8009bcb192 | ||
|
|
c82b9b33fc | ||
|
|
886b04a5f3 | ||
|
|
07b5787f5b | ||
|
|
c4cd39a785 | ||
|
|
a7be74494c | ||
|
|
525a083a78 | ||
|
|
5a4190c392 | ||
|
|
e4ba44bcb7 | ||
|
|
5a494f1bbf | ||
|
|
93dcb69132 | ||
|
|
b9de68ea8c | ||
|
|
311accf5eb | ||
|
|
ad61527aff | ||
|
|
e75f24ff76 | ||
|
|
72a1d09920 | ||
|
|
03be75de97 | ||
|
|
ec77e3988a | ||
|
|
ccadddc9f5 | ||
|
|
8adb531554 | ||
|
|
11f3d34963 | ||
|
|
f561f1d069 | ||
|
|
6376dc1bb9 | ||
|
|
1bcd411837 | ||
|
|
31ed77c025 | ||
|
|
a124640f43 | ||
|
|
9ca0124b99 | ||
|
|
29d17b7174 | ||
|
|
ceab44d9fd | ||
|
|
d8d5adb62b | ||
|
|
aad2c99e48 | ||
|
|
5630c8aefc | ||
|
|
80cf146882 | ||
|
|
c6c2381171 | ||
|
|
c7e8d7f99a | ||
|
|
1b8d520eb9 | ||
|
|
d2af3ac8f4 | ||
|
|
8a5a0d038c | ||
|
|
3331d6d9b6 | ||
|
|
47241bd101 | ||
|
|
42feb99ddc | ||
|
|
261c252e28 | ||
|
|
6a09060d1a | ||
|
|
fbf9839a3e | ||
|
|
e21949b16b | ||
|
|
ddc27a60df | ||
|
|
36e3062378 | ||
|
|
102d4ad3f8 | ||
|
|
3ef4611c1f | ||
|
|
b81b023ea5 | ||
|
|
35938b691a | ||
|
|
ada5c8f4e9 | ||
|
|
16fede3622 | ||
|
|
ebdfac0dd7 | ||
|
|
a89d24843f | ||
|
|
05f18737a0 | ||
|
|
b06b1c0006 | ||
|
|
6b8865705e | ||
|
|
ebcb6cc603 | ||
|
|
dee6d806dd | ||
|
|
0b5be406aa | ||
|
|
10e5ac4403 | ||
|
|
f645886ada | ||
|
|
2a974948a6 | ||
|
|
e8e6b1b159 | ||
|
|
d52d1fea0a | ||
|
|
c2e028c916 | ||
|
|
c18c204279 | ||
|
|
80a4407f74 | ||
|
|
a4a5f784ab | ||
|
|
8d3c5ee11c | ||
|
|
b8da9d0448 | ||
|
|
431d3ea432 | ||
|
|
5ddc41f07e | ||
|
|
8d4b3657aa | ||
|
|
ebbd6dee0c | ||
|
|
d585f60011 | ||
|
|
39c661388e | ||
|
|
f1eb3267bb | ||
|
|
0cde0fd1bb | ||
|
|
57483a1921 | ||
|
|
60b82f2fe4 | ||
|
|
1eab3a2193 | ||
|
|
8becbfbb7d | ||
|
|
2ae4b1914a | ||
|
|
813e99a41b | ||
|
|
15af16215a | ||
|
|
bc76844900 | ||
|
|
8f0d944b26 | ||
|
|
ea283d2ac7 | ||
|
|
242b2c4f91 | ||
|
|
15deef3206 | ||
|
|
abc7074b3a | ||
|
|
18c0719ba2 | ||
|
|
cc8cebaa99 | ||
|
|
de262c4134 | ||
|
|
c0c3618627 | ||
|
|
e1c2bf47d5 | ||
|
|
5403f28eca | ||
|
|
f5cd5b4117 | ||
|
|
9cc92e72c2 | ||
|
|
820ececea0 | ||
|
|
b113b19cd6 | ||
|
|
7885381cd8 | ||
|
|
6286897c16 | ||
|
|
c779c2ec8d | ||
|
|
a5391d125f | ||
|
|
550904ab55 | ||
|
|
d1bd5c6d95 | ||
|
|
7f63fba18c | ||
|
|
5ac9aa4a47 | ||
|
|
11b8330ae3 | ||
|
|
58ca949331 | ||
|
|
4c27a6c252 | ||
|
|
25b23be5e8 | ||
|
|
05151e52eb | ||
|
|
d27e88109d | ||
|
|
e9a5bf3c8f | ||
|
|
b58e1e09d3 | ||
|
|
40e3d14449 | ||
|
|
f5469f73b7 | ||
|
|
8b1df3016d | ||
|
|
1bfce790ae | ||
|
|
e39b7498fc | ||
|
|
838cc607b6 | ||
|
|
fddc4fb9e1 | ||
|
|
06a49e3e42 | ||
|
|
5577701da0 | ||
|
|
10b486fe47 | ||
|
|
a0d1e57794 | ||
|
|
d370ba7ff3 | ||
|
|
99338fd4de | ||
|
|
d7573ec13b | ||
|
|
555dec9d2c | ||
|
|
cee9e87fde | ||
|
|
2380adc193 | ||
|
|
57397e0373 | ||
|
|
5f0b9d52ff | ||
|
|
cd36c676eb | ||
|
|
901a2143b7 | ||
|
|
780f383762 | ||
|
|
77a58cd1ab | ||
|
|
debdaff7b2 | ||
|
|
161163a160 | ||
|
|
913c80e05f | ||
|
|
1afe29794a | ||
|
|
b03e51fdac | ||
|
|
12c9aa6207 | ||
|
|
08353d2c4f | ||
|
|
a96b9f873b | ||
|
|
6723095508 | ||
|
|
6dd313d466 | ||
|
|
f09e08bb8f | ||
|
|
4b1709a418 | ||
|
|
5429f7af4a | ||
|
|
c9accc9abe | ||
|
|
331111c3d3 | ||
|
|
d93891f6c1 |
@@ -72,6 +72,9 @@ jobs:
|
||||
codecov: NO
|
||||
- target: opt
|
||||
codecov: YES
|
||||
- os: ubuntu-latest
|
||||
target: dbg
|
||||
config-opts: 'CPPFLAGS+=-Og'
|
||||
- os: windows-latest
|
||||
codecov: NO
|
||||
- os: windows-latest
|
||||
@@ -230,7 +233,7 @@ jobs:
|
||||
metis-dir: ${{ env.METIS_TOP_DIR }}
|
||||
mfem-dir: ${{ env.MFEM_TOP_DIR }}
|
||||
config-options: ${{ matrix.config-opts }}
|
||||
library-only: ${{ matrix.target == 'dbg' }}
|
||||
library-only: ${{ matrix.target == 'dbg' && matrix.os != 'ubuntu-latest' }}
|
||||
|
||||
# Run checks (and only checks) on debug targets
|
||||
- name: checks
|
||||
@@ -240,7 +243,7 @@ jobs:
|
||||
|
||||
# Note: 'tests' include the unit tests
|
||||
- name: tests
|
||||
if: matrix.build-system == 'make' && matrix.target == 'opt'
|
||||
if: matrix.build-system == 'make' && (matrix.target == 'opt' || matrix.os == 'ubuntu-latest')
|
||||
run: |
|
||||
cd ${{ env.MFEM_TOP_DIR }} && make test
|
||||
|
||||
|
||||
@@ -213,6 +213,7 @@ miniapps/meshing/twist
|
||||
miniapps/meshing/mesh-explorer
|
||||
miniapps/meshing/shaper
|
||||
miniapps/meshing/extruder
|
||||
miniapps/meshing/fit-node-position
|
||||
miniapps/meshing/trimmer
|
||||
miniapps/meshing/reflector
|
||||
miniapps/meshing/mesh-optimizer
|
||||
@@ -265,11 +266,15 @@ miniapps/navier/*_output
|
||||
miniapps/nurbs/nurbs_ex1
|
||||
miniapps/nurbs/nurbs_ex1p
|
||||
miniapps/nurbs/nurbs_ex11p
|
||||
miniapps/nurbs/nurbs_patch_ex1
|
||||
miniapps/nurbs/nurbs_curveint
|
||||
miniapps/nurbs/refined.mesh
|
||||
miniapps/nurbs/mesh.*
|
||||
miniapps/nurbs/sol.*
|
||||
miniapps/nurbs/mode_*
|
||||
miniapps/nurbs/Example1*
|
||||
miniapps/nurbs/sin-fit.mesh
|
||||
miniapps/nurbs/CurveInt
|
||||
|
||||
miniapps/performance/ex1
|
||||
miniapps/performance/ex1p
|
||||
@@ -292,6 +297,7 @@ miniapps/tools/display-basis
|
||||
miniapps/tools/load-dc
|
||||
miniapps/tools/convert-dc
|
||||
miniapps/tools/lor-transfer
|
||||
miniapps/tools/plor-transfer
|
||||
miniapps/tools/get-values
|
||||
miniapps/tools/check-tmop-metric
|
||||
miniapps/tools/tmop-metric-magnitude
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
Version 4.5.3 (development)
|
||||
===========================
|
||||
- Added curve interpolation method for NURBS.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
@@ -20,7 +21,10 @@ New and updated examples and miniapps
|
||||
skewness, and aspect-ratio computed from the Jacobian of the transformation.
|
||||
|
||||
- Added a new miniapp for interface and boundary fitting to implicit domains
|
||||
defined using level-set functions. See miniapps/meshing/pmesh-fitting.cpp
|
||||
defined using level-set functions. See miniapps/meshing/pmesh-fitting.cpp.
|
||||
|
||||
- Added a new miniapp for fitting of selected mesh nodes to specified positions,
|
||||
while maintaining mesh quality. See miniapps/meshing/fit-node-position.cpp.
|
||||
|
||||
- Added new Discontinuous Petrov-Galerkin (DPG) miniapp which includes serial
|
||||
and parallel examples for diffusion, convection-diffusion, acoustics and
|
||||
@@ -32,6 +36,8 @@ New and updated examples and miniapps
|
||||
- Added new SubMesh examples demonstrating source terms and boundary conditions
|
||||
transferred from SubMesh objects.
|
||||
|
||||
- Added a miniapp for interpolation of NURBS.
|
||||
|
||||
- Added a new H(div) solvers miniapp in miniapps/hdiv-linear-solver,
|
||||
demonstrating the use of a matrix-free saddle-point solver methodology,
|
||||
suitable for high-order discretizations and for GPU acceleration. Examples
|
||||
@@ -42,8 +48,20 @@ New and updated examples and miniapps
|
||||
|
||||
- Moved the distance solver methods from miniapps/shifted to miniapps/common.
|
||||
|
||||
- Added a new parallel LOR transfer miniapp, miniapps/tools/plor-transfer, which
|
||||
mirrors the functionality of the serial LOR transfer miniapp,
|
||||
miniapps/tools/lor-transfer
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- Added support for free connectivity of NURBS patches allowing for more complex
|
||||
patch configurations such as C-meshes. This is demonstrated in a new NURBS
|
||||
miniapp.
|
||||
|
||||
- The edge to knot map for NURBS meshes can be determined automatically. It is no
|
||||
longer needed to specify this in the NURBS mesh. A mesh in the NURBS miniapp
|
||||
demonstrates this.
|
||||
|
||||
- Added new methods in the Mesh class to set and get attributes on NURBS patches
|
||||
and patch boundaries.
|
||||
|
||||
@@ -63,8 +81,16 @@ Discretization improvements
|
||||
|
||||
- VectorFEBoundaryFluxLFIntegrator is now supported on device/GPU.
|
||||
|
||||
- Added support for partial assembly on NURBS patches and NURBS patch sparse
|
||||
matrix assembly. Patch matrix assembly includes the option to use reduced
|
||||
approximate integration rules, computed by the newly implemented non-negative
|
||||
least-squares (NNLS) solver.
|
||||
|
||||
- Added support for p-refined meshes in FindPointsGSLIB.
|
||||
|
||||
- Support for parallel transfer of H1 fields using the low-order refined (LOR)
|
||||
transfer operators in L2ProjectionGridTransfer
|
||||
|
||||
Linear and nonlinear solvers
|
||||
----------------------------
|
||||
- Updated interface to MUMPS direct solver to support multiple right-hand
|
||||
@@ -90,6 +116,8 @@ Miscellaneous
|
||||
- Reorganized files for bilinear form, linear form, and nonlinear form integrators
|
||||
in the fem/integ/ subdirectory.
|
||||
|
||||
- FiniteElementSpace::GetFE has been updated to abort instead of returning NULL for
|
||||
an empty partition.
|
||||
|
||||
Version 4.5.2, released on March 23, 2023
|
||||
=========================================
|
||||
|
||||
+1
-1
@@ -138,7 +138,7 @@ if (MFEM_USE_CUDA)
|
||||
set(CUDA_FLAGS "-ccbin=${CMAKE_CXX_COMPILER} ${CUDA_FLAGS}")
|
||||
set(CMAKE_CUDA_HOST_LINK_LAUNCHER ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
set(CMAKE_CUDA_FLAGS ${CMAKE_CUDA_FLAGS} ${CUDA_FLAGS})
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${CUDA_FLAGS}")
|
||||
set(CUSPARSE_FOUND TRUE)
|
||||
set(CUSPARSE_LIBRARIES "cusparse")
|
||||
set(CUBLAS_FOUND TRUE)
|
||||
|
||||
@@ -19,9 +19,7 @@ RUN apt-get update && \
|
||||
apt-get install -y libcurl4-openssl-dev libssl-dev
|
||||
|
||||
ENV PATH=$PATH:/opt/mfem-view/bin
|
||||
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/mfem-view/lib:/opt/mfem-view/lib64
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# The user will see the view on shell into the container
|
||||
WORKDIR /opt/mfem-view
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
|
||||
@@ -34,14 +34,14 @@ RUN cd /opt/mfem-env && \
|
||||
. /opt/spack/share/spack/setup-env.sh && \
|
||||
spack env activate . && \
|
||||
spack develop --path /code mfem@master+examples+miniapps && \
|
||||
spack add mfem@master+examples+miniapps # && \
|
||||
# spack install
|
||||
spack add mfem@master+examples+miniapps && \
|
||||
spack install
|
||||
|
||||
# ensure mfem always on various paths
|
||||
#RUN cd /opt/mfem-env && \
|
||||
# spack env activate --sh -d . >> /etc/profile.d/z10_spack_environment.sh
|
||||
RUN cd /opt/mfem-env && \
|
||||
spack env activate --sh -d . >> /etc/profile.d/z10_spack_environment.sh
|
||||
|
||||
# Present the software install when we shell in
|
||||
# The view is at /opt/mfem-env/.spack-env/view
|
||||
#WORKDIR /opt/software
|
||||
#ENTRYPOINT ["/bin/bash", "--rcfile", "/etc/profile", "-l", "-c"]
|
||||
WORKDIR /opt/software
|
||||
ENTRYPOINT ["/bin/bash", "--rcfile", "/etc/profile", "-l", "-c"]
|
||||
|
||||
+108
-46
@@ -7,21 +7,31 @@ You can use this image for a demo of using mfem! 🎉️
|
||||
Updated containers are built and deployed on merges to the main branch and releases.
|
||||
If you want to request a build on demand, you can [manually run the workflow](https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow) thanks to the workflow dispatch event.
|
||||
|
||||
### Usage
|
||||
## Usage
|
||||
|
||||
Here is how to build the container. Note that we build so it belongs to the same
|
||||
namespace as the repository here. "ghcr.io" means "GitHub Container Registry" and
|
||||
We provide two containers, which you can either build or use directly from
|
||||
[GitHub packages](https://github.com/orgs/mfem/packages?repo_name=mfem).
|
||||
|
||||
- `ghcr.io/mfem/mfem-ubuntu-base`: a "build from scratch" for mfem
|
||||
- `ghcr.io/mfem/mfem-ubuntu`: a quick build that uses the base container
|
||||
|
||||
In the above, "ghcr.io" means "GitHub Container Registry" and
|
||||
is the [GitHub packages](https://github.com/features/packages) registry that supports
|
||||
Docker images and other OCI artifacts. From the root of the repository:
|
||||
Docker images and other OCI artifacts.
|
||||
|
||||
### Ubuntu
|
||||
|
||||
> Use or build this container for a multi-stage, slimmer base to develop on top of mfem
|
||||
|
||||
Note that this container is provided on GitHub packages [here](https://github.com/mfem/mfem/pkgs/container/mfem-ubuntu)
|
||||
so you don't need to build it. However, if you want to, you can do the following:
|
||||
|
||||
```bash
|
||||
$ docker build -f config/docker/Dockerfile -t ghcr.io/mfem/mfem-ubuntu .
|
||||
$ docker build -f config/docker/Dockerfile.base -t ghcr.io/mfem/mfem-ubuntu-base .
|
||||
```
|
||||
|
||||
### Shell Ubuntu
|
||||
|
||||
To shell into the container:
|
||||
Note that this will pull the base image. If you want to rebuild it, see [ubuntu base](#ubuntu-base)
|
||||
below. Once you have built (or prefer to pull) you can shell into the container as follows:
|
||||
|
||||
```bash
|
||||
$ docker run -it ghcr.io/mfem/mfem-ubuntu
|
||||
@@ -37,39 +47,13 @@ bin etc include lib libexec sbin share var
|
||||
- Examples are in share/mfem/examples
|
||||
- Examples are in share/mfem/miniapps
|
||||
|
||||
You can read more about interaction with these examples and miniapps below.
|
||||
|
||||
### Shell Ubuntu Base
|
||||
|
||||
To shell into the container:
|
||||
Using this container, if you want to develop a tool that _uses_ mfem, you can find the libraries / includes in:
|
||||
|
||||
```bash
|
||||
$ docker run -it ghcr.io/mfem/mfem-ubuntu-base bash
|
||||
```
|
||||
|
||||
Off the bat, you can see mfem libraries are in your path so you can jump into development:
|
||||
|
||||
```bash
|
||||
env | grep mfem
|
||||
```
|
||||
```bash
|
||||
PKG_CONFIG_PATH=/opt/mfem-env/.spack-env/view/lib/pkgconfig:/opt/mfem-env/.spack-env/view/share/pkgconfig:/opt/mfem-env/.spack-env/view/lib64/pkgconfig
|
||||
PWD=/opt/mfem-env
|
||||
MANPATH=/opt/mfem-env/.spack-env/view/share/man:/opt/mfem-env/.spack-env/view/man:
|
||||
CMAKE_PREFIX_PATH=/opt/mfem-env/.spack-env/view
|
||||
SPACK_ENV=/opt/mfem-env
|
||||
ACLOCAL_PATH=/opt/mfem-env/.spack-env/view/share/aclocal
|
||||
LD_LIBRARY_PATH=/opt/mfem-env/.spack-env/view/lib:/opt/mfem-env/.spack-env/view/lib64
|
||||
PATH=/opt/mfem-env/.spack-env/view/bin:/opt/view/bin:/opt/spack/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
```
|
||||
|
||||
#### Examples and MiniApps
|
||||
|
||||
If you want to develop a tool that _uses_ mfem, you can find the built libraries in:
|
||||
|
||||
```
|
||||
$ ls /opt/mfem-env/.spack-env/view/
|
||||
bin etc include lib libexec sbin share var
|
||||
$ ls include/ | grep mfem
|
||||
mfem
|
||||
mfem-performance.hpp
|
||||
mfem.hpp
|
||||
```
|
||||
|
||||
And yes, this is the working directory when you shell into the container!
|
||||
@@ -79,6 +63,16 @@ You can find the examples here:
|
||||
```bash
|
||||
cd share/mfem/examples
|
||||
```
|
||||
|
||||
Try quickly setting the `LD_LIBRARY_PATH` so we can see the shared libraries
|
||||
we need:
|
||||
|
||||
```bash
|
||||
export LD_LIBRARY_PATH=/opt/mfem-view/lib:$LD_LIBRARY_PATH
|
||||
```
|
||||
|
||||
And then run:
|
||||
|
||||
```bash
|
||||
$ ./ex0
|
||||
Options used:
|
||||
@@ -97,7 +91,6 @@ Number of unknowns: 101
|
||||
Average reduction factor = 0.140201
|
||||
```
|
||||
|
||||
Try running a few, and look at the associated .cpp file for the source code!
|
||||
You can also explore the "mini apps," also in share/mfem, but under miniapps.
|
||||
|
||||
```bash
|
||||
@@ -130,18 +123,87 @@ Rule:
|
||||
Applying rule...done.
|
||||
```
|
||||
|
||||
Have fun!
|
||||
Have fun! As a reminder, this container is ideal for developing your own
|
||||
applications that might use mfem, or having a nice environment to test out
|
||||
examples.
|
||||
|
||||
|
||||
#### Your own App
|
||||
If you want to develop with your own code base
|
||||
(and mfem as is in the container) you can bind to somewhere else in the container (e.g., src)
|
||||
### Ubuntu Base
|
||||
|
||||
> Use this build for a development environment with spack and mfem
|
||||
|
||||
This container is also [provided on GitHub packages](https://github.com/mfem/mfem/pkgs/container/mfem-ubuntu-base),
|
||||
however you can build it locally too:
|
||||
|
||||
```bash
|
||||
$ docker run -it ghcr.io/mfem/mfem-ubuntu-base -v $PWD:/src bash
|
||||
$ docker build -f config/docker/Dockerfile.base -t ghcr.io/mfem/mfem-ubuntu-base .
|
||||
```
|
||||
|
||||
To shell into the container:
|
||||
|
||||
```bash
|
||||
$ docker run -it ghcr.io/mfem/mfem-ubuntu-base bash
|
||||
```
|
||||
|
||||
Change directory to the mfem environment, setup spack, and activate the environment:
|
||||
|
||||
```bash
|
||||
source /opt/spack/share/spack/setup-env.sh
|
||||
cd /opt/mfem-env/
|
||||
spack env activate .
|
||||
```
|
||||
|
||||
Note that this environment is installing to the view at `/opt/view`. Since the environment
|
||||
knows to install mfem from `/code` this means that you could make changes in the container (or bind
|
||||
`/code` to your container) and then update spack:
|
||||
|
||||
```bash
|
||||
# Note that concretization takes a hot minute!
|
||||
$ spack install
|
||||
```
|
||||
|
||||
And if you want to load mfem:
|
||||
|
||||
```bash
|
||||
$ spack load mfem
|
||||
$ env | grep mfem
|
||||
```
|
||||
|
||||
In this development container, you can find the examples and miniapps alongside
|
||||
mfem under `/code`:
|
||||
|
||||
```bash
|
||||
cd /code/examples
|
||||
```
|
||||
```bash
|
||||
$ ./ex0
|
||||
```
|
||||
```console
|
||||
Options used:
|
||||
--mesh ../data/star.mesh
|
||||
--order 1
|
||||
Number of unknowns: 101
|
||||
Iteration : 0 (B r, r) = 0.184259
|
||||
Iteration : 1 (B r, r) = 0.102754
|
||||
Iteration : 2 (B r, r) = 0.00558141
|
||||
Iteration : 3 (B r, r) = 1.5247e-05
|
||||
Iteration : 4 (B r, r) = 1.13807e-07
|
||||
Iteration : 5 (B r, r) = 6.27231e-09
|
||||
Iteration : 6 (B r, r) = 3.76268e-11
|
||||
Iteration : 7 (B r, r) = 6.07423e-13
|
||||
Iteration : 8 (B r, r) = 4.10615e-15
|
||||
Average reduction factor = 0.140201
|
||||
```
|
||||
|
||||
This container is likely ideal for someone that wants to develop mfem itself.
|
||||
For other use cases, we recommend using the slimmer image. As an example,
|
||||
if you want to develop with your own code base (and mfem as is in the container)
|
||||
you can bind to somewhere else in the container (e.g., src)
|
||||
|
||||
```bash
|
||||
$ docker run -it ghcr.io/mfem/mfem-ubuntu-base -v $PWD:/code bash
|
||||
```
|
||||
|
||||
In the above, we can pretend your project is in the present working directory (PWD) and we are
|
||||
binding to source. You can then use the mfem in the container for development, and if you
|
||||
want to distribute your library or app in a container, you can use the mfem container as the base.
|
||||
|
||||
|
||||
@@ -1,907 +0,0 @@
|
||||
#include "mfem.hpp"
|
||||
#include "IPsolver.hpp"
|
||||
#include "problems.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
|
||||
InteriorPointSolver::InteriorPointSolver(OptProblem * Problem, ParFiniteElementSpace *Vhin)
|
||||
: problem(Problem), block_offsetsumlz(5), block_offsetsuml(4), block_offsetsx(3),
|
||||
saveLogBarrierIterates(false), Vh(Vhin)
|
||||
{
|
||||
tol = 1.e-2;
|
||||
max_iter = 20;
|
||||
mu_k = 1.0;
|
||||
|
||||
sMax = 1.e2;
|
||||
kSig = 1.e10; // control deviation from primal Hessian
|
||||
tauMin = 0.8; // control rate at which iterates can approach the boundary
|
||||
eta = 1.e-4; // backtracking constant
|
||||
thetaMin = 1.e-4; // allowed violation of the equality constraints
|
||||
|
||||
// constants in line-step A-5.4
|
||||
delta = 1.0;
|
||||
sTheta = 1.1;
|
||||
sPhi = 2.3;
|
||||
|
||||
// control the rate at which the penalty parameter is decreased
|
||||
kMu = 0.2;
|
||||
thetaMu = 1.5;
|
||||
|
||||
|
||||
thetaMax = 1.e6; // maximum constraint violation
|
||||
// data for the second order correction
|
||||
kSoc = 0.99;
|
||||
|
||||
// equation (18)
|
||||
gTheta = 1.e-5;
|
||||
gPhi = 1.e-5;
|
||||
|
||||
kEps = 1.e1;
|
||||
|
||||
dimU = problem->GetDimU();
|
||||
dimM = problem->GetDimM();
|
||||
dimC = problem->GetDimC();
|
||||
ckSoc.SetSize(dimC);
|
||||
|
||||
block_offsetsumlz[0] = 0;
|
||||
block_offsetsumlz[1] = dimU; // u
|
||||
block_offsetsumlz[2] = dimM; // m
|
||||
block_offsetsumlz[3] = dimC; // lambda
|
||||
block_offsetsumlz[4] = dimM; // zl
|
||||
block_offsetsumlz.PartialSum();
|
||||
|
||||
for(int i = 0; i < block_offsetsuml.Size(); i++) { block_offsetsuml[i] = block_offsetsumlz[i]; }
|
||||
for(int i = 0; i < block_offsetsx.Size(); i++) { block_offsetsx[i] = block_offsetsuml[i] ; }
|
||||
|
||||
// lower-bound for the inequality constraint m >= ml
|
||||
ml = problem->Getml();
|
||||
|
||||
lk.SetSize(dimC); lk = 0.0;
|
||||
zlk.SetSize(dimM); zlk = 0.0;
|
||||
mf.SetSize(dimM); mf = 0.0;
|
||||
|
||||
linSolver = 0;
|
||||
MyRank = 0;
|
||||
iAmRoot = MyRank == 0 ? true : false;
|
||||
}
|
||||
|
||||
double InteriorPointSolver::MaxStepSize(Vector &x, Vector &xl, Vector &xhat, double tau)
|
||||
{
|
||||
double alphaMaxloc = 1.0;
|
||||
double alphaTmp;
|
||||
for(int i = 0; i < x.Size(); i++)
|
||||
{
|
||||
if( xhat(i) < 0. )
|
||||
{
|
||||
alphaTmp = -1. * tau * (x(i) - xl(i)) / xhat(i);
|
||||
alphaMaxloc = min(alphaMaxloc, alphaTmp);
|
||||
}
|
||||
}
|
||||
|
||||
// alphaMaxloc is the local maximum step size which is
|
||||
// distinct on each MPI process. Need to compute
|
||||
// the global maximum step size
|
||||
double alphaMaxglb;
|
||||
alphaMaxglb = alphaMaxloc;
|
||||
return alphaMaxglb;
|
||||
}
|
||||
|
||||
double InteriorPointSolver::MaxStepSize(Vector &x, Vector &xhat, double tau)
|
||||
{
|
||||
Vector zero(x.Size()); zero = 0.0;
|
||||
return MaxStepSize(x, zero, xhat, tau);
|
||||
}
|
||||
|
||||
|
||||
void InteriorPointSolver::Mult(const Vector &x0, Vector &xf)
|
||||
{
|
||||
BlockVector x0block(block_offsetsx); x0block = 0.0;
|
||||
x0block.GetBlock(0).Set(1.0, x0);
|
||||
// hard coded initialization :(
|
||||
x0block.GetBlock(1) = 1.0;
|
||||
x0block.GetBlock(1).Add(1.0, ml);
|
||||
BlockVector xfblock(block_offsetsx); xfblock = 0.0;
|
||||
Mult(x0block, xfblock);
|
||||
xf.Set(1.0, xfblock.GetBlock(0));
|
||||
mf.Set(1.0, xfblock.GetBlock(1));
|
||||
}
|
||||
|
||||
void InteriorPointSolver::Mult(const BlockVector &x0, BlockVector &xf)
|
||||
{
|
||||
converged = false;
|
||||
IPNewtonKrylovIters.open("IPNewtonKrylovIters.dat", ios::out | ios::trunc);
|
||||
BlockVector xk(block_offsetsx), xhat(block_offsetsx); xk = 0; xhat = 0.0;
|
||||
BlockVector Xk(block_offsetsumlz), Xhat(block_offsetsumlz); Xk = 0.0; Xhat = 0.0;
|
||||
BlockVector Xhatuml(block_offsetsuml); Xhatuml = 0.0;
|
||||
Vector zlhat(dimM); zlhat = 0.0;
|
||||
|
||||
xk.GetBlock(0).Set(1.0, x0.GetBlock(0));
|
||||
xk.GetBlock(1).Set(1.0, x0.GetBlock(1));
|
||||
// running estimate of the final values of the Lagrange multipliers
|
||||
lk = 0.0;
|
||||
zlk = 0.0;
|
||||
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
zlk(i) = 1.e1 * mu_k / (xk(i+dimU) - ml(i));
|
||||
}
|
||||
|
||||
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
|
||||
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
|
||||
Xk.GetBlock(2).Set(1.0, lk);
|
||||
Xk.GetBlock(3).Set(1.0, zlk);
|
||||
|
||||
/* set theta0 = theta(x0)
|
||||
* thetaMin
|
||||
* thetaMax
|
||||
* when theta(xk) < thetaMin and the switching condition holds
|
||||
* then we ask for the Armijo sufficient decrease of the barrier
|
||||
* objective to be satisfied, in order to accept the trial step length alphakl
|
||||
*
|
||||
* thetaMax controls how the filter is initialized for each log-barrier subproblem
|
||||
* F0 = {(th, phi) s.t. th > thetaMax}
|
||||
* that is the filter does not allow for iterates where the constraint violation
|
||||
* is larger than that of thetaMax
|
||||
*/
|
||||
double theta0 = theta(xk);
|
||||
thetaMin = 1.e-4 * max(1.0, theta0);
|
||||
thetaMax = 1.e8 * thetaMin;
|
||||
|
||||
double Eeval, maxBarrierSolves, Eevalmu0;
|
||||
bool printOptimalityError; // control optimality error print to console for log-barrier subproblems
|
||||
|
||||
maxBarrierSolves = 10;
|
||||
|
||||
for(jOpt = 0; jOpt < max_iter; jOpt++)
|
||||
{
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "interior-point solve step " << jOpt << endl;
|
||||
}
|
||||
// A-2. Check convergence of overall optimization problem
|
||||
printOptimalityError = false;
|
||||
Eevalmu0 = E(xk, lk, zlk, printOptimalityError);
|
||||
if(Eevalmu0 < tol)
|
||||
{
|
||||
converged = true;
|
||||
if(iAmRoot)
|
||||
{
|
||||
IPNewtonKrylovIters.close();
|
||||
cout << "solved optimization problem :)\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(jOpt > 0) { maxBarrierSolves = 1; }
|
||||
|
||||
for(int i = 0; i < maxBarrierSolves; i++)
|
||||
{
|
||||
// A-3. Check convergence of the barrier subproblem
|
||||
printOptimalityError = true;
|
||||
Eeval = E(xk, lk, zlk, mu_k, printOptimalityError);
|
||||
if(Eeval < kEps * mu_k)
|
||||
{
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "solved barrier subproblem :), for mu = " << mu_k << endl;
|
||||
}
|
||||
// A-3.1. Recompute the barrier parameter
|
||||
mu_k = max(tol / 10., min(kMu * mu_k, pow(mu_k, thetaMu)));
|
||||
// A-3.2. Re-initialize the filter
|
||||
F1.DeleteAll();
|
||||
F2.DeleteAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// A-4. Compute the search direction
|
||||
// solve for (uhat, mhat, lhat)
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "\n** A-4. IP-Newton solve **\n";
|
||||
}
|
||||
zlhat = 0.0; Xhatuml = 0.0;
|
||||
// why do we have Xhatuml ....???
|
||||
// TO DO: remove Xhatuml in favor of passing Xhat
|
||||
IPNewtonSolve(xk, lk, zlk, zlhat, Xhatuml, mu_k, false);
|
||||
|
||||
|
||||
// assign data stack, X = (u, m, l, zl)
|
||||
Xk = 0.0;
|
||||
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
|
||||
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
|
||||
Xk.GetBlock(2).Set(1.0, lk);
|
||||
Xk.GetBlock(3).Set(1.0, zlk);
|
||||
|
||||
// assign data stack, Xhat = (uhat, mhat, lhat, zlhat)
|
||||
Xhat = 0.0;
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
Xhat.GetBlock(i).Set(1.0, Xhatuml.GetBlock(i));
|
||||
}
|
||||
Xhat.GetBlock(3).Set(1.0, zlhat);
|
||||
|
||||
|
||||
// A-5. Backtracking line search.
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "\n** A-5. Linesearch **\n";
|
||||
cout << "mu = " << mu_k << endl;
|
||||
}
|
||||
lineSearch(Xk, Xhat, mu_k);
|
||||
|
||||
if(lineSearchSuccess)
|
||||
{
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "lineSearch successful :)\n";
|
||||
}
|
||||
if(!switchCondition || !sufficientDecrease)
|
||||
{
|
||||
F1.Append( (1. - gTheta) * thx0);
|
||||
F2.Append( phx0 - gPhi * thx0);
|
||||
}
|
||||
// ----- A-6: Accept the trial point
|
||||
// print info regarding zl...
|
||||
xk.GetBlock(0).Add(alpha, Xhat.GetBlock(0));
|
||||
xk.GetBlock(1).Add(alpha, Xhat.GetBlock(1));
|
||||
lk.Add(alpha, Xhat.GetBlock(2));
|
||||
zlk.Add(alphaz, Xhat.GetBlock(3));
|
||||
projectZ(xk, zlk, mu_k);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "lineSearch not successful :(\n";
|
||||
cout << "attempting feasibility restoration with theta = " << thx0 << endl;
|
||||
cout << "no feasibility restoration implemented, exiting now \n";
|
||||
}
|
||||
break;
|
||||
//cout << "feasibility restoration!!! :( :( :(\n";
|
||||
//problem->feasibilityRestoration(x, 1.e-12);
|
||||
// break;
|
||||
}
|
||||
//
|
||||
if(jOpt + 1 == max_iter && iAmRoot)
|
||||
{
|
||||
cout << "maximum optimization iterations :(\n";
|
||||
IPNewtonKrylovIters.close();
|
||||
}
|
||||
}
|
||||
// done with optimization routine, just reassign data to xf reference so
|
||||
// that the application code has access to the optimal point
|
||||
xf = 0.0;
|
||||
xf.GetBlock(0).Set(1.0, xk.GetBlock(0));
|
||||
xf.GetBlock(1).Set(1.0, xk.GetBlock(1));
|
||||
}
|
||||
|
||||
void InteriorPointSolver::FormIPNewtonMat(BlockVector & x, Vector & l, Vector &zl, BlockOperator &Ak)
|
||||
{
|
||||
// WARNING: Huu, Hum, Hmu, Hmm should all be Hessian terms of the Lagrangian, currently we
|
||||
// them by Hessian terms of the objective function and neglect the Hessian of l^T c
|
||||
|
||||
Huu = problem->Duuf(x); Hum = problem->Dumf(x);
|
||||
Hmu = problem->Dmuf(x); Hmm = problem->Dmmf(x);
|
||||
|
||||
Vector DiagLogBar(dimM); DiagLogBar = 0.0;
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
DiagLogBar(ii) = zl(ii) / (x(ii+dimU) - ml(ii));
|
||||
}
|
||||
if(saveLogBarrierIterates)
|
||||
{
|
||||
std::ofstream diagStream;
|
||||
char diagString[100];
|
||||
snprintf(diagString, 100, "logBarrierHessiandata/D%d.dat", jOpt);
|
||||
diagStream.open(diagString, ios::out | ios::trunc);
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
diagStream << setprecision(30) << DiagLogBar(ii) << endl;
|
||||
}
|
||||
diagStream.close();
|
||||
}
|
||||
|
||||
delete Wmm;
|
||||
if(Hmm != nullptr)
|
||||
{
|
||||
SparseMatrix * D = new SparseMatrix(DiagLogBar);
|
||||
Wmm = Add(*Hmm, *D);
|
||||
delete D;
|
||||
}
|
||||
else
|
||||
{
|
||||
Wmm = new SparseMatrix(DiagLogBar);
|
||||
}
|
||||
|
||||
delete JuT;
|
||||
delete JmT;
|
||||
Ju = problem->Duc(x); JuT = Transpose(*Ju);
|
||||
Jm = problem->Dmc(x); JmT = Transpose(*Jm);
|
||||
|
||||
// IP-Newton system matrix
|
||||
// Ak = [[H_(u,u) H_(u,m) J_u^T]
|
||||
// [H_(m,u) W_(m,m) J_m^T]
|
||||
// [ J_u J_m 0 ]]
|
||||
|
||||
Ak.SetBlock(0, 0, Huu); Ak.SetBlock(0, 2, JuT);
|
||||
Ak.SetBlock(1, 1, Wmm); Ak.SetBlock(1, 2, JmT);
|
||||
Ak.SetBlock(2, 0, Ju); Ak.SetBlock(2, 1, Jm);
|
||||
|
||||
if(Hum != nullptr) { Ak.SetBlock(0, 1, Hum); Ak.SetBlock(1, 0, Hmu); }
|
||||
}
|
||||
|
||||
|
||||
// perturbed KKT system solve
|
||||
// determine the search direction
|
||||
void InteriorPointSolver::IPNewtonSolve(BlockVector &x, Vector &l, Vector &zl, Vector &zlhat, BlockVector &Xhat, double mu, bool socSolve)
|
||||
{
|
||||
// solve A x = b, where A is the IP-Newton matrix
|
||||
BlockOperator A(block_offsetsuml, block_offsetsuml); BlockVector b(block_offsetsuml); b = 0.0;
|
||||
FormIPNewtonMat(x, l, zl, A);
|
||||
|
||||
// [grad_u phi + Ju^T l]
|
||||
// b = - [grad_m phi + Jm^T l]
|
||||
// [ c ]
|
||||
BlockVector gradphi(block_offsetsx); gradphi = 0.0;
|
||||
BlockVector JTl(block_offsetsx); JTl = 0.0;
|
||||
Dxphi(x, mu, gradphi);
|
||||
|
||||
(A.GetBlock(0,2)).Mult(l, JTl.GetBlock(0));
|
||||
(A.GetBlock(1,2)).Mult(l, JTl.GetBlock(1));
|
||||
|
||||
for(int ii = 0; ii < 2; ii++)
|
||||
{
|
||||
b.GetBlock(ii).Set(1.0, gradphi.GetBlock(ii));
|
||||
b.GetBlock(ii).Add(1.0, JTl.GetBlock(ii));
|
||||
}
|
||||
if(!socSolve)
|
||||
{
|
||||
problem->c(x, b.GetBlock(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
b.GetBlock(2).Set(1.0, ckSoc);
|
||||
}
|
||||
b *= -1.0;
|
||||
Xhat = 0.0;
|
||||
|
||||
#ifdef MFEM_USE_SUITESPARSE
|
||||
// Direct solve for IP-Newton saddle-point system
|
||||
// A = [ [ Huu 0 Ju^T]
|
||||
// [ 0 D -I ]
|
||||
// [ Ju -I 0 ]]
|
||||
if(linSolver == 0)
|
||||
{
|
||||
BlockMatrix ABlockMatrix(block_offsetsuml, block_offsetsuml);
|
||||
for(int ii = 0; ii < 3; ii++)
|
||||
{
|
||||
for(int jj = 0; jj < 3; jj++)
|
||||
{
|
||||
if(!A.IsZeroBlock(ii, jj))
|
||||
{
|
||||
ABlockMatrix.SetBlock(ii, jj, dynamic_cast<SparseMatrix *>(&(A.GetBlock(ii, jj))));
|
||||
}
|
||||
}
|
||||
}
|
||||
/* direct solve of the 3x3 IP-Newton linear system */
|
||||
UMFPackSolver ASolver;
|
||||
SparseMatrix *ASparse = ABlockMatrix.CreateMonolithic();
|
||||
ASolver.SetOperator(*ASparse);
|
||||
ASolver.Mult(b, Xhat);
|
||||
|
||||
Vector residual(Xhat.Size());
|
||||
ASparse->Mult(Xhat, residual);
|
||||
residual.Add(-1.0, b);
|
||||
delete ASparse;
|
||||
}
|
||||
else if(linSolver == 1)
|
||||
{
|
||||
// Direct solve for 0,0 Schur complement of IP-Newton system, Huu + Ju^T Wmm Ju,
|
||||
// where Wmm = D for contact problems
|
||||
SparseMatrix * Huuloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 0))));
|
||||
SparseMatrix * Wmmloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(1, 1))));
|
||||
SparseMatrix * Juloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(2, 0))));
|
||||
SparseMatrix * JuTloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 2))));
|
||||
Vector Dvec(dimM); Dvec = 0.0;
|
||||
Vector one(dimM); one = 1.0;
|
||||
Wmmloc->Mult(one, Dvec);
|
||||
SparseMatrix *JuTDJu = Mult_AtDA(*Juloc, Dvec); // Ju^T D Ju
|
||||
SparseMatrix *Areduced = Add(*Huuloc, *JuTDJu); // Huu + Ju^T D Ju
|
||||
|
||||
|
||||
/* prepare the reduced rhs */
|
||||
// breduced = bu + Ju^T (bm + Wmm bl)
|
||||
Vector breduced(dimU); breduced = 0.0;
|
||||
Vector tempVec(dimM); tempVec = 0.0;
|
||||
Wmmloc->Mult(b.GetBlock(2), tempVec);
|
||||
tempVec.Add(1.0, b.GetBlock(1));
|
||||
JuTloc->Mult(tempVec, breduced);
|
||||
breduced.Add(1.0, b.GetBlock(0));
|
||||
|
||||
// solve the reduced linear system
|
||||
UMFPackSolver AreducedSolver;
|
||||
AreducedSolver.SetOperator(*Areduced);
|
||||
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
|
||||
|
||||
// now propagate solved uhat to obtain mhat and lhat
|
||||
// xm = Ju xu - bl
|
||||
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
|
||||
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
|
||||
|
||||
// xl = Wmm xm - bm
|
||||
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
|
||||
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
|
||||
|
||||
delete Wmmloc;
|
||||
delete Huuloc;
|
||||
delete JuTDJu;
|
||||
delete Juloc;
|
||||
delete Areduced;
|
||||
}
|
||||
#else
|
||||
MFEM_VERIFY(linSolver > 1, "linSolver = 0, 1 require MFEM_USE_SUITESPARSE=YES");
|
||||
#endif
|
||||
if (linSolver == 2 || linSolver == 3)
|
||||
{
|
||||
// Iterative solve for 0,0 Schur complement of IP-Newton system, Huu + Ju^T Wmm Ju,
|
||||
// where Wmm = D for contact problems
|
||||
// here the iterative solver is a Jacobi-preconditioned CG-solve
|
||||
SparseMatrix * Huuloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 0))));
|
||||
SparseMatrix * Wmmloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(1, 1))));
|
||||
SparseMatrix * Juloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(2, 0))));
|
||||
SparseMatrix * JuTloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 2))));
|
||||
// Vector Dvec(dimM); Dvec = 0.0;
|
||||
// Vector one(dimM); one = 1.0;
|
||||
// Wmmloc->Mult(one, Dvec);
|
||||
|
||||
// SparseMatrix *JuTDJu = Mult_AtDA(*Juloc, Dvec); // Ju^T D Ju
|
||||
SparseMatrix *JuTDJu = RAP(*Juloc,*Wmmloc,*Juloc); // Ju^T D Ju
|
||||
SparseMatrix *Areduced = Add(*Huuloc, *JuTDJu); // Huu + Ju^T D Ju
|
||||
/* prepare the reduced rhs */
|
||||
// breduced = bu + Ju^T (bm + Wmm bl)
|
||||
Vector breduced(dimU); breduced = 0.0;
|
||||
Vector tempVec(dimM); tempVec = 0.0;
|
||||
Wmmloc->Mult(b.GetBlock(2), tempVec);
|
||||
tempVec.Add(1.0, b.GetBlock(1));
|
||||
JuTloc->Mult(tempVec, breduced);
|
||||
breduced.Add(1.0, b.GetBlock(0));
|
||||
|
||||
/* set up an iterative solver */
|
||||
int globalNumRows = dimU;
|
||||
HYPRE_BigInt rowStarts[2];
|
||||
rowStarts[0] = 0;
|
||||
rowStarts[1] = dimU;
|
||||
HypreParMatrix * Ahypre = new HypreParMatrix(MPI_COMM_WORLD, globalNumRows, rowStarts, Areduced);
|
||||
// CGSolver Asolver(MPI_COMM_WORLD);
|
||||
HyprePCG Asolver(MPI_COMM_WORLD);
|
||||
HypreBoomerAMG * Aprec = new HypreBoomerAMG(*Ahypre);
|
||||
Aprec->SetPrintLevel(0);
|
||||
if(linSolver == 3)
|
||||
{
|
||||
Aprec->SetElasticityOptions(Vh);
|
||||
}
|
||||
Aprec->SetSystemsOptions(3,false);
|
||||
|
||||
Asolver.SetOperator(*Ahypre);
|
||||
Asolver.SetPrintLevel(2);
|
||||
Asolver.SetMaxIter(1000);
|
||||
// Asolver.SetResidualConvergenceOptions();
|
||||
Asolver.SetTol(1.e-6);
|
||||
Asolver.SetPreconditioner(*Aprec);
|
||||
// Asolver.SetResidualConvergenceOptions();
|
||||
|
||||
Asolver.Mult(breduced, Xhat.GetBlock(0));
|
||||
int num_iterations;
|
||||
Asolver.GetNumIterations(num_iterations);
|
||||
cgnum_iterations.Append(num_iterations);
|
||||
// int numNewtonKrylovIters = -1;
|
||||
// numNewtonKrylovIters = Asolver.GetNumIterations();
|
||||
// IPNewtonKrylovIters << numNewtonKrylovIters << endl;
|
||||
|
||||
delete Aprec;
|
||||
delete Ahypre;
|
||||
|
||||
// now propagate solved uhat to obtain mhat and lhat
|
||||
// xm = Ju xu - bl
|
||||
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
|
||||
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
|
||||
|
||||
// // xl = Wmm xm - bm
|
||||
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
|
||||
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
|
||||
|
||||
|
||||
delete Wmmloc;
|
||||
delete Huuloc;
|
||||
delete JuTDJu;
|
||||
delete Juloc;
|
||||
delete Areduced;
|
||||
}
|
||||
else if(linSolver > 2)
|
||||
{
|
||||
// Iterative solve for 0,0 Schur complement of IP-Newton system, Huu + Ju^T Wmm Ju,
|
||||
// where Wmm = D for contact problems
|
||||
// here the iterative solver is a Jacobi-preconditioned CG-solve
|
||||
SparseMatrix * Huuloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 0))));
|
||||
SparseMatrix * Wmmloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(1, 1))));
|
||||
SparseMatrix * Juloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(2, 0))));
|
||||
SparseMatrix * JuTloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 2))));
|
||||
Vector Dvec(dimM); Dvec = 0.0;
|
||||
Vector one(dimM); one = 1.0;
|
||||
Wmmloc->Mult(one, Dvec);
|
||||
SparseMatrix *JuTDJu = Mult_AtDA(*Juloc, Dvec); // Ju^T D Ju
|
||||
SparseMatrix *Areduced = Add(*Huuloc, *JuTDJu); // Huu + Ju^T D Ju
|
||||
|
||||
/* prepare the reduced rhs */
|
||||
// breduced = bu + Ju^T (bm + Wmm bl)
|
||||
Vector breduced(dimU); breduced = 0.0;
|
||||
Vector tempVec(dimM); tempVec = 0.0;
|
||||
Wmmloc->Mult(b.GetBlock(2), tempVec);
|
||||
tempVec.Add(1.0, b.GetBlock(1));
|
||||
JuTloc->Mult(tempVec, breduced);
|
||||
breduced.Add(1.0, b.GetBlock(0));
|
||||
|
||||
/* set up an iterative solver */
|
||||
GSSmoother AreducedPrec((SparseMatrix &)(*Areduced));
|
||||
GMRESSolver AreducedSolver;
|
||||
AreducedSolver.SetOperator(*Areduced);
|
||||
AreducedSolver.SetAbsTol(1.e-12);
|
||||
AreducedSolver.SetRelTol(1.e-8);
|
||||
AreducedSolver.SetMaxIter(500);
|
||||
AreducedSolver.SetPreconditioner(AreducedPrec);
|
||||
AreducedSolver.SetPrintLevel(1);
|
||||
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
|
||||
|
||||
// now propagate solved uhat to obtain mhat and lhat
|
||||
// xm = Ju xu - bl
|
||||
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
|
||||
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
|
||||
|
||||
// xl = Wmm xm - bm
|
||||
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
|
||||
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
|
||||
|
||||
delete Wmmloc;
|
||||
delete Huuloc;
|
||||
delete JuTDJu;
|
||||
delete Juloc;
|
||||
delete Areduced;
|
||||
}
|
||||
|
||||
|
||||
/* backsolve to determine zlhat */
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
zlhat(ii) = -1.*(zl(ii) + (zl(ii) * Xhat(ii + dimU) - mu) / (x(ii + dimU) - ml(ii)) );
|
||||
}
|
||||
}
|
||||
|
||||
// here Xhat, X will be BlockVectors w.r.t. the 4 partitioning X = (u, m, l, zl)
|
||||
|
||||
void InteriorPointSolver::lineSearch(BlockVector& X0, BlockVector& Xhat, double mu)
|
||||
{
|
||||
double tau = max(tauMin, 1.0 - mu);
|
||||
Vector u0 = X0.GetBlock(0);
|
||||
Vector m0 = X0.GetBlock(1);
|
||||
Vector l0 = X0.GetBlock(2);
|
||||
Vector z0 = X0.GetBlock(3);
|
||||
Vector uhat = Xhat.GetBlock(0);
|
||||
Vector mhat = Xhat.GetBlock(1);
|
||||
Vector lhat = Xhat.GetBlock(2);
|
||||
Vector zhat = Xhat.GetBlock(3);
|
||||
double alphaMax = MaxStepSize(m0, ml, mhat, tau);
|
||||
double alphaMaxz = MaxStepSize(z0, zhat, tau);
|
||||
alphaz = alphaMaxz;
|
||||
|
||||
|
||||
BlockVector x0(block_offsetsx); x0 = 0.0;
|
||||
x0.GetBlock(0).Set(1.0, u0);
|
||||
x0.GetBlock(1).Set(1.0, m0);
|
||||
|
||||
BlockVector xhat(block_offsetsx); xhat = 0.0;
|
||||
xhat.GetBlock(0).Set(1.0, uhat);
|
||||
xhat.GetBlock(1).Set(1.0, mhat);
|
||||
|
||||
BlockVector xtrial(block_offsetsx); xtrial = 0.0;
|
||||
BlockVector Dxphi0(block_offsetsx); Dxphi0 = 0.0;
|
||||
int maxBacktrack = 20;
|
||||
alpha = alphaMax;
|
||||
|
||||
|
||||
Vector ck0(dimC); ck0 = 0.0;
|
||||
Vector zhatsoc(dimM); zhatsoc = 0.0;
|
||||
BlockVector Xhatumlsoc(block_offsetsuml); Xhatumlsoc = 0.0;
|
||||
BlockVector xhatsoc(block_offsetsx); xhatsoc = 0.0;
|
||||
Vector uhatsoc(dimU); uhatsoc = 0.0;
|
||||
Vector mhatsoc(dimM); mhatsoc = 0.0;
|
||||
|
||||
Dxphi(x0, mu, Dxphi0);
|
||||
Dxphi0_xhat = InnerProduct(Dxphi0, xhat);
|
||||
double xhat_L2norm = sqrt(InnerProduct(xhat, xhat));
|
||||
double Dxphi_L2norm = sqrt(InnerProduct(Dxphi0, Dxphi0));
|
||||
descentDirection = Dxphi0_xhat < 0. ? true : false;
|
||||
if(descentDirection)
|
||||
{
|
||||
cout << "is a descent direction for the log-barrier objective\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "is not a descent direction for the log-barrier objective\n";
|
||||
}
|
||||
cout << "Dxphi^T xhat / (|| Dxphi ||_2 * || xhat ||_2) = " << Dxphi0_xhat / (xhat_L2norm * Dxphi_L2norm) << endl;
|
||||
thx0 = theta(x0);
|
||||
phx0 = phi(x0, mu);
|
||||
|
||||
lineSearchSuccess = false;
|
||||
for(int i = 0; i < maxBacktrack; i++)
|
||||
{
|
||||
cout << "\n--------- alpha = " << alpha << " ---------\n";
|
||||
|
||||
// ----- A-5.2. Compute trial point: xtrial = x0 + alpha_i xhat
|
||||
xtrial.Set(1.0, x0);
|
||||
xtrial.Add(alpha, xhat);
|
||||
|
||||
// ------ A-5.3. if not in filter region go to A.5.4 otherwise go to A-5.5.
|
||||
thxtrial = theta(xtrial);
|
||||
phxtrial = phi(xtrial, mu);
|
||||
filterCheck(thxtrial, phxtrial);
|
||||
if(!inFilterRegion)
|
||||
{
|
||||
cout << "not in filter region :)\n";
|
||||
// ------ A.5.4: Check sufficient decrease
|
||||
if(!descentDirection)
|
||||
{
|
||||
switchCondition = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
switchCondition = (alpha * pow(abs(Dxphi0_xhat), sPhi) > delta * pow(thx0, sTheta)) ? true : false;
|
||||
}
|
||||
cout << "alpha |Dxphi(x0)^T xhat|^sPhi = " << alpha * pow(abs(Dxphi0_xhat), sPhi) << endl;
|
||||
cout << "delta * theta(x0)^sTheta = " << delta * pow(thx0, sTheta) << endl;
|
||||
cout << "theta(x0) = " << thx0 << ", thetaMin = " << thetaMin << endl;
|
||||
cout << "theta(xtrial) = " << thxtrial << ", (1-gTheta) *theta(x0) = " << (1. - gTheta) * thx0 << endl;
|
||||
cout << "phi(xtrial) = " << phxtrial << ", phi(x0) - gPhi *theta(x0) = " << phx0 - gPhi * thx0 << endl;
|
||||
|
||||
// Case I
|
||||
if(thx0 <= thetaMin && switchCondition)
|
||||
{
|
||||
sufficientDecrease = phxtrial <= phx0 + eta * alpha * Dxphi0_xhat ? true : false;
|
||||
if(sufficientDecrease)
|
||||
{
|
||||
if(iAmRoot) { cout << "A-5.4. Case I -- accepted step length.\n"; }
|
||||
// accept the trial step
|
||||
lineSearchSuccess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(thxtrial <= (1. - gTheta) * thx0 || phxtrial <= phx0 - gPhi * thx0)
|
||||
{
|
||||
if(iAmRoot) { cout << "A-5.4. Case II -- accepted step length.\n"; }
|
||||
// accept the trial step
|
||||
lineSearchSuccess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// A-5.5: Initialize the second-order correction
|
||||
if((!(thx0 < thxtrial)) && i == 0)
|
||||
{
|
||||
cout << "second order correction\n";
|
||||
problem->c(xtrial, ckSoc);
|
||||
problem->c(x0, ck0);
|
||||
ckSoc.Add(alphaMax, ck0);
|
||||
// A-5.6 Compute the second-order correction.
|
||||
IPNewtonSolve(x0, l0, z0, zhatsoc, Xhatumlsoc, mu, true);
|
||||
mhatsoc.Set(1.0, Xhatumlsoc.GetBlock(1));
|
||||
// alphasoc = MaxStepSize(m0, ml, mhatsoc, tau);
|
||||
//WARNING: not complete but currently solver isn't entering this region
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "in filter region :(\n";
|
||||
}
|
||||
|
||||
// include more if needed
|
||||
alpha *= 0.5;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void InteriorPointSolver::projectZ(const Vector &x, Vector &z, double mu)
|
||||
{
|
||||
double zi;
|
||||
double mudivmml;
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
zi = z(i);
|
||||
mudivmml = mu / (x(i + dimU) - ml(i));
|
||||
z(i) = max(min(zi, kSig * mudivmml), mudivmml / kSig);
|
||||
}
|
||||
}
|
||||
|
||||
void InteriorPointSolver::filterCheck(double th, double ph)
|
||||
{
|
||||
inFilterRegion = false;
|
||||
if(th > thetaMax)
|
||||
{
|
||||
inFilterRegion = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < F1.Size(); i++)
|
||||
{
|
||||
if(th >= F1[i] && ph >= F2[i])
|
||||
{
|
||||
inFilterRegion = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double InteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, double mu, bool print)
|
||||
{
|
||||
double E1, E2, E3;
|
||||
double sc, sd;
|
||||
BlockVector gradL(block_offsetsx); gradL = 0.0; // stationarity grad L = grad f + J^T l - z
|
||||
Vector cx(dimC); cx = 0.0; // feasibility c = c(x)
|
||||
Vector comp(dimM); comp = 0.0; // complementarity M Z - mu 1
|
||||
|
||||
DxL(x, l, zl, gradL);
|
||||
E1 = gradL.Normlinf();
|
||||
|
||||
problem->c(x, cx);
|
||||
E2 = cx.Normlinf();
|
||||
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
comp(ii) = x(dimU + ii) * zl(ii) - mu;
|
||||
}
|
||||
E3 = comp.Normlinf();
|
||||
|
||||
double ll1, zl1;
|
||||
zl1 = zl.Norml1() / double(dimC + dimM);
|
||||
ll1 = l.Norml1();
|
||||
sc = max(sMax, zl1 / (double(dimM)) ) / sMax;
|
||||
sd = max(sMax, (ll1 + zl1) / (double(dimC + dimM))) / sMax;
|
||||
if(iAmRoot && print)
|
||||
{
|
||||
cout << "evaluating optimality error for mu = " << mu << endl;
|
||||
cout << "stationarity measure = " << E1 / sd << endl;
|
||||
cout << "feasibility measure = " << E2 << endl;
|
||||
cout << "complimentarity measure = " << E3 / sc << endl;
|
||||
}
|
||||
return max(max(E1 / sd, E2), E3 / sc);
|
||||
}
|
||||
|
||||
double InteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, bool print)
|
||||
{
|
||||
return E(x, l, zl, 0.0, print);
|
||||
}
|
||||
|
||||
double InteriorPointSolver::theta(const BlockVector &x)
|
||||
{
|
||||
Vector cx(dimC); cx = 0.0;
|
||||
problem->c(x, cx);
|
||||
return sqrt(InnerProduct(cx, cx));
|
||||
}
|
||||
|
||||
// log-barrier objective
|
||||
double InteriorPointSolver::phi(const BlockVector &x, double mu)
|
||||
{
|
||||
double fx = problem->CalcObjective(x);
|
||||
double logBarrierLoc = 0.0;
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
logBarrierLoc += log(x(dimU+i)-ml(i));
|
||||
}
|
||||
double logBarrierGlb = 0.0;
|
||||
logBarrierGlb = logBarrierLoc;
|
||||
return fx - mu * logBarrierGlb;
|
||||
}
|
||||
|
||||
|
||||
// gradient of log-barrier objective with respect to x = (u, m)
|
||||
void InteriorPointSolver::Dxphi(const BlockVector &x, double mu, BlockVector &y)
|
||||
{
|
||||
problem->CalcObjectiveGrad(x, y);
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
y(dimU + i) -= mu / (x(dimU + i) - ml(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Lagrangian function evaluation
|
||||
// L(x, l, zl) = f(x) + l^T c(x) - zl^T m
|
||||
double InteriorPointSolver::L(const BlockVector &x, const Vector &l, const Vector &zl)
|
||||
{
|
||||
double fx = problem->CalcObjective(x);
|
||||
Vector cx(dimC); problem->c(x, cx);
|
||||
return (fx + InnerProduct(cx, l) - InnerProduct(x.GetBlock(1), zl));
|
||||
}
|
||||
|
||||
void InteriorPointSolver::DxL(const BlockVector &x, const Vector &l, const Vector &zl, BlockVector &y)
|
||||
{
|
||||
// evaluate the gradient of the objective with respect to the primal variables x = (u, m)
|
||||
BlockVector gradxf(block_offsetsx); gradxf = 0.0;
|
||||
problem->CalcObjectiveGrad(x, gradxf);
|
||||
|
||||
SparseMatrix *Jacu, *Jacm, *JacuT, *JacmT;
|
||||
Jacu = problem->Duc(x); Jacm = problem->Dmc(x);
|
||||
JacuT = Transpose(*Jacu);
|
||||
JacmT = Transpose(*Jacm);
|
||||
JacuT->Mult(l, y.GetBlock(0));
|
||||
JacmT->Mult(l, y.GetBlock(1));
|
||||
delete Jacu; delete JacuT;
|
||||
delete Jacm; delete JacmT;
|
||||
y.Add(1.0, gradxf);
|
||||
(y.GetBlock(1)).Add(-1.0, zl);
|
||||
}
|
||||
|
||||
|
||||
bool InteriorPointSolver::GetConverged() const
|
||||
{
|
||||
return converged;
|
||||
}
|
||||
|
||||
void InteriorPointSolver::SetTol(double Tol)
|
||||
{
|
||||
tol = Tol;
|
||||
}
|
||||
|
||||
void InteriorPointSolver::SetMaxIter(int max_it)
|
||||
{
|
||||
max_iter = max_it;
|
||||
}
|
||||
|
||||
void InteriorPointSolver::SetBarrierParameter(double mu_0)
|
||||
{
|
||||
mu_k = mu_0;
|
||||
}
|
||||
|
||||
void InteriorPointSolver::SaveLogBarrierHessianIterates(bool save)
|
||||
{
|
||||
MFEM_ASSERT(MyRank == 0 || save == false, "currently can only save logbarrier hessian in serial codes");
|
||||
saveLogBarrierIterates = save;
|
||||
}
|
||||
|
||||
void InteriorPointSolver::SetLinearSolver(int LinSolver)
|
||||
{
|
||||
linSolver = LinSolver;
|
||||
}
|
||||
|
||||
|
||||
|
||||
InteriorPointSolver::~InteriorPointSolver()
|
||||
{
|
||||
delete Wmm;
|
||||
delete Huu;
|
||||
delete Hum;
|
||||
delete Hmu;
|
||||
delete Hmm;
|
||||
delete Hum;
|
||||
delete Ju;
|
||||
delete Jm;
|
||||
delete JuT;
|
||||
delete JmT;
|
||||
|
||||
F1.DeleteAll();
|
||||
F2.DeleteAll();
|
||||
block_offsetsx.DeleteAll();
|
||||
block_offsetsumlz.DeleteAll();
|
||||
block_offsetsuml.DeleteAll();
|
||||
ml.SetSize(0);
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
#include "mfem.hpp"
|
||||
#include "problems.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
#ifndef IPSOLVER
|
||||
#define IPSOLVER
|
||||
|
||||
class InteriorPointSolver
|
||||
{
|
||||
protected:
|
||||
OptProblem* problem;
|
||||
double tol;
|
||||
int max_iter;
|
||||
double mu_k; // \mu_k
|
||||
Vector lk, zlk, mf;
|
||||
|
||||
double sMax, kSig, tauMin, eta, thetaMin, delta, sTheta, sPhi, kMu, thetaMu;
|
||||
double thetaMax, kSoc, gTheta, gPhi, kEps;
|
||||
|
||||
// filter
|
||||
Array<double> F1, F2;
|
||||
|
||||
// quantities computed in lineSearch
|
||||
double alpha, alphaz;
|
||||
double thx0, thxtrial;
|
||||
double phx0, phxtrial;
|
||||
bool descentDirection, switchCondition, sufficientDecrease, lineSearchSuccess, inFilterRegion;
|
||||
double Dxphi0_xhat;
|
||||
|
||||
int dimU, dimM, dimC;
|
||||
Array<int> block_offsetsumlz, block_offsetsuml, block_offsetsx;
|
||||
Vector ml;
|
||||
|
||||
Vector ckSoc;
|
||||
SparseMatrix * Huu = nullptr;
|
||||
SparseMatrix * Hum = nullptr;
|
||||
SparseMatrix * Hmu = nullptr;
|
||||
SparseMatrix * Hmm = nullptr;
|
||||
SparseMatrix * Wmm = nullptr;
|
||||
SparseMatrix * Ju = nullptr;
|
||||
SparseMatrix * Jm = nullptr;
|
||||
SparseMatrix * JuT = nullptr;
|
||||
SparseMatrix * JmT = nullptr;;
|
||||
|
||||
int jOpt;
|
||||
bool converged;
|
||||
|
||||
int MyRank;
|
||||
bool iAmRoot;
|
||||
|
||||
bool saveLogBarrierIterates;
|
||||
|
||||
int linSolver;
|
||||
std::ofstream IPNewtonKrylovIters;
|
||||
|
||||
ParFiniteElementSpace *Vh;
|
||||
Array<int> cgnum_iterations;
|
||||
|
||||
|
||||
// not sure if this data is needed or if it can
|
||||
// all be accounted for in the problem class
|
||||
// which variables have equality constraints
|
||||
//Array<int> eqConstrainedVariables;
|
||||
//Array<double> eqConstrainedValues;
|
||||
|
||||
|
||||
|
||||
public:
|
||||
InteriorPointSolver(OptProblem*, ParFiniteElementSpace *);
|
||||
void Mult(const BlockVector& , BlockVector&); // used when the user wants to be aware of bound-constrained variable m >= ml
|
||||
void Mult(const Vector&, Vector &); // useful when the user doesn't need to know about bound-constrained variable m >= ml
|
||||
double MaxStepSize(Vector& , Vector& , Vector& , double);
|
||||
double MaxStepSize(Vector& , Vector& , double);
|
||||
void FormIPNewtonMat(BlockVector& , Vector& , Vector& , BlockOperator &);
|
||||
void IPNewtonSolve(BlockVector& , Vector& , Vector& , Vector&, BlockVector& , double, bool);
|
||||
void lineSearch(BlockVector& , BlockVector& , double);
|
||||
void projectZ(const Vector & , Vector &, double);
|
||||
void filterCheck(double, double);
|
||||
double E(const BlockVector &, const Vector &, const Vector &, double, bool);
|
||||
double E(const BlockVector &, const Vector &, const Vector &, bool);
|
||||
bool GetConverged() const;
|
||||
// TO DO: include Hessian of Lagrangian
|
||||
double theta(const BlockVector &);
|
||||
double phi(const BlockVector &, double);
|
||||
void Dxphi(const BlockVector &, double, BlockVector &);
|
||||
double L(const BlockVector &, const Vector &, const Vector &);
|
||||
void DxL(const BlockVector &, const Vector &, const Vector &, BlockVector &);
|
||||
void SetTol(double);
|
||||
void SetMaxIter(int);
|
||||
void SetBarrierParameter(double);
|
||||
void SaveLogBarrierHessianIterates(bool);
|
||||
void SetLinearSolver(int);
|
||||
Vector GetBoundConstrainedVariable() {return mf;}
|
||||
Array<int> & GetCGIterNumbers() {return cgnum_iterations;}
|
||||
virtual ~InteriorPointSolver();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,17 +0,0 @@
|
||||
# OneProcessAMGContact
|
||||
|
||||
|
||||
|
||||
Be sure to edit the makefile so that it points to a parallel MFEM build
|
||||
|
||||
specifically the MFEM_BUILD_DIR
|
||||
|
||||
|
||||
after building exQPContactBlockTL one can
|
||||
|
||||
1. run the bash script scalingJobArray.bat via `source scalingJobArray.bat' which will populate the CG iterations required to solve
|
||||
various linear systems into the data/ subdirectory
|
||||
2. run the python script data/process.py in order to put the scaling information into the single files algorithmicScaling_Elasticity.dat and algorithmicScaling_noElasticity.dat
|
||||
in order to see the number of average AMG-CG iterations per optimization solve.
|
||||
|
||||
|
||||
@@ -1,274 +0,0 @@
|
||||
// Contact example
|
||||
//
|
||||
// Compile with: make contact
|
||||
//
|
||||
// Sample runs: ./contact -m1 block1.mesh -m2 block2.mesh -at "5 6 7 8"
|
||||
// Sample runs: ./contact -m1 block1_d.mesh -m2 block2_d.mesh -at "5 6 7 8"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "problems.hpp"
|
||||
#include "IPsolver.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Mpi::Init(argc, argv);
|
||||
Hypre::Init();
|
||||
int linSolver = 2;
|
||||
int maxIPMiters = 30;
|
||||
bool iAmRoot = true;
|
||||
int ref_levels = 0;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&linSolver, "-linSolver", "--linearSolver", \
|
||||
"IP-Newton linear system solution strategy.");
|
||||
args.AddOption(&maxIPMiters, "-IPMiters", "--IPMiters",\
|
||||
"Maximum number of IPM iterations");
|
||||
args.AddOption(&ref_levels, "-r", "--mesh_refinement", \
|
||||
"Mesh Refinement");
|
||||
|
||||
|
||||
args.Parse();
|
||||
if(!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( iAmRoot )
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an instance of the nlp
|
||||
ExContactBlockTL * contact = new ExContactBlockTL(ref_levels);
|
||||
int ndofs = contact->GetDimD();
|
||||
int nconstraints = contact->GetDimS();
|
||||
std::ofstream problemDimStream;
|
||||
problemDimStream.open("problemDim.dat", ios::out | ios::trunc);
|
||||
problemDimStream << ndofs << endl;
|
||||
problemDimStream.close();
|
||||
std::ofstream problemDimConstraintsStream;
|
||||
problemDimConstraintsStream.open("problemDimConstraints.dat", ios::out | ios::trunc);
|
||||
problemDimConstraintsStream << nconstraints << endl;
|
||||
problemDimConstraintsStream.close();
|
||||
|
||||
// set up a QP-problem
|
||||
// E(d) = 1 / 2 d^T K d + f^T d
|
||||
// g(d) = J d + g0
|
||||
// where K, J, f and g0 are evaluated at d0 (a valid configuration)
|
||||
|
||||
// to do: seems more appropriate to evaluate at a valid configuration...
|
||||
// that is one where the Dirichlet conditions hold... need to pull
|
||||
// this data from contactBlockTL...
|
||||
Vector d0(ndofs); d0 = 0.0;
|
||||
Array<int> DirichletDofs = contact->GetDirichletDofs();
|
||||
Array<double> DirichletVals = contact->GetDirichletVals();
|
||||
SparseMatrix *K;
|
||||
Vector f(ndofs); f = 0.0;
|
||||
contact->DdE(d0, f); K = contact->DddE(d0);
|
||||
for(int i = 0; i < DirichletDofs.Size(); i++)
|
||||
{
|
||||
d0(DirichletDofs[i]) = DirichletVals[i];
|
||||
}
|
||||
SparseMatrix *J;
|
||||
Vector g0(nconstraints); g0 = 0.0;
|
||||
J = contact->Ddg(d0); contact->g(d0, g0);
|
||||
Vector temp(nconstraints);
|
||||
J->Mult(d0, temp);
|
||||
g0.Add(-1.0, temp);
|
||||
|
||||
// check which rows of the Jacobian are zero!
|
||||
Vector ei(nconstraints); ei = 0.0;
|
||||
Vector JTei(ndofs); JTei = 0.0;
|
||||
|
||||
double normJTei;
|
||||
|
||||
int reduced_nconstraints = 0; // find actual number of constraints
|
||||
|
||||
|
||||
Array<int> nonZeroRows;
|
||||
for(int i = 0; i < nconstraints; i++)
|
||||
{
|
||||
ei(i) = 1.0;
|
||||
J->MultTranspose(ei, JTei);
|
||||
// nullify contributions from Dirichlet constrined dofs
|
||||
for(int j = 0; j < DirichletDofs.Size(); j++)
|
||||
{
|
||||
JTei(DirichletDofs[j]) = 0.0;
|
||||
}
|
||||
normJTei = sqrt(InnerProduct(JTei, JTei));
|
||||
if (normJTei > 1.e-12)
|
||||
{
|
||||
reduced_nconstraints += 1;
|
||||
nonZeroRows.Append(i);
|
||||
}
|
||||
ei(i) = 0.0;
|
||||
}
|
||||
cout << "number of linearized constraints = " << reduced_nconstraints << endl; // 9 constraints
|
||||
|
||||
// remove zero rows of the gap function Jacobian and corresponding gap function entries
|
||||
SparseMatrix * Jreduced = new SparseMatrix(reduced_nconstraints, ndofs);
|
||||
Vector g0reduced(reduced_nconstraints); g0reduced = 0.0;
|
||||
|
||||
|
||||
for(int i = 0; i < reduced_nconstraints; i++)
|
||||
{
|
||||
Array<int> col_tmp;
|
||||
Vector v_tmp; v_tmp = 0.0;
|
||||
J->GetRow(nonZeroRows[i], col_tmp, v_tmp);
|
||||
|
||||
/* obtain subset of columns of the given nonZero Jacobian row that are not Dirichlet constrained */
|
||||
bool freeDof;
|
||||
Array<int> loc_indicies;
|
||||
for(int j = 0; j < col_tmp.Size(); j++)
|
||||
{
|
||||
freeDof = true;
|
||||
for(int k = 0; k < DirichletDofs.Size(); k++)
|
||||
{
|
||||
if(col_tmp[j] == DirichletDofs[k])
|
||||
{
|
||||
freeDof = false;
|
||||
}
|
||||
}
|
||||
if(freeDof)
|
||||
{
|
||||
loc_indicies.Append(j);
|
||||
}
|
||||
}
|
||||
|
||||
Array<int> col_tmp_reduced(loc_indicies.Size());
|
||||
Vector v_tmp_reduced(loc_indicies.Size());
|
||||
for(int j = 0; j < loc_indicies.Size(); j++)
|
||||
{
|
||||
col_tmp_reduced[j] = col_tmp[loc_indicies[j]];
|
||||
v_tmp_reduced(j) = v_tmp(loc_indicies[j]);
|
||||
}
|
||||
|
||||
Jreduced->SetRow(i, col_tmp_reduced, v_tmp_reduced);
|
||||
g0reduced(i) = g0(nonZeroRows[i]);
|
||||
}
|
||||
|
||||
|
||||
QPContactProblem *QPContact = new QPContactProblem(*K, *Jreduced, f, g0reduced);
|
||||
|
||||
Mesh * mesh1 = new Mesh("meshes/block1.mesh", 1, 1);
|
||||
Mesh * mesh2 = new Mesh("meshes/rotatedblock2.mesh", 1, 1);
|
||||
for(int i = 0; i < ref_levels; i++)
|
||||
{
|
||||
mesh1->UniformRefinement();
|
||||
mesh2->UniformRefinement();
|
||||
}
|
||||
|
||||
int numMeshes = 2;
|
||||
Mesh *meshArray[numMeshes];
|
||||
meshArray[0] = mesh1;
|
||||
meshArray[1] = mesh2;
|
||||
Mesh mesh(meshArray, numMeshes);
|
||||
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
H1_FECollection fec(1, mesh.Dimension());
|
||||
ParFiniteElementSpace fespace(&pmesh, &fec, mesh.Dimension(), Ordering::byVDIM);
|
||||
|
||||
InteriorPointSolver * QPContactOptimizer = new InteriorPointSolver(QPContact, &fespace);
|
||||
QPContactOptimizer->SetTol(1.e-6);
|
||||
QPContactOptimizer->SetLinearSolver(linSolver);
|
||||
QPContactOptimizer->SetMaxIter(50);
|
||||
Vector x0(ndofs); x0 = 0.0;
|
||||
for(int i = 0; i < DirichletDofs.Size(); i++)
|
||||
{
|
||||
x0(DirichletDofs[i]) = DirichletVals[i];
|
||||
}
|
||||
Vector xf(ndofs); xf = 0.0;
|
||||
QPContactOptimizer->Mult(x0, xf);
|
||||
|
||||
double Einitial = QPContact->E(x0);
|
||||
double Efinal = QPContact->E(xf);
|
||||
cout << "Energy objective at initial point = " << Einitial << endl;
|
||||
cout << "Energy objective at QP optimizer = " << Efinal << endl;
|
||||
QPContactOptimizer->GetCGIterNumbers().Print(mfem::out, 20);
|
||||
MFEM_VERIFY(QPContactOptimizer->GetConverged(), "Interior point solver did not converge.");
|
||||
|
||||
|
||||
//Mesh * mesh1 = new Mesh("meshes/block1.mesh", 1, 1);
|
||||
//Mesh * mesh2 = new Mesh("meshes/rotatedblock2.mesh", 1, 1);
|
||||
//for(int i = 0; i < ref_levels; i++)
|
||||
//{
|
||||
// mesh1->UniformRefinement();
|
||||
// mesh2->UniformRefinement();
|
||||
//}
|
||||
//int gdim = mesh1->Dimension();
|
||||
//FiniteElementCollection * fec = new H1_FECollection(1, gdim);
|
||||
//FiniteElementSpace * fespace1 = new FiniteElementSpace(mesh1, fec, gdim, Ordering::byVDIM);
|
||||
//FiniteElementSpace * fespace2 = new FiniteElementSpace(mesh2, fec, gdim, Ordering::byVDIM);
|
||||
//
|
||||
//GridFunction x1_gf(fespace1);
|
||||
//GridFunction x2_gf(fespace2);
|
||||
|
||||
//int ndof1 = fespace1->GetTrueVSize();
|
||||
//int ndof2 = fespace2->GetTrueVSize();
|
||||
//int ndof = ndof1 + ndof2;
|
||||
//for(int i = 0; i < ndof1; i++)
|
||||
//{
|
||||
// x1_gf(i) = xf(i);
|
||||
//}
|
||||
//for(int i = ndof1; i < ndof; i++)
|
||||
//{
|
||||
// x2_gf(i - ndof1) = xf(i);
|
||||
//}
|
||||
|
||||
//mesh1->SetNodalFESpace(fespace1);
|
||||
//mesh2->SetNodalFESpace(fespace2);
|
||||
//GridFunction *nodes1 = mesh1->GetNodes();
|
||||
//GridFunction *nodes2 = mesh2->GetNodes();
|
||||
|
||||
//{
|
||||
// *nodes1 += x1_gf;
|
||||
// *nodes2 += x2_gf;
|
||||
//}
|
||||
//
|
||||
|
||||
//ParaViewDataCollection paraview_dc1("QPContactBody1", mesh1);
|
||||
//paraview_dc1.SetPrefixPath("ParaView");
|
||||
//paraview_dc1.SetLevelsOfDetail(1);
|
||||
//paraview_dc1.SetDataFormat(VTKFormat::BINARY);
|
||||
//paraview_dc1.SetHighOrderOutput(true);
|
||||
//paraview_dc1.SetCycle(0);
|
||||
//paraview_dc1.SetTime(0.0);
|
||||
//paraview_dc1.RegisterField("Body1", &x1_gf);
|
||||
//paraview_dc1.Save();
|
||||
//
|
||||
//ParaViewDataCollection paraview_dc2("QPContactBody2", mesh2);
|
||||
//paraview_dc2.SetPrefixPath("ParaView");
|
||||
//paraview_dc2.SetLevelsOfDetail(1);
|
||||
//paraview_dc2.SetDataFormat(VTKFormat::BINARY);
|
||||
//paraview_dc2.SetHighOrderOutput(true);
|
||||
//paraview_dc2.SetCycle(0);
|
||||
//paraview_dc2.SetTime(0.0);
|
||||
//paraview_dc2.RegisterField("Body2", &x2_gf);
|
||||
//paraview_dc2.Save();
|
||||
|
||||
//delete fespace1;
|
||||
//delete fespace2;
|
||||
//delete fec;
|
||||
//delete mesh1;
|
||||
//delete mesh2;
|
||||
|
||||
delete QPContact;
|
||||
delete QPContactOptimizer;
|
||||
|
||||
delete K;
|
||||
delete J;
|
||||
delete Jreduced;
|
||||
delete contact;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
SRC = ./
|
||||
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
# Remove built-in rule
|
||||
#%: %.cpp
|
||||
|
||||
exQPContactBlockTL: exQPContactBlockTL.o problems.o IPsolver.o $(MFEM_LIB_FILE)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) exQPContactBlockTL.o problems.o IPsolver.o -o $@ $(MFEM_LIBS)
|
||||
|
||||
|
||||
|
||||
exQPContactBlockTL.o: exQPContactBlockTL.cpp $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
|
||||
|
||||
problems.o: problems.cpp $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
|
||||
|
||||
IPsolver.o: IPsolver.cpp $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
|
||||
|
||||
# Generate an error message if the MFEM library is not built and exit
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *.o exQPContactBlockTL
|
||||
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
elements
|
||||
9
|
||||
1 5 0 1 3 2 8 9 11 10
|
||||
1 5 2 3 5 4 10 11 13 12
|
||||
1 5 4 5 7 6 12 13 15 14
|
||||
1 5 8 9 11 10 16 17 19 18
|
||||
1 5 10 11 13 12 18 19 21 20
|
||||
1 5 12 13 15 14 20 21 23 22
|
||||
1 5 16 17 19 18 24 25 27 26
|
||||
1 5 18 19 21 20 26 27 29 28
|
||||
1 5 20 21 23 22 28 29 31 30
|
||||
|
||||
|
||||
|
||||
# 0 nothing
|
||||
# 1 dirichlet bc
|
||||
# 2 contact
|
||||
boundary
|
||||
30
|
||||
1 3 1 0 2 3
|
||||
1 3 3 2 4 5
|
||||
1 3 5 4 6 7
|
||||
1 3 24 25 27 26
|
||||
1 3 26 27 29 28
|
||||
1 3 28 29 31 30
|
||||
2 3 2 0 8 10
|
||||
2 3 4 2 10 12
|
||||
2 3 6 4 12 14
|
||||
2 3 10 8 16 18
|
||||
2 3 12 10 18 20
|
||||
2 3 14 12 20 22
|
||||
2 3 18 16 24 26
|
||||
2 3 20 18 26 28
|
||||
2 3 22 20 28 30
|
||||
3 3 1 3 11 9
|
||||
3 3 3 5 13 11
|
||||
3 3 5 7 15 13
|
||||
3 3 9 11 19 17
|
||||
3 3 11 13 21 19
|
||||
3 3 13 15 23 21
|
||||
3 3 17 19 27 25
|
||||
3 3 19 21 29 27
|
||||
3 3 21 23 31 29
|
||||
1 3 8 0 1 9
|
||||
1 3 16 8 9 17
|
||||
1 3 24 16 17 25
|
||||
1 3 6 14 15 7
|
||||
1 3 14 22 23 15
|
||||
1 3 22 30 31 23
|
||||
|
||||
|
||||
vertices
|
||||
32
|
||||
3
|
||||
-1.0000 0 0
|
||||
0 0 0
|
||||
-1.0000 0.3000 0
|
||||
0 0.3000 0
|
||||
-1.0000 0.6500 0
|
||||
0 0.6500 0
|
||||
-1.0000 1.0000 0
|
||||
0 1.0000 0
|
||||
-1.0000 0 0.3000
|
||||
0 0 0.3000
|
||||
-1.0000 0.3000 0.3500
|
||||
0 0.3000 0.3500
|
||||
-1.0000 0.6500 0.3000
|
||||
0 0.6500 0.3000
|
||||
-1.0000 1.0000 0.3000
|
||||
0 1.0000 0.3000
|
||||
-1.0000 0 0.6500
|
||||
0 0 0.6500
|
||||
-1.0000 0.3000 0.6500
|
||||
0 0.3000 0.6500
|
||||
-1.0000 0.6500 0.6500
|
||||
0 0.6500 0.6500
|
||||
-1.0000 1.0000 0.6500
|
||||
0 1.0000 0.6500
|
||||
-1.0000 0 1.0000
|
||||
0 0 1.0000
|
||||
-1.0000 0.3000 1.0000
|
||||
0 0.3000 1.0000
|
||||
-1.0000 0.6500 1.0000
|
||||
0 0.6500 1.0000
|
||||
-1.0000 1.0000 1.0000
|
||||
0 1.0000 1.0000
|
||||
@@ -1,70 +0,0 @@
|
||||
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
# 1 nothing
|
||||
elements
|
||||
4
|
||||
1 5 0 1 3 2 6 7 9 8
|
||||
1 5 2 3 5 4 8 9 11 10
|
||||
1 5 6 7 9 8 12 13 15 14
|
||||
1 5 8 9 11 10 14 15 17 16
|
||||
|
||||
# 0 nothing
|
||||
# 1 dirichlet bc
|
||||
# 2 contact
|
||||
boundary
|
||||
16
|
||||
1 3 1 0 2 3
|
||||
1 3 3 2 4 5
|
||||
1 3 12 13 15 14
|
||||
1 3 14 15 17 16
|
||||
3 3 2 0 6 8
|
||||
3 3 4 2 8 10
|
||||
3 3 8 6 12 14
|
||||
3 3 10 8 14 16
|
||||
2 3 1 3 9 7
|
||||
2 3 3 5 11 9
|
||||
2 3 7 9 15 13
|
||||
2 3 9 11 17 15
|
||||
1 3 6 0 1 7
|
||||
1 3 12 6 7 13
|
||||
1 3 4 10 11 5
|
||||
1 3 10 16 17 11
|
||||
|
||||
vertices
|
||||
18
|
||||
3
|
||||
|
||||
0.000000000000 0.145770950245 0.443895630208
|
||||
0.507100000000 0.145770950245 0.443895630208
|
||||
0.000000000000 0.350937660019 0.294833290227
|
||||
0.507100000000 0.350937660019 0.294833290227
|
||||
0.000000000000 0.556104369792 0.145770950245
|
||||
0.507100000000 0.556104369792 0.145770950245
|
||||
0.000000000000 0.294833290227 0.649062339981
|
||||
0.507100000000 0.294833290227 0.649062339981
|
||||
0.000000000000 0.500000000000 0.500000000000
|
||||
0.507100000000 0.500000000000 0.500000000000
|
||||
0.000000000000 0.705166709773 0.350937660019
|
||||
0.507100000000 0.705166709773 0.350937660019
|
||||
0.000000000000 0.443895630208 0.854229049755
|
||||
0.507100000000 0.443895630208 0.854229049755
|
||||
0.000000000000 0.649062339981 0.705166709773
|
||||
0.507100000000 0.649062339981 0.705166709773
|
||||
0.000000000000 0.854229049755 0.556104369792
|
||||
0.507100000000 0.854229049755 0.556104369792
|
||||
@@ -1,897 +0,0 @@
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
void BasisEval(const Vector xi, Vector &N, DenseMatrix &dNdxi) // dNdxi is 2*4
|
||||
{
|
||||
N[0] = 0.25*(1-xi[0])*(1-xi[1]);
|
||||
N[1] = 0.25*(1+xi[0])*(1-xi[1]);
|
||||
N[2] = 0.25*(1+xi[0])*(1+xi[1]);
|
||||
N[3] = 0.25*(1-xi[0])*(1+xi[1]);
|
||||
|
||||
dNdxi(0,0) = 0.25*(-1+xi[1]);
|
||||
dNdxi(0,1) = 0.25*(1-xi[1]);
|
||||
dNdxi(0,2) = 0.25*(1+xi[1]);
|
||||
dNdxi(0,3) = 0.25*(-1-xi[1]);
|
||||
dNdxi(1,0) = 0.25*(-1+xi[0]);
|
||||
dNdxi(1,1) = 0.25*(-1-xi[0]);
|
||||
dNdxi(1,2) = 0.25*(1+xi[0]);
|
||||
dNdxi(1,3) = 0.25*(1-xi[0]);
|
||||
}
|
||||
|
||||
|
||||
void BasisEvalDerivs(const Vector xi, Vector& N, DenseMatrix& dNdxi,
|
||||
DenseMatrix& dN2dxi)
|
||||
{
|
||||
N[0] = 0.25*(1-xi[0])*(1-xi[1]);
|
||||
N[1] = 0.25*(1+xi[0])*(1-xi[1]);
|
||||
N[2] = 0.25*(1+xi[0])*(1+xi[1]);
|
||||
N[3] = 0.25*(1-xi[0])*(1+xi[1]);
|
||||
|
||||
dNdxi.SetSize(2,4); dNdxi = 0.0;
|
||||
dN2dxi.SetSize(3,4);
|
||||
dN2dxi = 0.0; // first row dxi2, second detadxi, third deta2
|
||||
|
||||
dNdxi(0,0) = 0.25*(-1+xi[1]); dNdxi(0,1) = 0.25*(1-xi[1]);
|
||||
dNdxi(0,2) = 0.25*(1+xi[1]); dNdxi(0,3) = 0.25*(-1-xi[1]);
|
||||
dNdxi(1,0) = 0.25*(-1+xi[0]); dNdxi(1,1) = 0.25*(-1-xi[0]);
|
||||
dNdxi(1,2) = 0.25*(1+xi[0]); dNdxi(1,3) = 0.25*(1-xi[0]);
|
||||
|
||||
dN2dxi(1,0) = 0.25; dN2dxi(1,1) = -0.25; dN2dxi(1,2) = 0.25;
|
||||
dN2dxi(1,3) = -0.25;
|
||||
}
|
||||
|
||||
// returns the vector and matrix form of the shape functions and its derivative
|
||||
void BasisVectorDerivs(const Vector xi, DenseMatrix& N, DenseMatrix& dNdxi,
|
||||
DenseMatrix& ddNdxi)
|
||||
{
|
||||
N.SetSize(3,12); N = 0.0;
|
||||
N(0,0) = 0.25*(1-xi[0])*(1-xi[1]); N(0,3) = 0.25*(1+xi[0])*(1-xi[1]);
|
||||
N(0,6) = 0.25*(1+xi[0])*(1+xi[1]); N(0,9) = 0.25*(1-xi[0])*(1+xi[1]);
|
||||
|
||||
N(1,1) = 0.25*(1-xi[0])*(1-xi[1]); N(1,4) = 0.25*(1+xi[0])*(1-xi[1]);
|
||||
N(1,7) = 0.25*(1+xi[0])*(1+xi[1]); N(1,10) = 0.25*(1-xi[0])*(1+xi[1]);
|
||||
|
||||
N(2,2) = 0.25*(1-xi[0])*(1-xi[1]); N(2,5) = 0.25*(1+xi[0])*(1-xi[1]);
|
||||
N(2,8) = 0.25*(1+xi[0])*(1+xi[1]); N(2,11) = 0.25*(1-xi[0])*(1+xi[1]);
|
||||
|
||||
dNdxi.SetSize(3*2, 3*4); dNdxi = 0.0;
|
||||
dNdxi(0,0) = 0.25*(-1+xi[1]); dNdxi(0,3) = 0.25*(1-xi[1]);
|
||||
dNdxi(0,6) = 0.25*(1+xi[1]); dNdxi(0,9) = 0.25*(-1-xi[1]);
|
||||
dNdxi(1,1) = 0.25*(-1+xi[1]); dNdxi(1,4) = 0.25*(1-xi[1]);
|
||||
dNdxi(1,7) = 0.25*(1+xi[1]); dNdxi(1,10) = 0.25*(-1-xi[1]);
|
||||
dNdxi(2,2) = 0.25*(-1+xi[1]); dNdxi(2,5) = 0.25*(1-xi[1]);
|
||||
dNdxi(2,8) = 0.25*(1+xi[1]); dNdxi(2,11) = 0.25*(-1-xi[1]);
|
||||
|
||||
dNdxi(3,0) = 0.25*(-1+xi[0]); dNdxi(3,3) = 0.25*(-1-xi[0]);
|
||||
dNdxi(3,6) = 0.25*(1+xi[0]); dNdxi(3,9) = 0.25*(1-xi[0]);
|
||||
dNdxi(4,1) = 0.25*(-1+xi[0]); dNdxi(4,4) = 0.25*(-1-xi[0]);
|
||||
dNdxi(4,7) = 0.25*(1+xi[0]); dNdxi(4,10) = 0.25*(1-xi[0]);
|
||||
dNdxi(5,2) = 0.25*(-1+xi[0]); dNdxi(5,5) = 0.25*(-1-xi[0]);
|
||||
dNdxi(5,8) = 0.25*(1+xi[0]); dNdxi(5,11) = 0.25*(1-xi[0]);
|
||||
|
||||
ddNdxi.SetSize(3*4, 3*4); ddNdxi = 0.0;
|
||||
ddNdxi(3,0) = 0.25; ddNdxi(3,3) = -0.25;
|
||||
ddNdxi(3,6) = 0.25; ddNdxi(3,9) = -0.25;
|
||||
ddNdxi(4,1) = 0.25; ddNdxi(4,4) = -0.25;
|
||||
ddNdxi(4,7) = 0.25; ddNdxi(4,10) = -0.25;
|
||||
ddNdxi(5,2) = 0.25; ddNdxi(5,5) = -0.25;
|
||||
ddNdxi(5,8) = 0.25; ddNdxi(5,11) = -0.25;
|
||||
|
||||
ddNdxi(6,0) = 0.25; ddNdxi(6,3) = -0.25;
|
||||
ddNdxi(6,6) = 0.25; ddNdxi(6,9) = -0.25;
|
||||
ddNdxi(7,1) = 0.25; ddNdxi(7,4) = -0.25;
|
||||
ddNdxi(7,7) = 0.25; ddNdxi(7,10) = -0.25;
|
||||
ddNdxi(8,2) = 0.25; ddNdxi(8,5) = -0.25;
|
||||
ddNdxi(8,8) = 0.25; ddNdxi(8,11) = -0.25;
|
||||
}
|
||||
|
||||
|
||||
void cross(const Vector a, const Vector b, Vector& c)
|
||||
{
|
||||
assert(a.Size()==3);
|
||||
c.SetSize(3);
|
||||
c[0] = a[1]*b[2] - a[2]*b[1];
|
||||
c[1] = -a[0]*b[2] + b[0]*a[2];
|
||||
c[2] = a[0]*b[1] - a[1]*b[0];
|
||||
|
||||
}
|
||||
// a outer b
|
||||
void outer(const Vector a, const Vector b, DenseMatrix& c)
|
||||
{
|
||||
int m = a.Size();
|
||||
int n = b.Size();
|
||||
assert(c.Height()==m);
|
||||
assert(c.Width() ==n);
|
||||
for (int i=0; i<m; i++)
|
||||
{
|
||||
for (int j=0; j<n; j++)
|
||||
{
|
||||
c(i,j) = a[i]*b[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
// dphidxi 2*4
|
||||
// coords 4*3
|
||||
void ComputeNormal(const DenseMatrix& dphidxi, const DenseMatrix& coords,
|
||||
Vector& normal, double& nnorm)
|
||||
{
|
||||
|
||||
DenseMatrix dxdxi(2,3);
|
||||
Mult(dphidxi, coords, dxdxi);
|
||||
Vector dxdxi1(3);
|
||||
Vector dxdxi2(3);
|
||||
|
||||
dxdxi.GetRow(0,dxdxi1);
|
||||
dxdxi.GetRow(1,dxdxi2);
|
||||
|
||||
cross(dxdxi1, dxdxi2, normal); // is there a cross product? no
|
||||
// VectorCrossProductCoefficient::Eval has hard-coded cross product
|
||||
nnorm = normal.Norml2( );
|
||||
normal /= nnorm;
|
||||
}
|
||||
|
||||
void SlaveToMaster(const DenseMatrix& m_coords, const Vector& s_x, Vector& xi)
|
||||
{
|
||||
bool converged = false;
|
||||
bool pt_on_elem = false;
|
||||
int dim = 3;
|
||||
xi.SetSize(dim-1);
|
||||
xi = 0.0;
|
||||
int max_iter = 15;
|
||||
double off_el_xi = 1e-2;
|
||||
double proj_newton_tol = 1e-13;
|
||||
double proj_max_gap = 0.5;
|
||||
Vector gap_v(dim);
|
||||
// warm start from linear solution
|
||||
|
||||
for (int it=0; it<max_iter; it++)
|
||||
{
|
||||
//cout<<it<<endl;
|
||||
Vector m_N(4);
|
||||
m_N = 0.;
|
||||
DenseMatrix m_dN(2,4);
|
||||
m_dN = 0.;
|
||||
DenseMatrix m_dN2(3,4);
|
||||
m_dN2 = 0.;
|
||||
BasisEvalDerivs(xi, m_N, m_dN, m_dN2);
|
||||
|
||||
Vector x_c(dim);
|
||||
m_coords.MultTranspose(m_N, x_c);
|
||||
|
||||
gap_v = s_x;
|
||||
gap_v -= x_c;
|
||||
|
||||
DenseMatrix m_dx(2,3);
|
||||
m_dx = 0.;
|
||||
Mult(m_dN, m_coords, m_dx);
|
||||
|
||||
Vector r(dim-1);
|
||||
r = 0.0;
|
||||
m_dx.Mult(gap_v, r);
|
||||
|
||||
if (r.Normlinf() < proj_newton_tol)
|
||||
{
|
||||
converged = true;
|
||||
break;
|
||||
}
|
||||
|
||||
DenseMatrix drdxi(dim-1,dim-1);
|
||||
drdxi = 0.;
|
||||
MultABt(m_dx, m_dx, drdxi); // m_dx * m_dx.T
|
||||
drdxi *= -1.0;
|
||||
|
||||
DenseMatrix m_dx2(3,3); m_dx2 = 0.0;
|
||||
Mult(m_dN2,m_coords, m_dx2);
|
||||
|
||||
//m_d2x = m_dN(:,:,2) * m_elem_coords(1:4,:); //m_dN(:,:,2) is 3*4
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
DenseMatrix Mtemp(2,2); Mtemp = 0.0;
|
||||
Mtemp(0,0) = m_dx2(0,d); Mtemp(0,1) = m_dx2(1,d);
|
||||
Mtemp(1,0) = m_dx2(1,d); Mtemp(1,1) = m_dx2(2,d);
|
||||
|
||||
drdxi.Add(gap_v[d], Mtemp);
|
||||
}
|
||||
|
||||
//cond_num = rcond(drdxi); condition number?
|
||||
//drdxi.TestInversion();
|
||||
DenseMatrixInverse drdxi_inv(drdxi);
|
||||
Vector xi_tmp(dim-1);
|
||||
|
||||
drdxi_inv.Mult(r,xi_tmp);
|
||||
xi -= xi_tmp;
|
||||
}
|
||||
if (!converged)
|
||||
{
|
||||
xi = 0.0;
|
||||
}
|
||||
off_el_xi += 1 ; // tolerance of offset of xi outside [-1,1]
|
||||
|
||||
//cout<<gap_v.Norml2()<<" " <<xi.Normlinf()<<endl;
|
||||
//
|
||||
// Discuss with Frank... what is happening here
|
||||
if (gap_v.Norml2() < proj_max_gap && xi.Normlinf() <= off_el_xi)
|
||||
{
|
||||
pt_on_elem = true;
|
||||
}
|
||||
|
||||
if (pt_on_elem)
|
||||
{
|
||||
//cout << "convergence of node to segment projection? " << converged << endl;
|
||||
//for(int i = 0; i < 2; i++)
|
||||
//{
|
||||
// cout << "xi_" << i << " = " << xi(i) << endl;
|
||||
//}
|
||||
}
|
||||
MFEM_VERIFY(pt_on_elem == true, "xi went out of bounds");
|
||||
MFEM_VERIFY(converged == true, "projection didn't converge");
|
||||
}
|
||||
|
||||
|
||||
|
||||
// m_coords is expected to be 4 * 3
|
||||
void ComputeGapJacobian(const Vector x_s, const Vector xi,
|
||||
const DenseMatrix m_coords,
|
||||
double& gap, Vector& normal, Vector& dgdxm, Vector& dgdxs)
|
||||
{
|
||||
Vector m_N(4);
|
||||
DenseMatrix m_dN(2,4);
|
||||
DenseMatrix m_dN2(3,4);
|
||||
BasisEvalDerivs(xi, m_N, m_dN, m_dN2);
|
||||
|
||||
Vector x_c(3);
|
||||
m_coords.MultTranspose(m_N, x_c);
|
||||
|
||||
Vector gap_v(3); gap_v = 0.0;
|
||||
gap_v = x_s;
|
||||
gap_v -= x_c;
|
||||
|
||||
DenseMatrix m_dx(2,3);
|
||||
Mult(m_dN, m_coords, m_dx);
|
||||
|
||||
double nnorm = 0;
|
||||
ComputeNormal(m_dN, m_coords, normal, nnorm);
|
||||
|
||||
gap = gap_v * normal; // gap function value, dot product between vectors
|
||||
|
||||
//dr_dx = zeros(2,4,3); % nsegment, nodes in quad, ndim
|
||||
|
||||
DenseMatrix dr_dx_res1(4,3); dr_dx_res1 = 0.;
|
||||
DenseMatrix dr_dx_res2(4,3); dr_dx_res2 = 0.;
|
||||
|
||||
Vector m_dxrow1(3);
|
||||
m_dx.GetRow(0, m_dxrow1);
|
||||
outer(m_N, m_dxrow1, dr_dx_res1);// 4*1 times 1*3
|
||||
dr_dx_res1 *= -1.0;
|
||||
|
||||
Vector m_dxrow2(3);
|
||||
m_dx.GetRow(1, m_dxrow2);
|
||||
outer(m_N, m_dxrow2, dr_dx_res2);// 4*1 times 1*3
|
||||
dr_dx_res2 *= -1.0;
|
||||
|
||||
Vector m_dNrow1(4); m_dN.GetRow(0, m_dNrow1);
|
||||
Vector m_dNrow2(4); m_dN.GetRow(1, m_dNrow2);
|
||||
|
||||
DenseMatrix dr_dx_res1_tmp(4,3); dr_dx_res1_tmp = 0.;
|
||||
DenseMatrix dr_dx_res2_tmp(4,3); dr_dx_res2_tmp = 0.;
|
||||
outer(m_dNrow1, gap_v, dr_dx_res1_tmp);// 4*1 times 1*3
|
||||
outer(m_dNrow2, gap_v, dr_dx_res2_tmp);// 4*1 times 1*3
|
||||
|
||||
dr_dx_res1 += dr_dx_res1_tmp; // outer product in vector?
|
||||
dr_dx_res2 += dr_dx_res2_tmp;
|
||||
|
||||
|
||||
DenseMatrix K_dxidx1(2,2); // 2*2
|
||||
K_dxidx1 = 0.;
|
||||
MultABt(m_dx, m_dx, K_dxidx1); // m_dx * m_dx.T
|
||||
|
||||
Vector v_dxidx2(4);
|
||||
m_coords.Mult(gap_v, v_dxidx2); // m_coords * gap_v; // 4*3 * 3 = 4
|
||||
|
||||
DenseMatrix K_dxidx2(2,2); K_dxidx2 = 0.0;
|
||||
|
||||
Vector m_dN2row1(4); m_dN2.GetRow(0, m_dN2row1);
|
||||
Vector m_dN2row2(4); m_dN2.GetRow(1, m_dN2row2);
|
||||
Vector m_dN2row3(4); m_dN2.GetRow(2, m_dN2row3);
|
||||
// how to get 2nd order? multidimensional matrix?
|
||||
K_dxidx2(0,0) = m_dN2row1 * v_dxidx2; // how would 4*1 * 1*4 be computed?
|
||||
K_dxidx2(0,1) = m_dN2row2 * v_dxidx2;
|
||||
K_dxidx2(1,0) = m_dN2row2 * v_dxidx2;
|
||||
K_dxidx2(1,1) = m_dN2row3 * v_dxidx2;
|
||||
|
||||
DenseMatrix K_dxidx(2,2);
|
||||
K_dxidx -= K_dxidx1;
|
||||
K_dxidx += K_dxidx2;
|
||||
|
||||
// resize the vectors and matrices
|
||||
Vector dxidx(24); dxidx = 0.0;
|
||||
Vector drdx_r(24); drdx_r = 0.0;
|
||||
|
||||
for (int i=0; i<4; i++)
|
||||
{
|
||||
for (int j=0; j<3; j++)
|
||||
{
|
||||
drdx_r[4*j+i] = dr_dx_res1(i,j);
|
||||
drdx_r[4*j+i+12] = dr_dx_res2(i,j);
|
||||
|
||||
}
|
||||
}
|
||||
//drdx_r(1:4*3,1) = reshape(dr_dx_res(:,:,1),4*3,1);
|
||||
//drdx_r(4*3+1:2*4*3,1) = reshape(dr_dx_res(:,:,2),4*3,1);
|
||||
DenseMatrix drdx_K(24,24); drdx_K = 0.;
|
||||
for (int i =0; i<12; i++)
|
||||
{
|
||||
drdx_K(i,i) = K_dxidx(0,0);
|
||||
drdx_K(i,12+i) = K_dxidx(0,1);
|
||||
drdx_K(12+i,i) = K_dxidx(1,0);
|
||||
drdx_K(12+i,12+i) = K_dxidx(1,1);
|
||||
}
|
||||
|
||||
DenseMatrixInverse drdxK_inv(drdx_K);
|
||||
drdxK_inv.Mult(drdx_r,dxidx);
|
||||
// LinearSolve (drdx_K,drdx_r, dxidx) ; //???
|
||||
dxidx *= -1.0;
|
||||
|
||||
|
||||
|
||||
Vector drdxs_r(6);
|
||||
drdxs_r[0] = m_dx(0,0); drdxs_r[1] = m_dx(0,1); drdxs_r[2] = m_dx(0,2);
|
||||
drdxs_r[3] = m_dx(1,0); drdxs_r[4] = m_dx(1,1); drdxs_r[5] = m_dx(1,2);
|
||||
|
||||
DenseMatrix drdxs_K(6,6); drdxs_K = 0.;
|
||||
for (int i=0; i<3; i++)
|
||||
{
|
||||
drdxs_K(i,i) = K_dxidx(0,0);
|
||||
drdxs_K(i,3+i) = K_dxidx(0,1);
|
||||
drdxs_K(i+3,i) = K_dxidx(1,0);
|
||||
drdxs_K(i+3,i+3) = K_dxidx(1,1);
|
||||
}
|
||||
|
||||
Vector dxidxs(6); dxidxs = 0.0;
|
||||
DenseMatrixInverse drdxsK_inv(drdxs_K);
|
||||
drdxsK_inv.Mult(drdxs_r,dxidxs);
|
||||
dxidxs *= -1.0;
|
||||
//dxidxs = -drdxs_K\drdxs_r;
|
||||
|
||||
//dxidx = reshape(dxidx, 4,3,2); dxidxs = reshape(dxidxs, 1,3,2);
|
||||
|
||||
dgdxm.SetSize(12); dgdxm = 0.;
|
||||
DenseMatrix dgdxm_tmp(4,3);
|
||||
outer(m_N, normal,dgdxm_tmp);
|
||||
for (int i=0; i<4; i++)
|
||||
{
|
||||
for (int j=0; j<3; j++)
|
||||
{
|
||||
dgdxm[3*i+j] = -dgdxm_tmp(i,j);
|
||||
}
|
||||
}
|
||||
//dxidx_M = -m_dN(1:2,:,1) * (m_coords(1:4,:)*normal'); % this turns out to be 0
|
||||
|
||||
dgdxs.SetSize(3);
|
||||
dgdxs += normal;
|
||||
//dgdxs = dgdxs + dxidx_M(1) * dxidxs(:,:,1) + dxidx_M(2) * dxidxs(:,:,2);
|
||||
};
|
||||
|
||||
void ComputeGapHessian(const Vector x_s, const Vector xi,
|
||||
const DenseMatrix m_coords,
|
||||
DenseMatrix& dg2dx)
|
||||
{
|
||||
Vector m_N(4);
|
||||
DenseMatrix m_dN(2,4);
|
||||
DenseMatrix m_dN2(3,4);
|
||||
BasisEvalDerivs(xi, m_N, m_dN, m_dN2);
|
||||
|
||||
int dim = 3;
|
||||
int num_dofs1 = dim;
|
||||
int num_dofs2 = 4*dim;
|
||||
int num_dofs = num_dofs1 + num_dofs2;
|
||||
dg2dx.SetSize(num_dofs,num_dofs); dg2dx = 0.0;
|
||||
|
||||
Vector x_c(3);
|
||||
m_coords.MultTranspose(m_N,x_c);
|
||||
|
||||
Vector gap_v(3); gap_v = 0.0;
|
||||
gap_v = x_s;
|
||||
gap_v -= x_c;
|
||||
|
||||
DenseMatrix m_dx(2,3);
|
||||
Mult(m_dN, m_coords, m_dx);
|
||||
|
||||
DenseMatrix m_dx2(3,3); m_dx2 = 0.0;
|
||||
Mult(m_dN2,m_coords, m_dx2);
|
||||
double nnorm = 0.0;
|
||||
Vector normal(3); normal = 0.0;
|
||||
ComputeNormal(m_dN, m_coords, normal, nnorm);
|
||||
|
||||
double gap = gap_v * normal; // gap function value, dot product between vectors
|
||||
|
||||
DenseMatrix M(2,2); M = 0.0;
|
||||
MultABt(m_dx, m_dx, M);
|
||||
|
||||
DenseMatrix f(2, num_dofs2); f = 0.0;
|
||||
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
DenseMatrix Mtemp(2,2); Mtemp = 0.0;
|
||||
Mtemp(0,0) = m_dx2(0,d); Mtemp(0,1) = m_dx2(1,d);
|
||||
Mtemp(1,0) = m_dx2(1,d); Mtemp(1,1) = m_dx2(2,d);
|
||||
|
||||
M.Add(-gap_v[d], Mtemp);
|
||||
|
||||
Vector m_dxcol(2); m_dx.GetColumn(d, m_dxcol);
|
||||
DenseMatrix ftmp(2,4);
|
||||
outer(m_dxcol, m_N, ftmp);
|
||||
ftmp *= -1;
|
||||
ftmp.Add( gap_v[d], m_dN); // 2*4
|
||||
|
||||
for (int j=0; j<4; j++)
|
||||
{
|
||||
assert(d+3*j<num_dofs2);
|
||||
f(0,d+j*3) = ftmp(0,j);
|
||||
f(1,d+j*3) = ftmp(1,j);
|
||||
}
|
||||
}
|
||||
//fprintf('hess dxidxm\n');
|
||||
DenseMatrixInverse Minv(M);
|
||||
DenseMatrix dxidxm(2,num_dofs2); dxidxm = 0.0;
|
||||
Minv.Mult(f, dxidxm);
|
||||
//LinearSolve??
|
||||
//dxidxm = M\f;
|
||||
|
||||
DenseMatrix nde2(2,2); nde2 = 0.0;
|
||||
DenseMatrix Nndx2(2,num_dofs2); Nndx2 = 0.0;
|
||||
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
DenseMatrix ndetmp(2,2); ndetmp = 0.0;
|
||||
ndetmp(0,0) = normal(d)*m_dx2(0,d); ndetmp(0,1) = normal(d)*m_dx2(1,d);
|
||||
ndetmp(1,0) = normal(d)*m_dx2(1,d); ndetmp(1,1) = normal(d)*m_dx2(2,d);
|
||||
|
||||
nde2 += ndetmp;
|
||||
|
||||
for (int j=0; j<4; j++)
|
||||
{
|
||||
assert(d+3*j<num_dofs2);
|
||||
Nndx2(0,d+j*3) = normal[d]*m_dN(0,j);
|
||||
Nndx2(1,d+j*3) = normal[d]*m_dN(1,j);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DenseMatrix Ndn(2,num_dofs2); Ndn = 0.0;
|
||||
Ndn += Nndx2;
|
||||
AddMult(nde2, dxidxm, Ndn);
|
||||
|
||||
|
||||
DenseMatrix M2(2,2); M2 = 0.0;
|
||||
MultABt(m_dx, m_dx, M2);
|
||||
DenseMatrixInverse M2inv(M2);
|
||||
DenseMatrix diag2(2,2); diag2(0,0) = 1.0; diag2(1,1) = 1.0;
|
||||
DenseMatrix m_con(2,2); m_con = 0.0;
|
||||
|
||||
M2inv.Mult(diag2, m_con);
|
||||
|
||||
DenseMatrix dg2dxm(num_dofs2, num_dofs2); dg2dxm = 0.0;
|
||||
|
||||
DenseMatrix dg2dxm_tmp(num_dofs2,2); dg2dxm_tmp = 0.0;
|
||||
MultAtB(Ndn, m_con, dg2dxm_tmp);
|
||||
Mult(dg2dxm_tmp, Ndn, dg2dxm);
|
||||
dg2dxm *= gap;
|
||||
|
||||
DenseMatrix dg2dxm_tmp2(num_dofs2,num_dofs2); dg2dxm_tmp2 = 0.0;
|
||||
MultAtB(Nndx2, dxidxm, dg2dxm_tmp2);
|
||||
dg2dxm.Add(-1.0, dg2dxm_tmp2);
|
||||
|
||||
dg2dxm_tmp = 0.0;
|
||||
MultAtB(dxidxm, nde2, dg2dxm_tmp);
|
||||
|
||||
AddMult_a(-1.0, dg2dxm_tmp, dxidxm, dg2dxm);
|
||||
|
||||
dg2dxm_tmp2 = 0.0;
|
||||
MultAtB(dxidxm, Nndx2, dg2dxm_tmp2);
|
||||
dg2dxm.Add(-1.0, dg2dxm_tmp2);
|
||||
|
||||
Vector v_dxidx2(4);
|
||||
m_coords.Mult(gap_v, v_dxidx2); // m_coords * gap_v; // 4*3 * 3 = 4
|
||||
|
||||
DenseMatrix K_dxidx2(2,2); K_dxidx2 = 0.0;
|
||||
|
||||
Vector m_dN2row1(4); m_dN2.GetRow(0, m_dN2row1);
|
||||
Vector m_dN2row2(4); m_dN2.GetRow(1, m_dN2row2);
|
||||
Vector m_dN2row3(4); m_dN2.GetRow(2, m_dN2row3);
|
||||
K_dxidx2(0,0) = m_dN2row1 * v_dxidx2; // how would 4*1 * 1*4 be computed?
|
||||
K_dxidx2(0,1) = m_dN2row2 * v_dxidx2;
|
||||
K_dxidx2(1,0) = m_dN2row2 * v_dxidx2;
|
||||
K_dxidx2(1,1) = m_dN2row3 * v_dxidx2;
|
||||
|
||||
DenseMatrix K_dxidx(2,2);
|
||||
K_dxidx -= M2;
|
||||
K_dxidx += K_dxidx2;
|
||||
|
||||
Vector drdxs_r(6);
|
||||
drdxs_r[0] = m_dx(0,0); drdxs_r[1] = m_dx(0,1); drdxs_r[2] = m_dx(0,2);
|
||||
drdxs_r[3] = m_dx(1,0); drdxs_r[4] = m_dx(1,1); drdxs_r[5] = m_dx(1,2);
|
||||
|
||||
DenseMatrix drdxs_K(6,6); drdxs_K = 0.;
|
||||
for (int i=0; i<3; i++)
|
||||
{
|
||||
drdxs_K(i,i) = K_dxidx(0,0);
|
||||
drdxs_K(i,3+i) = K_dxidx(0,1);
|
||||
drdxs_K(i+3,i) = K_dxidx(1,0);
|
||||
drdxs_K(i+3,i+3) = K_dxidx(1,1);
|
||||
}
|
||||
Vector dxidxs(6);
|
||||
|
||||
DenseMatrixInverse drdxsK_inv(drdxs_K);
|
||||
drdxsK_inv.Mult(drdxs_r,dxidxs);
|
||||
dxidxs *= -1.0;
|
||||
//dxidxs = -drdxs_K\drdxs_r;
|
||||
|
||||
DenseMatrix dxidxs_m(2,3); dxidxs_m = 0.0;
|
||||
dxidxs_m(0,0) = dxidxs[0]; dxidxs_m(0,1) = dxidxs[1]; dxidxs_m(0,2) = dxidxs[2];
|
||||
dxidxs_m(1,0) = dxidxs[3]; dxidxs_m(1,1) = dxidxs[4]; dxidxs_m(1,2) = dxidxs[5];
|
||||
|
||||
DenseMatrix dtao1dxs(3,3); dtao1dxs = 0.0;
|
||||
DenseMatrix dtao2dxs(3,3); dtao2dxs = 0.0;
|
||||
|
||||
Vector dxidxs_row1(3); dxidxs_row1 = 0.0; Vector dxidxs_row2(3);
|
||||
dxidxs_row2 = 0.0;
|
||||
Vector mdx2_row1(3); mdx2_row1 = 0.0; Vector mdx2_row2(3); mdx2_row2 = 0.0;
|
||||
Vector mdx2_row3(3); mdx2_row3 = 0.0;
|
||||
dxidxs_m.GetRow(0,dxidxs_row1);
|
||||
dxidxs_m.GetRow(1,dxidxs_row2);
|
||||
m_dx2.GetRow(0,mdx2_row1);
|
||||
m_dx2.GetRow(1,mdx2_row2);
|
||||
m_dx2.GetRow(2,mdx2_row3);
|
||||
|
||||
DenseMatrix dtaotmp(3,3); dtaotmp = 0.0;
|
||||
outer(mdx2_row1, dxidxs_row1,dtaotmp);
|
||||
dtao1dxs += dtaotmp; dtaotmp = 0.0;
|
||||
outer(mdx2_row2, dxidxs_row1,dtaotmp);
|
||||
dtao1dxs += dtaotmp; dtaotmp = 0.0;
|
||||
|
||||
outer(mdx2_row2, dxidxs_row2, dtaotmp);
|
||||
dtao2dxs += dtaotmp; dtaotmp = 0.0;
|
||||
outer(mdx2_row3, dxidxs_row2, dtaotmp);
|
||||
dtao2dxs += dtaotmp; dtaotmp = 0.0;
|
||||
|
||||
DenseMatrix dtaodxs(3,3); dtaodxs = 0.0; //tao = tao1 cross tao2
|
||||
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
Vector dtao1dxs_tmp(3); dtao1dxs_tmp = 0.0;
|
||||
dtao1dxs.GetColumn(d,dtao1dxs_tmp);
|
||||
Vector m_dxrow(3); m_dx.GetRow(1, m_dxrow);
|
||||
|
||||
Vector dtaodxs_tmp(3); dtaodxs_tmp = 0.0;
|
||||
cross(dtao1dxs_tmp, m_dxrow, dtaodxs_tmp);
|
||||
|
||||
Vector dtaodxs_tmp2(3); dtaodxs_tmp2 = 0.0;
|
||||
m_dx.GetRow(0, m_dxrow);
|
||||
dtao1dxs_tmp = 0.0; // reuse the same vector for dtao2
|
||||
dtao2dxs.GetColumn(d,dtao1dxs_tmp);
|
||||
cross(m_dxrow, dtao1dxs_tmp, dtaodxs_tmp2);
|
||||
|
||||
dtaodxs_tmp2 += dtaodxs_tmp;
|
||||
dtaodxs.SetCol(d, dtaodxs_tmp2);
|
||||
}
|
||||
|
||||
DenseMatrix dndxs(3,3); dndxs = 0.0; dndxs += dtaodxs; dndxs *= 1.0/nnorm;
|
||||
DenseMatrix dndxs_tmp(3,3); dndxs_tmp = 0.0;
|
||||
outer(normal, normal, dndxs_tmp);
|
||||
AddMult_a(-1/nnorm, dndxs_tmp, dtaodxs, dndxs);
|
||||
|
||||
DenseMatrix dgvdxs(3,3); dgvdxs = 0.0;
|
||||
MultAtB(m_dx, dxidxs_m, dgvdxs);
|
||||
dgvdxs *= -1;
|
||||
for (int d=0; d<3; d++)
|
||||
{
|
||||
dgvdxs(d,d) += 1.0;
|
||||
}
|
||||
//dxidxs: 2*3
|
||||
|
||||
DenseMatrix dg2dxs(3,3); dg2dxs = 0.0;
|
||||
DenseMatrix dg2dxs_tmp(3,2); dg2dxs_tmp = 0.0;
|
||||
MultAtB(dxidxs_m, nde2, dg2dxs_tmp);
|
||||
AddMult_a(-1.0, dg2dxs_tmp, dxidxs_m, dg2dxs);
|
||||
DenseMatrix dg2dxs_tmp2(3,3); dg2dxs_tmp2 = 0.0;
|
||||
MultAtB(dgvdxs, dndxs, dg2dxs_tmp2);
|
||||
dg2dxs += dg2dxs_tmp2;
|
||||
dg2dxs_tmp2 = 0.0;
|
||||
MultAtB(dndxs, dndxs_tmp, dg2dxs_tmp2);
|
||||
AddMult(dg2dxs_tmp2, dgvdxs, dg2dxs);
|
||||
|
||||
DenseMatrix Ne(3,12), Be(6,12), dBe(12,12);
|
||||
BasisVectorDerivs(xi, Ne, Be, dBe);
|
||||
|
||||
DenseMatrix dtao1dxm(3,12); dtao1dxm.CopyRows(Be, 0, 2);
|
||||
DenseMatrix dtao2dxm(3,12); dtao2dxm.CopyRows(Be, 3, 5);
|
||||
|
||||
Vector m_coords_v(12);
|
||||
for (int i=0; i<4; i++)
|
||||
{
|
||||
for (int j=0; j<3; j++)
|
||||
{
|
||||
m_coords_v[i*3+j] = m_coords(i,j);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<2; i++)
|
||||
{
|
||||
Vector dxidxm_tmp(num_dofs2); dxidxm_tmp = 0.0;
|
||||
dxidxm.GetRow(i,dxidxm_tmp);
|
||||
|
||||
DenseMatrix dBe_tmp(3,12);
|
||||
dBe_tmp.CopyRows(dBe,i*3,(i+1)*3-1);
|
||||
|
||||
DenseMatrix dtaodxm_tmp(12,12); dtaodxm_tmp = 0.0;
|
||||
outer(m_coords_v, dxidxm_tmp, dtaodxm_tmp);
|
||||
AddMult(dBe_tmp, dtaodxm_tmp, dtao1dxm);
|
||||
|
||||
//dtao1dxm += dBe(:,:,i)*reshape(m_coords(1:4,:)',12,1)*reshape(dxidxm(i,:),1,12); % 3*12
|
||||
dBe_tmp = 0.0;
|
||||
dBe_tmp.CopyRows(dBe,(i+2)*3,(i+3)*3-1);
|
||||
AddMult(dBe_tmp, dtaodxm_tmp, dtao2dxm);
|
||||
|
||||
}
|
||||
|
||||
DenseMatrix dtaodxm(3,12); dtaodxm = 0.0;//tao = tao1 cross tao2
|
||||
|
||||
for (int d=0; d<12; d++)
|
||||
{
|
||||
Vector dtaodxm_tmp(3); dtaodxm_tmp = 0.0;
|
||||
Vector dtaodxm_tmp2(3); dtaodxm_tmp2 = 0.0;
|
||||
Vector tmp1(3); tmp1 = 0.0; dtao1dxm.GetColumn(d,tmp1);
|
||||
Vector m_dxrow2(3); m_dx.GetRow(1, m_dxrow2);
|
||||
Vector m_dxrow1(3); m_dx.GetRow(0, m_dxrow1);
|
||||
Vector tmp2(3); tmp2 = 0.0; dtao2dxm.GetColumn(d,tmp2);
|
||||
|
||||
cross(tmp1, m_dxrow2, dtaodxm_tmp);
|
||||
cross(m_dxrow1,tmp2, dtaodxm_tmp2);
|
||||
dtaodxm_tmp += dtaodxm_tmp2;
|
||||
|
||||
dtaodxm.SetCol(d, dtaodxm_tmp);
|
||||
}
|
||||
|
||||
DenseMatrix dndxm(3,12); dndxm = 0.0;
|
||||
dndxm += dtaodxm;
|
||||
dndxm *= 1.0/nnorm;
|
||||
AddMult_a(-1/nnorm, dndxs_tmp, dtaodxm, dndxm); //dndxs_tmp = normal'*normal
|
||||
|
||||
DenseMatrix dgvdxm(3,12); dgvdxm = 0.0;
|
||||
dgvdxm -= Ne;
|
||||
|
||||
for (int i=0; i<2; i++)
|
||||
{
|
||||
Vector dxidxm_tmp(num_dofs2); dxidxm_tmp = 0.0;
|
||||
dxidxm.GetRow(i,dxidxm_tmp);
|
||||
|
||||
DenseMatrix Be_tmp(3,12);
|
||||
Be_tmp.CopyRows(Be,i*3,(i+1)*3-1);
|
||||
|
||||
DenseMatrix dgvdxm_tmp(12,12); dgvdxm_tmp = 0.0;
|
||||
outer(m_coords_v, dxidxm_tmp, dgvdxm_tmp);
|
||||
AddMult_a(-1.0, Be_tmp, dgvdxm_tmp, dgvdxm);
|
||||
|
||||
}
|
||||
|
||||
DenseMatrix dg2dxsxm(3,12); dg2dxsxm = 0.0;
|
||||
DenseMatrix dg2dxsxm_tmp(3,3); dg2dxsxm_tmp = 0.0;
|
||||
MultAtB(dgvdxs, dndxm, dg2dxsxm);
|
||||
|
||||
MultAtB(dndxs, dndxs_tmp, dg2dxsxm_tmp);
|
||||
AddMult(dg2dxsxm_tmp, dgvdxm, dg2dxsxm); // += dndxs'*normal'*normal*dgvdxm;
|
||||
|
||||
DenseMatrix dgvdxsxmn(3,12); dgvdxsxmn = 0.0;
|
||||
DenseMatrix dgvdxsxmn_tmp(3,2); dgvdxsxmn_tmp = 0.0;
|
||||
MultAtB(dxidxs_m, nde2, dgvdxsxmn_tmp); //dxidxs_m: 2*3
|
||||
|
||||
AddMult_a(-1.0, dgvdxsxmn_tmp, dxidxm, dgvdxsxmn);
|
||||
|
||||
|
||||
for (int i =0; i<2; i++)
|
||||
{
|
||||
DenseMatrix Be_tmp(3,12);
|
||||
Be_tmp.CopyRows(Be,i*3,(i+1)*3-1);
|
||||
|
||||
Vector dxidxs_row(3); dxidxs_row = 0.0; dxidxs_m.GetRow(i,dxidxs_row);
|
||||
DenseMatrix dgvdxsxmn_tmp2(3,3); dgvdxsxmn_tmp2 = 0.0;
|
||||
outer(dxidxs_row, normal, dgvdxsxmn_tmp2);
|
||||
AddMult_a(-1.0, dgvdxsxmn_tmp2, Be_tmp, dgvdxsxmn);
|
||||
}
|
||||
|
||||
dg2dxsxm += dgvdxsxmn;
|
||||
|
||||
DenseMatrix dg2dxmxs(12,3); dg2dxmxs = 0.0;
|
||||
DenseMatrix dg2dxmxs_tmp(12,3); dg2dxmxs_tmp = 0.0;
|
||||
MultAtB(dgvdxm, dndxs, dg2dxmxs);
|
||||
MultAtB(dndxm, dndxs_tmp, dg2dxmxs_tmp);
|
||||
AddMult(dg2dxmxs_tmp, dgvdxs, dg2dxmxs);
|
||||
|
||||
DenseMatrix dgvdxmxsn(12,3); dgvdxmxsn = 0.0;
|
||||
DenseMatrix dgvdxmxsn_tmp(12,2); dgvdxmxsn_tmp = 0.0;
|
||||
|
||||
MultAtB(dxidxm, nde2, dgvdxmxsn_tmp);
|
||||
dgvdxmxsn_tmp *= -1.0;
|
||||
AddMult(dgvdxmxsn_tmp, dxidxs_m, dgvdxmxsn);
|
||||
|
||||
for (int i =0; i<2; i++)
|
||||
{
|
||||
DenseMatrix Be_tmp(3,12);
|
||||
Be_tmp.CopyRows(Be,i*3,(i+1)*3-1);
|
||||
Be_tmp.Transpose(); // Be is now 12*3
|
||||
|
||||
Vector dxidxs_row(3); dxidxs_row = 0.0; dxidxs_m.GetRow(i,dxidxs_row);
|
||||
DenseMatrix dgvdxmxsn_tmp2(3,3); dgvdxmxsn_tmp2 = 0.0;
|
||||
outer(normal, dxidxs_row, dgvdxmxsn_tmp2);
|
||||
AddMult_a(-1.0, Be_tmp, dgvdxmxsn_tmp2, dgvdxmxsn);
|
||||
|
||||
}
|
||||
|
||||
dg2dxmxs += dgvdxmxsn;
|
||||
|
||||
dg2dx.CopyMN(dg2dxs, 0, 0);
|
||||
dg2dx.CopyMN(dg2dxm, 3, 3);
|
||||
dg2dx.CopyMN(dg2dxsxm, 0, 3);
|
||||
dg2dx.CopyMN(dg2dxmxs, 3, 0);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
void NodeSegConPairs(const Vector x1, const Vector xi2,
|
||||
const DenseMatrix coords2,
|
||||
double& node_g, Vector& node_dg, DenseMatrix& node_dg2)
|
||||
{
|
||||
double gap = 0.0;
|
||||
Vector normal(3); normal = 0.0;
|
||||
Vector dgdxm(12); dgdxm = 0.0;
|
||||
Vector dgdxs(3); dgdxs = 0.0;
|
||||
|
||||
ComputeGapJacobian(x1, xi2, coords2, gap, normal, dgdxm, dgdxs);
|
||||
node_g = gap;
|
||||
|
||||
node_dg.SetSize(12+3);
|
||||
for (int i=0; i<3; i++) { node_dg[i] = dgdxs[i]; }
|
||||
for (int i=0; i<12; i++) { node_dg[i+3] = dgdxm[i]; }
|
||||
|
||||
DenseMatrix dg2dx(15,15); dg2dx = 0.0;
|
||||
DenseMatrix dgvdxmxsn(12,3); dgvdxmxsn = 0.0;
|
||||
ComputeGapHessian(x1, xi2, coords2, dg2dx);
|
||||
|
||||
node_dg2.SetSize(15,15);
|
||||
node_dg2 = dg2dx;
|
||||
|
||||
/*
|
||||
if(obj.space1.conns{e1}(i)==150) % for debugging purpose
|
||||
|
||||
v1 = 1:3;
|
||||
v2 = 1:12;
|
||||
%v1 = ones(1,3)
|
||||
%v2 = ones(1,12)
|
||||
v2 = reshape(v2,4,3);
|
||||
x1n1 = x1 + 0.01*v1;
|
||||
coords2n1 = coords2 + 0.001*v2;
|
||||
[xi2n1, gapv1, ~, ~] = SlaveToMaster(obj, coords2n1, x1n1);
|
||||
[gapn1, n1,dgdxmn1, dgdxsn1] = ComputeGapJacobian(obj, x1n1, xi2n1, coords2n1);
|
||||
x1n2 = x1 - 0.01*v1;
|
||||
coords2n2 = coords2 - 0.001*v2;
|
||||
[xi2n2, gapv2, ~, ~] = SlaveToMaster(obj, coords2n2, x1n2);
|
||||
[gapn2, n2,dgdxmn2, dgdxsn2] = ComputeGapJacobian(obj, x1n2, xi2n2, coords2n2);
|
||||
fprintf('fd\n');
|
||||
%gapv1-gapv2
|
||||
[dgdxsn1(:)',dgdxmn1(:)'] - [dgdxsn2(:)',dgdxmn2(:)']
|
||||
|
||||
%dgdxsn1-dgdxsn2
|
||||
fprintf('code\n');
|
||||
v2n = v2';
|
||||
%dg2dx(1:3,1:3)*0.04*ones(3,1)
|
||||
temp = zeros(12,3);
|
||||
for i = 1:4
|
||||
temp1 = dg2dx(3+(i-1)*3+1:3+i*3,1:3);
|
||||
temp((i-1)*3+1:i*3,:) = temp1';
|
||||
end
|
||||
temp2 = zeros(3,12);
|
||||
for i = 1:4
|
||||
temp3 = dg2dx(1:3,3+(i-1)*3+1:3+i*3);
|
||||
temp2(:,(i-1)*3+1:i*3) = temp3';
|
||||
end
|
||||
%dg2dx
|
||||
%dg2dx(4:end,1:3) = temp;
|
||||
%dg2dx(1:3,4:end) = temp2;
|
||||
%dgvdxm * 0.002*v2n(:)
|
||||
(dg2dx*[0.02*v1(:)',0.002*v2n(:)']')'
|
||||
%dg2dx(4:end,1:3)
|
||||
end*/
|
||||
|
||||
};
|
||||
|
||||
|
||||
// coordsm : (npoints*4, 3) use what class?
|
||||
// m_conn: (npoints*4)
|
||||
void Assemble_Contact(const int m, const int npoints, const int ndofs,
|
||||
const Vector x_s,
|
||||
const Vector xi, const DenseMatrix coordsm, const Array<int> s_conn,
|
||||
const Array<int> m_conn, Vector& g, SparseMatrix& M,
|
||||
std::vector<SparseMatrix>& dM)
|
||||
{
|
||||
int ndim = 3;
|
||||
|
||||
g.SetSize(m);
|
||||
g = 0.0;
|
||||
|
||||
//SparseMatrix M(m, n); // M needs to be the correct size
|
||||
|
||||
//dM.resize(m); // needs to clear?
|
||||
|
||||
double g_tmp = 0.;
|
||||
Vector dg(4*ndim+ndim);
|
||||
dg = 0.;
|
||||
DenseMatrix dg2(4*ndim+ndim,4*ndim+ndim);
|
||||
dg2 = 0.;
|
||||
|
||||
for (int i=0; i<npoints; i++)
|
||||
{
|
||||
Vector x1(ndim);
|
||||
x1[0] = x_s[i*ndim];
|
||||
x1[1] = x_s[i*ndim+1];
|
||||
x1[2] = x_s[i*ndim+2];
|
||||
|
||||
Vector xi2(ndim-1);
|
||||
xi2[0] = xi[i*(ndim-1)];
|
||||
xi2[1] = xi[i*(ndim-1)+1];
|
||||
|
||||
DenseMatrix coords2(4,3);
|
||||
coords2.CopyRows(coordsm, i*4,(i+1)*4-1);
|
||||
|
||||
//how to get coords2?
|
||||
dg = 0.0;
|
||||
dg2 = 0.;
|
||||
NodeSegConPairs(x1, xi2, coords2, g_tmp, dg, dg2);
|
||||
g[s_conn[i]] = g_tmp; // should be unique
|
||||
Array<int> m_conn_i(4);
|
||||
m_conn.GetSubArray(4*i, 4, m_conn_i);
|
||||
|
||||
Array<int> node_conn(5);
|
||||
node_conn[0] = s_conn[i];
|
||||
for (int j=0; j<4; j++)
|
||||
{
|
||||
node_conn[j+1] = m_conn_i[j];
|
||||
}
|
||||
|
||||
Array<int> M_i_tmp(1);
|
||||
M_i_tmp[0] = s_conn[i];
|
||||
|
||||
//j_idx = (node_conn-1)*obj.disp_field.num_components +repmat((1:obj.disp_field.num_components)', 1, length(node_conn{i}));
|
||||
Array<int> j_idx(5*ndim); j_idx = 0;
|
||||
for (int j=0; j< 5; j++)
|
||||
{
|
||||
for (int k=0; k<ndim; k++)
|
||||
{
|
||||
j_idx[j*ndim+k] = node_conn[j]*ndim+k;
|
||||
}
|
||||
}
|
||||
DenseMatrix M_v_tmp(1, ndim*(4+1)); // SetData now?
|
||||
M_v_tmp.SetRow(0, dg);
|
||||
|
||||
M.AddSubMatrix(M_i_tmp, j_idx, M_v_tmp);
|
||||
|
||||
Array<int> dM_i(ndim*(4+1));
|
||||
Array<int> dM_j(ndim*(4+1));
|
||||
|
||||
for (int j=0; j< ndim*(4+1); j++)
|
||||
{
|
||||
dM_i[j] = j_idx[j];
|
||||
dM_j[j] = j_idx[j];
|
||||
}
|
||||
dM[s_conn[i]].AddSubMatrix(dM_i,dM_j, dg2);
|
||||
dM[s_conn[i]].Finalize();
|
||||
dM[s_conn[i]].Threshold(0.0);
|
||||
dM[s_conn[i]].SortColumnIndices();
|
||||
}
|
||||
M.Finalize();
|
||||
M.Threshold(0.0);
|
||||
M.SortColumnIndices();
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,396 +0,0 @@
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
#ifndef PROBLEM_DEFS
|
||||
#define PROBLEM_DEFS
|
||||
|
||||
|
||||
|
||||
// abstract OptProblem class
|
||||
// of the form
|
||||
// min_(u,m) f(u,m) s.t. c(u,m)=0 and m>=ml
|
||||
// the primal variable (u, m) is represented as a BlockVector
|
||||
|
||||
class OptProblem
|
||||
{
|
||||
protected:
|
||||
int dimU, dimM, dimC;
|
||||
Array<int> block_offsetsx;
|
||||
Vector ml;
|
||||
public:
|
||||
OptProblem();
|
||||
virtual double CalcObjective(const BlockVector &) const = 0;
|
||||
virtual void Duf(const BlockVector &, Vector &) const = 0;
|
||||
virtual void Dmf(const BlockVector &, Vector &) const = 0;
|
||||
void CalcObjectiveGrad(const BlockVector &, BlockVector &) const;
|
||||
virtual SparseMatrix* Duuf(const BlockVector &) = 0;
|
||||
virtual SparseMatrix* Dumf(const BlockVector &) = 0;
|
||||
virtual SparseMatrix* Dmuf(const BlockVector &) = 0;
|
||||
virtual SparseMatrix* Dmmf(const BlockVector &) = 0;
|
||||
virtual void c(const BlockVector &, Vector &) const = 0;
|
||||
virtual SparseMatrix* Duc(const BlockVector &) = 0;
|
||||
virtual SparseMatrix* Dmc(const BlockVector &) = 0;
|
||||
// TO DO: include Hessian terms of constraint c
|
||||
// TO DO: include log-barrier lumped-mass and pass that
|
||||
// to the optimizer
|
||||
//virtual SparseMatrix* GetLogBarrierLumpedMass() = 0;
|
||||
int GetDimU() const { return dimU; };
|
||||
int GetDimM() const { return dimM; };
|
||||
int GetDimC() const { return dimC; };
|
||||
Vector Getml() const { return ml; };
|
||||
~OptProblem();
|
||||
};
|
||||
|
||||
|
||||
// abstract ContactProblem class
|
||||
// of the form
|
||||
// min_d e(d) s.t. g(d) >= 0
|
||||
// TO DO: add functionality for gap function Hessian apply
|
||||
class ContactProblem : public OptProblem
|
||||
{
|
||||
protected:
|
||||
int dimD;
|
||||
int dimS;
|
||||
Array<int> block_offsetsx;
|
||||
public:
|
||||
//ContactProblem(int, int); // constructor
|
||||
ContactProblem();
|
||||
void InitializeParentData(int, int);
|
||||
double CalcObjective(const BlockVector &) const; // objective e
|
||||
void Duf(const BlockVector &, Vector &) const;
|
||||
void Dmf(const BlockVector &, Vector &) const;
|
||||
SparseMatrix* Duuf(const BlockVector &);
|
||||
SparseMatrix* Dumf(const BlockVector &);
|
||||
SparseMatrix* Dmuf(const BlockVector &);
|
||||
SparseMatrix* Dmmf(const BlockVector &);
|
||||
void c(const BlockVector &, Vector &) const;
|
||||
SparseMatrix* Duc(const BlockVector &);
|
||||
SparseMatrix* Dmc(const BlockVector &);
|
||||
virtual double E(const Vector &) const = 0; // objective e(d) (energy function)
|
||||
virtual void DdE(const Vector &, Vector &) const = 0; // gradient of objective De / Dd
|
||||
virtual SparseMatrix* DddE(const Vector &) = 0; // Hessian of objective D^2 e / D d^2
|
||||
virtual void g(const Vector &, Vector &) const = 0; // inequality constraint g(d) >= 0 (gap function)
|
||||
virtual SparseMatrix* Ddg(const Vector &) = 0; // Jacobian of inequality constraint Dg / Dd
|
||||
int GetDimD() const { return dimD; };
|
||||
int GetDimS() const { return dimS; };
|
||||
virtual ~ContactProblem();
|
||||
};
|
||||
|
||||
|
||||
class ObstacleProblem : public ContactProblem
|
||||
{
|
||||
protected:
|
||||
// data to define energy objective function e(d) = 0.5 d^T K d - f^T d, g(d) = d >= 0
|
||||
// stiffness matrix used to define objective
|
||||
BilinearForm *Kform;
|
||||
LinearForm *fform;
|
||||
Array<int> empty_tdof_list; // needed for calls to FormSystemMatrix
|
||||
SparseMatrix K;
|
||||
SparseMatrix *J;
|
||||
FiniteElementSpace *Vh;
|
||||
Vector f;
|
||||
public :
|
||||
ObstacleProblem(FiniteElementSpace* , double (*fSource)(const Vector &));
|
||||
double E(const Vector &) const;
|
||||
void DdE(const Vector &, Vector &) const;
|
||||
SparseMatrix* DddE(const Vector &);
|
||||
void g(const Vector &, Vector &) const;
|
||||
SparseMatrix* Ddg(const Vector &);
|
||||
// TO DO: include lumped-mass for the log-barrier term
|
||||
//SparseMatrix* GetLogBarrierLumpedMass();
|
||||
virtual ~ObstacleProblem();
|
||||
};
|
||||
|
||||
class DirichletObstacleProblem : public ContactProblem
|
||||
{
|
||||
protected:
|
||||
// data to define energy objective function e(d) = 0.5 d^T K d - f^T d, g(d) = d + \psi >= 0
|
||||
// stiffness matrix used to define objective
|
||||
BilinearForm *Kform;
|
||||
LinearForm *fform;
|
||||
Array<int> ess_tdof_list; // needed for calls to FormSystemMatrix
|
||||
SparseMatrix *K;
|
||||
SparseMatrix *J;
|
||||
FiniteElementSpace *Vh;
|
||||
Vector f;
|
||||
Vector psi;
|
||||
Vector xDC;
|
||||
public :
|
||||
DirichletObstacleProblem(FiniteElementSpace*, Vector&, double (*fSource)(const Vector &), double (*obstacleSource)(const Vector &), Array<int> tdof_list, bool);
|
||||
double E(const Vector &) const;
|
||||
void DdE(const Vector &, Vector &) const;
|
||||
SparseMatrix* DddE(const Vector &);
|
||||
void g(const Vector &, Vector &) const;
|
||||
SparseMatrix* Ddg(const Vector &);
|
||||
virtual ~DirichletObstacleProblem();
|
||||
};
|
||||
|
||||
|
||||
// abstract out technology for removing null rows of the Jacobian from an existing contact problem
|
||||
class ReducedContactProblem : public ContactProblem
|
||||
{
|
||||
protected:
|
||||
Array<int> activeConstraints;
|
||||
Array<int> fixedDofs;
|
||||
ContactProblem * contact;
|
||||
int dimSin;
|
||||
public:
|
||||
ReducedContactProblem(ContactProblem * contact, Array<int> activeConstraints, Array<int> fixedDofs);
|
||||
double E(const Vector &) const;
|
||||
void DdE(const Vector &, Vector &) const;
|
||||
SparseMatrix* DddE(const Vector &);
|
||||
void g(const Vector &, Vector &) const;
|
||||
SparseMatrix* Ddg(const Vector &);
|
||||
virtual ~ReducedContactProblem();
|
||||
};
|
||||
|
||||
|
||||
class QPContactProblem : public ContactProblem
|
||||
{
|
||||
protected:
|
||||
SparseMatrix *K;
|
||||
SparseMatrix *J;
|
||||
Vector f;
|
||||
Vector g0;
|
||||
public:
|
||||
QPContactProblem(const SparseMatrix, const SparseMatrix, const Vector, const Vector);
|
||||
double E(const Vector &) const;
|
||||
void DdE(const Vector &, Vector &) const;
|
||||
SparseMatrix* DddE(const Vector &);
|
||||
void g(const Vector &, Vector &) const;
|
||||
SparseMatrix* Ddg(const Vector &);
|
||||
virtual ~QPContactProblem();
|
||||
};
|
||||
|
||||
|
||||
typedef int Index;
|
||||
typedef double Number;
|
||||
|
||||
class ExContactBlockTL : public ContactProblem
|
||||
{
|
||||
public:
|
||||
double E(const Vector &) const;
|
||||
void DdE(const Vector &, Vector &) const;
|
||||
SparseMatrix* DddE(const Vector &);
|
||||
void g(const Vector &, Vector &) const;
|
||||
SparseMatrix* Ddg(const Vector &);
|
||||
FiniteElementSpace GetVh1();
|
||||
FiniteElementSpace GetVh2();
|
||||
|
||||
public:
|
||||
/** default constructor */
|
||||
ExContactBlockTL(int );
|
||||
|
||||
|
||||
/** default destructor */
|
||||
virtual ~ExContactBlockTL();
|
||||
|
||||
///**@name Overloaded from TNLP */
|
||||
///** Method to return some info about the nlp */
|
||||
//virtual bool get_nlp_info(
|
||||
// Index& n,
|
||||
// Index& m,
|
||||
// Index& nnz_jac_g,
|
||||
// Index& nnz_h_lag,
|
||||
// IndexStyleEnum& index_style
|
||||
//);
|
||||
|
||||
///** Method to return the bounds for my problem */
|
||||
//virtual bool get_bounds_info(
|
||||
// Index n,
|
||||
// Number* x_l,
|
||||
// Number* x_u,
|
||||
// Index m,
|
||||
// Number* g_l,
|
||||
// Number* g_u
|
||||
//);
|
||||
|
||||
///** Method to return the starting point for the algorithm */
|
||||
//virtual bool get_starting_point(
|
||||
// Index n,
|
||||
// bool init_x,
|
||||
// Number* x,
|
||||
// bool init_z,
|
||||
// Number* z_L,
|
||||
// Number* z_U,
|
||||
// Index m,
|
||||
// bool init_lambda,
|
||||
// Number* lambda
|
||||
//);
|
||||
|
||||
/* Method to return the objective value */
|
||||
virtual bool eval_f(
|
||||
Index n,
|
||||
const Number* x,
|
||||
bool new_x,
|
||||
Number& obj_value
|
||||
) const;
|
||||
|
||||
/* Method to return the gradient of the objective */
|
||||
virtual bool eval_grad_f(
|
||||
Index n,
|
||||
const Number* x,
|
||||
bool new_x,
|
||||
Number* grad_f
|
||||
) const;
|
||||
|
||||
/* Method to return the constraint residuals */
|
||||
virtual bool eval_g(
|
||||
Index n,
|
||||
const Number* x,
|
||||
bool new_x,
|
||||
Index m,
|
||||
Number* cons
|
||||
) const;
|
||||
|
||||
/* Method to return:
|
||||
1) The structure of the Jacobian (if "values" is NULL)
|
||||
2) The values of the Jacobian (if "values" is not NULL)
|
||||
*/
|
||||
virtual bool eval_jac_g(
|
||||
Index n,
|
||||
const Number* x,
|
||||
bool new_x,
|
||||
Index m,
|
||||
Index nele_jac,
|
||||
Index* iRow,
|
||||
Index* jCol,
|
||||
Number* values
|
||||
) const;
|
||||
|
||||
/* Method to return:
|
||||
* 1) The structure of the Hessian of the Lagrangian (if "values" is NULL)
|
||||
* 2) The values of the Hessian of the Lagrangian (if "values" is not NULL)
|
||||
*/
|
||||
virtual bool eval_h(
|
||||
Index n,
|
||||
const Number* x,
|
||||
bool new_x,
|
||||
Number obj_factor,
|
||||
Index m,
|
||||
const Number* lambda,
|
||||
bool new_lambda,
|
||||
Index nele_hess,
|
||||
Index* iRow,
|
||||
Index* jCol,
|
||||
Number* values
|
||||
);
|
||||
|
||||
///** This method is called when the algorithm is complete so the TNLP can store/write the solution */
|
||||
//virtual void finalize_solution(
|
||||
// SolverReturn status,
|
||||
// Index n,
|
||||
// const Number* x,
|
||||
// const Number* z_L,
|
||||
// const Number* z_U,
|
||||
// Index m,
|
||||
// const Number* g,
|
||||
// const Number* lambda,
|
||||
// Number obj_value,
|
||||
// const IpoptData* ip_data,
|
||||
// IpoptCalculatedQuantities* ip_cq
|
||||
//);
|
||||
|
||||
private:
|
||||
void update_g() const;
|
||||
void update_jac();
|
||||
void update_hess();
|
||||
|
||||
private:
|
||||
/**@name Methods to block default compiler methods.
|
||||
*
|
||||
* The compiler automatically generates the following three methods.
|
||||
* Since the default compiler implementation is generally not what
|
||||
* you want (for all but the most simple classes), we usually
|
||||
* put the declarations of these methods in the private section
|
||||
* and never implement them. This prevents the compiler from
|
||||
* implementing an incorrect "default" behavior without us
|
||||
* knowing. (See Scott Meyers book, "Effective C++")
|
||||
*/
|
||||
ExContactBlockTL(
|
||||
const ExContactBlockTL&
|
||||
);
|
||||
|
||||
ExContactBlockTL& operator=(
|
||||
const ExContactBlockTL&
|
||||
);
|
||||
|
||||
Array<int> attr;
|
||||
Array<int> m_attr;
|
||||
Array<int> s_conn; // connectivity of the second/slave mesh
|
||||
std::string mesh_file1;
|
||||
std::string mesh_file2;
|
||||
Mesh* mesh1;
|
||||
Mesh* mesh2;
|
||||
FiniteElementCollection* fec1;
|
||||
FiniteElementCollection* fec2;
|
||||
FiniteElementSpace* fespace1;
|
||||
FiniteElementSpace* fespace2;
|
||||
Array<int> ess_tdof_list1;
|
||||
Array<int> ess_tdof_list2;
|
||||
GridFunction nodes0;
|
||||
GridFunction* nodes1;
|
||||
GridFunction* nodes2;
|
||||
mutable GridFunction* x1;
|
||||
mutable GridFunction* x2;
|
||||
LinearForm* b1;
|
||||
LinearForm* b2;
|
||||
PWConstCoefficient* lambda1_func;
|
||||
PWConstCoefficient* lambda2_func;
|
||||
PWConstCoefficient* mu1_func;
|
||||
PWConstCoefficient* mu2_func;
|
||||
BilinearForm* a1;
|
||||
BilinearForm* a2;
|
||||
|
||||
mfem::Vector lambda1;
|
||||
mfem::Vector lambda2;
|
||||
mfem::Vector mu1;
|
||||
mfem::Vector mu2;
|
||||
mutable mfem::Vector xyz;
|
||||
|
||||
std::set<int> bdryVerts2;
|
||||
|
||||
int dim;
|
||||
// degrees of freedom of both meshes
|
||||
int ndof_1;
|
||||
int ndof_2;
|
||||
int ndofs;
|
||||
// number of nodes for each mesh
|
||||
int nnd_1;
|
||||
int nnd_2;
|
||||
int nnd;
|
||||
|
||||
int npoints;
|
||||
|
||||
SparseMatrix A1;
|
||||
mfem::Vector B1, X1;
|
||||
SparseMatrix A2;
|
||||
mfem::Vector B2, X2;
|
||||
|
||||
SparseMatrix* K;
|
||||
mutable mfem::Vector gapv;
|
||||
mutable mfem::Vector m_xi;
|
||||
mutable mfem::Vector xs;
|
||||
|
||||
mutable Array<int> m_conn; // only works for linear elements that have 4 vertices!
|
||||
mutable DenseMatrix* coordsm;
|
||||
mutable SparseMatrix* M;
|
||||
|
||||
mutable std::vector<SparseMatrix>* dM;
|
||||
|
||||
Array<int> Dirichlet_dof;
|
||||
Array<double> Dirichlet_val;
|
||||
|
||||
public:
|
||||
Mesh * GetMesh1() {return mesh1;}
|
||||
Mesh * GetMesh2() {return mesh2;}
|
||||
Array<int> GetDirichletDofs() {return Dirichlet_dof;}
|
||||
Array<double> GetDirichletVals() {return Dirichlet_val;}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -22,7 +22,7 @@ using namespace mfem;
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
string mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ int main(int argc, char *argv[])
|
||||
Hypre::Init();
|
||||
|
||||
// 2. Parse command line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
string mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
/** Mass integrator (u⋅d, v⋅d) restricted to the boundary of a domain */
|
||||
class VectorBoundaryDirectionalMassIntegrator: public BilinearFormIntegrator
|
||||
{
|
||||
private:
|
||||
VectorCoefficient &direction;
|
||||
int vdim;
|
||||
int oa, ob;
|
||||
const double k;
|
||||
|
||||
public:
|
||||
/// Construct an integrator with coefficient 1.0
|
||||
VectorBoundaryDirectionalMassIntegrator(const double k,
|
||||
VectorCoefficient &direction,
|
||||
const int oa=1, const int ob=1)
|
||||
: k(k), vdim(direction.GetVDim()), direction(direction),
|
||||
oa(oa), ob(ob) { }
|
||||
|
||||
using BilinearFormIntegrator::AssembleElementMatrix;
|
||||
virtual void AssembleElementMatrix(const FiniteElement &el,
|
||||
ElementTransformation &Tr,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
int dof = el.GetDof();
|
||||
Vector shape(dof), vec(vdim);
|
||||
|
||||
out << Tr.Attribute - 1 << " " << dof << " LHSElement" << std::endl;
|
||||
elmat.SetSize(dof*vdim);
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int intorder = oa * el.GetOrder() + ob; // <------ user control
|
||||
ir = &IntRules.Get(Tr.GetGeometryType(), intorder); // of integration order
|
||||
}
|
||||
|
||||
DenseMatrix elmat_scalar(dof);
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
|
||||
// Set the integration point in the face and the neighboring element
|
||||
Tr.SetIntPoint(&ip);
|
||||
|
||||
// Access the neighboring element's integration point
|
||||
direction.Eval(vec, Tr, ip);
|
||||
double val = k*Tr.Weight() * ip.weight;
|
||||
|
||||
el.CalcShape(ip, shape);
|
||||
MultVVt(shape, elmat_scalar);
|
||||
for (int row = 0; row < vdim; row++)
|
||||
{
|
||||
for (int col = 0; col < vdim; col++)
|
||||
{
|
||||
elmat.AddMatrix(val*vec(row)*vec(col), elmat_scalar, dof*row, dof*col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using BilinearFormIntegrator::AssembleFaceMatrix;
|
||||
virtual void AssembleFaceMatrix(const FiniteElement &el,
|
||||
const FiniteElement &dummy,
|
||||
FaceElementTransformations &Tr,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
int dof = el.GetDof();
|
||||
Vector shape(dof), vec(vdim);
|
||||
|
||||
out << Tr.Attribute - 1 << " " << dof << " LHSFace" << std::endl;
|
||||
|
||||
elmat.SetSize(dof*vdim);
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int intorder = oa * el.GetOrder() + ob; // <------ user control
|
||||
ir = &IntRules.Get(Tr.FaceGeom, intorder); // of integration order
|
||||
}
|
||||
|
||||
DenseMatrix elmat_scalar(dof);
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
|
||||
// Set the integration point in the face and the neighboring element
|
||||
Tr.SetAllIntPoints(&ip);
|
||||
|
||||
// Access the neighboring element's integration point
|
||||
const IntegrationPoint &eip = Tr.GetElement1IntPoint();
|
||||
|
||||
direction.Eval(vec, *Tr.Face, ip);
|
||||
double val = k*Tr.Face->Weight() * ip.weight;
|
||||
|
||||
el.CalcShape(eip, shape);
|
||||
|
||||
for (int row = 0; row < vdim; row++)
|
||||
{
|
||||
for (int col = 0; col < vdim; col++)
|
||||
{
|
||||
elmat.AddMatrix(val*vec(row)*vec(col), elmat_scalar, dof*row, dof*col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/** Mass integrator (u⋅n, v⋅n) restricted to the boundary of a domain */
|
||||
class VectorBoundaryDirectionalLFIntegrator : public LinearFormIntegrator
|
||||
{
|
||||
VectorCoefficient &direction, &force;
|
||||
int oa, ob, vdim;
|
||||
public:
|
||||
/** @brief Constructs a boundary integrator with a given Coefficient @a QG.
|
||||
Integration order will be @a a * basis_order + @a b. */
|
||||
VectorBoundaryDirectionalLFIntegrator(VectorCoefficient &direction,
|
||||
VectorCoefficient &force,
|
||||
int a = 1, int b = 1)
|
||||
: direction(direction), force(force), oa(a), ob(b), vdim(direction.GetVDim()) { }
|
||||
|
||||
/** Given a particular boundary Finite Element and a transformation (Tr)
|
||||
computes the element boundary vector, elvect. */
|
||||
using LinearFormIntegrator::AssembleRHSElementVect;
|
||||
virtual void AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
{
|
||||
int dof = el.GetDof();
|
||||
|
||||
out << Tr.Attribute - 1 << " " << dof << " RHSElement" << std::endl;
|
||||
|
||||
Vector shape(dof), vec(vdim), vecF(vdim);
|
||||
elvect.SetSize(dof*vdim);
|
||||
elvect = 0.0;
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int intorder = oa * el.GetOrder() + ob; // <------ user control
|
||||
ir = &IntRules.Get(Tr.GetGeometryType(), intorder); // of integration order
|
||||
}
|
||||
double * data = elvect.GetData();
|
||||
Vector elvect_loc(data, dof);
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
|
||||
direction.Eval(vec, Tr, ip);
|
||||
force.Eval(vecF, Tr, ip);
|
||||
double val = Tr.Weight() * ip.weight * (vec * vecF);
|
||||
|
||||
el.CalcShape(ip, shape);
|
||||
for (int row = 0; row < vdim; row++)
|
||||
{
|
||||
elvect_loc.SetData(data + dof*row);
|
||||
elvect_loc.Add(val*vec(row), shape);
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void AssembleRHSElementVect(
|
||||
const FiniteElement &el, FaceElementTransformations &Tr, Vector &elvect)
|
||||
{
|
||||
int dof = el.GetDof();
|
||||
|
||||
out << Tr.Attribute - 1 << " " << dof << " RHSFace" << std::endl;
|
||||
|
||||
Vector shape(dof), vec(vdim), vecF(vdim);
|
||||
elvect.SetSize(dof*vdim);
|
||||
elvect = 0.0;
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int intorder = oa * el.GetOrder() + ob; // <------ user control
|
||||
ir = &IntRules.Get(Tr.FaceGeom, intorder); // of integration order
|
||||
}
|
||||
double * data = elvect.GetData();
|
||||
Vector elvect_loc(data, dof);
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
|
||||
// Set the integration point in the face and the neighboring element
|
||||
Tr.SetAllIntPoints(&ip);
|
||||
|
||||
// Access the neighboring element's integration point
|
||||
const IntegrationPoint &eip = Tr.GetElement1IntPoint();
|
||||
|
||||
direction.Eval(vec, Tr, ip);
|
||||
force.Eval(vecF, Tr, ip);
|
||||
double val = Tr.Face->Weight() * ip.weight * (vec * vecF);
|
||||
|
||||
el.CalcShape(eip, shape);
|
||||
for (int row = 0; row < vdim; row++)
|
||||
{
|
||||
elvect_loc.SetData(data + dof*row);
|
||||
elvect_loc.Add(val*vec(row), shape);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
enum BdrType
|
||||
{
|
||||
Fixed,
|
||||
XRoller,
|
||||
YRoller,
|
||||
ZRoller,
|
||||
Input,
|
||||
Output,
|
||||
Free,
|
||||
NumBdr
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int p=1;
|
||||
int nel = 40;
|
||||
int numelx = nel*2;
|
||||
int numely = nel;
|
||||
const double len = nel*0.025; // fixed, input, output boundary length
|
||||
// Setup spring
|
||||
double input_spring = 1;
|
||||
double output_spring = 0.0001;
|
||||
Vector input_direction(2), output_direction(2);
|
||||
input_direction = 0.0; output_direction = 0.0;
|
||||
input_direction[0] = 1.0;
|
||||
output_direction[0] = -1.0;
|
||||
|
||||
|
||||
// Mesh
|
||||
Mesh mesh = mesh.MakeCartesian2D(numelx, numely,
|
||||
mfem::Element::Type::QUADRILATERAL,
|
||||
true,
|
||||
(double)numelx, (double)numely);
|
||||
// Setup boundary
|
||||
//
|
||||
// ooooooooooooooooooooooo <- x roller (Y fixed)
|
||||
// Input -> II II <- Output
|
||||
// | |
|
||||
// | |
|
||||
// Fixed -> II--------------------|
|
||||
//
|
||||
// Otherwise, free.
|
||||
Array2D<int> ess_bdr(mesh.SpaceDimension() + 1, BdrType::NumBdr); // [X-fixed; Y-fixed; All-fixed]
|
||||
ess_bdr = 0;
|
||||
ess_bdr(0, BdrType::YRoller) = 1; // y-roller - x direction fixed
|
||||
ess_bdr(1, BdrType::XRoller) = 1; // x-roller - y direction fixed
|
||||
ess_bdr(2, BdrType::Fixed) = 1; // all direction fixed
|
||||
Array<int> input_bdr(BdrType::NumBdr), output_bdr(BdrType::NumBdr);
|
||||
input_bdr = 0; output_bdr = 0;
|
||||
input_bdr[BdrType::Input] = 1; output_bdr[BdrType::Output] = 1;
|
||||
// To ensure that there are input/output boundaries
|
||||
int nrInputBdrFace = 0;
|
||||
int nrOutputBdrFace = 0;
|
||||
// Set boundary attributes
|
||||
for (int i = 0; i<mesh.GetNBE(); i++)
|
||||
{
|
||||
Element * be = mesh.GetBdrElement(i);
|
||||
Array<int> vertices;
|
||||
be->GetVertices(vertices);
|
||||
|
||||
double * coords1 = mesh.GetVertex(vertices[0]);
|
||||
double * coords2 = mesh.GetVertex(vertices[1]);
|
||||
|
||||
Vector fc(2);
|
||||
fc(0) = 0.5*(coords1[0] + coords2[0]);
|
||||
fc(1) = 0.5*(coords1[1] + coords2[1]);
|
||||
|
||||
switch (be->GetAttribute())
|
||||
{
|
||||
case 1: // bottom
|
||||
be->SetAttribute(BdrType::Free + 1);
|
||||
break;
|
||||
case 2: // right
|
||||
if (fc(1) > numely - len)
|
||||
{
|
||||
be->SetAttribute(BdrType::Output + 1);
|
||||
nrOutputBdrFace++;
|
||||
break;
|
||||
}
|
||||
be->SetAttribute(BdrType::Free + 1);
|
||||
break;
|
||||
case 3: // top
|
||||
be->SetAttribute(BdrType::XRoller + 1);
|
||||
break;
|
||||
case 4: // left
|
||||
if (fc(1) > numely - len)
|
||||
{
|
||||
be->SetAttribute(BdrType::Input + 1);
|
||||
nrInputBdrFace++;
|
||||
break;
|
||||
}
|
||||
else if (fc(1) < len)
|
||||
{
|
||||
be->SetAttribute(BdrType::Fixed + 1);
|
||||
break;
|
||||
}
|
||||
be->SetAttribute(BdrType::Free + 1);
|
||||
break;
|
||||
default:
|
||||
mfem_error("Something went wrong");
|
||||
}
|
||||
}
|
||||
mesh.SetAttributes();
|
||||
|
||||
out << "(# Input, # Output) = (" << nrInputBdrFace << ", " << nrOutputBdrFace << ")" << std::endl;
|
||||
|
||||
|
||||
|
||||
H1_FECollection fec(p);
|
||||
FiniteElementSpace fes(&mesh, &fec, mesh.SpaceDimension(), Ordering::byNODES);
|
||||
VectorConstantCoefficient output_d_cf(output_direction), input_d_cf(input_direction);
|
||||
for(int i=0; i<10; i++)
|
||||
{
|
||||
// Expected output for each iteration:
|
||||
// BdrType::Input (p+1)*dim LHSFace
|
||||
// BdrType::Output (p+1)*dim LHSFace
|
||||
// BdrType::Input (p+1)*dim RHSFace
|
||||
// BdrType::Output (p+1)*dim RHSFace
|
||||
//
|
||||
// When p = 1 and dim = 2,
|
||||
// 4 4 LHSFace
|
||||
// 5 4 LHSFace
|
||||
// 4 4 RHSFace
|
||||
// 5 4 RHSFace
|
||||
//
|
||||
out << i << std::endl;
|
||||
|
||||
LinearForm b(&fes);
|
||||
b.AddBdrFaceIntegrator(new VectorBoundaryDirectionalLFIntegrator(input_d_cf, input_d_cf), input_bdr);
|
||||
b.AddBdrFaceIntegrator(new VectorBoundaryDirectionalLFIntegrator(output_d_cf, output_d_cf), output_bdr);
|
||||
b.Assemble();
|
||||
|
||||
BilinearForm a(&fes);
|
||||
a.AddBdrFaceIntegrator(new VectorBoundaryDirectionalMassIntegrator(input_spring, input_d_cf), input_bdr);
|
||||
a.AddBdrFaceIntegrator(new VectorBoundaryDirectionalMassIntegrator(output_spring, output_d_cf), output_bdr);
|
||||
a.Assemble();
|
||||
|
||||
out << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ set(SRCS
|
||||
integ/bilininteg_diffusion_mf.cpp
|
||||
integ/bilininteg_diffusion_pa.cpp
|
||||
integ/bilininteg_diffusion_ea.cpp
|
||||
integ/bilininteg_diffusion_patch.cpp
|
||||
integ/bilininteg_divdiv_pa.cpp
|
||||
integ/bilininteg_gradient_pa.cpp
|
||||
integ/bilininteg_interp_pa.cpp
|
||||
|
||||
+49
-3
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "fem.hpp"
|
||||
#include "../general/device.hpp"
|
||||
#include "../mesh/nurbs.hpp"
|
||||
#include <cmath>
|
||||
|
||||
namespace mfem
|
||||
@@ -421,23 +422,30 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
"invalid element marker for domain integrator #"
|
||||
<< k << ", counting from zero");
|
||||
}
|
||||
|
||||
if (domain_integs[k]->Patchwise())
|
||||
{
|
||||
MFEM_VERIFY(fes->GetNURBSext(), "Patchwise integration requires a "
|
||||
<< "NURBS FE space");
|
||||
}
|
||||
}
|
||||
|
||||
// Element-wise integration
|
||||
for (int i = 0; i < fes -> GetNE(); i++)
|
||||
{
|
||||
int elem_attr = fes->GetMesh()->GetAttribute(i);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
if (element_matrices)
|
||||
{
|
||||
elmat_p = &(*element_matrices)(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int elem_attr = fes->GetMesh()->GetAttribute(i);
|
||||
elmat.SetSize(0);
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
if ( domain_integs_marker[k] == NULL ||
|
||||
if ((domain_integs_marker[k] == NULL ||
|
||||
(*(domain_integs_marker[k]))[elem_attr-1] == 1)
|
||||
&& !domain_integs[k]->Patchwise())
|
||||
{
|
||||
const FiniteElement &fe = *fes->GetFE(i);
|
||||
eltrans = fes->GetElementTransformation(i);
|
||||
@@ -460,6 +468,7 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
{
|
||||
elmat_p = &elmat;
|
||||
}
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(elmat);
|
||||
@@ -479,6 +488,43 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Patch-wise integration
|
||||
if (fes->GetNURBSext())
|
||||
{
|
||||
for (int p=0; p<mesh->NURBSext->GetNP(); ++p)
|
||||
{
|
||||
bool vdofsSet = false;
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
if (domain_integs[k]->Patchwise())
|
||||
{
|
||||
if (!vdofsSet)
|
||||
{
|
||||
fes->GetPatchVDofs(p, vdofs);
|
||||
vdofsSet = true;
|
||||
}
|
||||
|
||||
SparseMatrix* spmat = nullptr;
|
||||
domain_integs[k]->AssemblePatchMatrix(p, *fes, spmat);
|
||||
Array<int> cols;
|
||||
Vector srow;
|
||||
|
||||
for (int r=0; r<spmat->Height(); ++r)
|
||||
{
|
||||
spmat->GetRow(r, cols, srow);
|
||||
for (int i=0; i<cols.Size(); ++i)
|
||||
{
|
||||
cols[i] = vdofs[cols[i]];
|
||||
}
|
||||
mat->AddRow(vdofs[r], cols, srow);
|
||||
}
|
||||
|
||||
delete spmat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (boundary_integs.Size())
|
||||
|
||||
@@ -299,7 +299,16 @@ void PABilinearFormExtension::Assemble()
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
for (BilinearFormIntegrator *integ : integrators)
|
||||
{
|
||||
integ->AssemblePA(*a->FESpace());
|
||||
if (integ->Patchwise())
|
||||
{
|
||||
MFEM_VERIFY(a->FESpace()->GetNURBSext(),
|
||||
"Patchwise integration requires a NURBS FE space");
|
||||
integ->AssembleNURBSPA(*a->FESpace());
|
||||
}
|
||||
else
|
||||
{
|
||||
integ->AssemblePA(*a->FESpace());
|
||||
}
|
||||
}
|
||||
|
||||
Array<BilinearFormIntegrator*> &bdr_integrators = *a->GetBBFI();
|
||||
@@ -410,13 +419,39 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
|
||||
const int iSz = integrators.Size();
|
||||
if (DeviceCanUseCeed() || !elem_restrict)
|
||||
|
||||
bool allPatchwise = true;
|
||||
bool somePatchwise = false;
|
||||
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
if (integrators[i]->Patchwise())
|
||||
{
|
||||
somePatchwise = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
allPatchwise = false;
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(!(somePatchwise && !allPatchwise),
|
||||
"All or none of the integrators should be patchwise");
|
||||
|
||||
if (DeviceCanUseCeed() || !elem_restrict || allPatchwise)
|
||||
{
|
||||
y.UseDevice(true); // typically this is a large vector, so store on device
|
||||
y = 0.0;
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
integrators[i]->AddMultPA(x, y);
|
||||
if (integrators[i]->Patchwise())
|
||||
{
|
||||
integrators[i]->AddMultNURBSPA(x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
integrators[i]->AddMultPA(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
+43
-6
@@ -26,6 +26,12 @@ void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&)
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleNURBSPA(const FiniteElementSpace&)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::AssembleNURBSPA(fes)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&,
|
||||
const FiniteElementSpace&)
|
||||
{
|
||||
@@ -92,7 +98,13 @@ void BilinearFormIntegrator::AssembleDiagonalPA_ADAt(const Vector &, Vector &)
|
||||
|
||||
void BilinearFormIntegrator::AddMultPA(const Vector &, Vector &) const
|
||||
{
|
||||
MFEM_ABORT("BilinearFormIntegrator::MultAssembled(...)\n"
|
||||
MFEM_ABORT("BilinearFormIntegrator:AddMultPA:(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AddMultNURBSPA(const Vector &, Vector &) const
|
||||
{
|
||||
MFEM_ABORT("BilinearFormIntegrator::AddMultNURBSPA(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
@@ -126,23 +138,30 @@ void BilinearFormIntegrator::AssembleDiagonalMF(Vector &)
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleElementMatrix (
|
||||
void BilinearFormIntegrator::AssembleElementMatrix(
|
||||
const FiniteElement &el, ElementTransformation &Trans,
|
||||
DenseMatrix &elmat )
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
MFEM_ABORT("BilinearFormIntegrator::AssembleElementMatrix(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleElementMatrix2 (
|
||||
void BilinearFormIntegrator::AssembleElementMatrix2(
|
||||
const FiniteElement &el1, const FiniteElement &el2,
|
||||
ElementTransformation &Trans, DenseMatrix &elmat )
|
||||
ElementTransformation &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
MFEM_ABORT("BilinearFormIntegrator::AssembleElementMatrix2(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleFaceMatrix (
|
||||
void BilinearFormIntegrator::AssemblePatchMatrix(
|
||||
const int patch, const FiniteElementSpace &fes, SparseMatrix*& smat)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::AssemblePatchMatrix(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleFaceMatrix(
|
||||
const FiniteElement &el1, const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
@@ -848,6 +867,19 @@ void DiffusionIntegrator::AssembleElementMatrix
|
||||
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
|
||||
|
||||
const NURBSFiniteElement *NURBSFE =
|
||||
dynamic_cast<const NURBSFiniteElement *>(&el);
|
||||
|
||||
bool deleteRule = false;
|
||||
if (NURBSFE && patchRules)
|
||||
{
|
||||
const int patch = NURBSFE->GetPatch();
|
||||
const int* ijk = NURBSFE->GetIJK();
|
||||
Array<const KnotVector*>& kv = NURBSFE->KnotVectors();
|
||||
ir = &patchRules->GetElementRule(NURBSFE->GetElement(), patch, ijk, kv,
|
||||
deleteRule);
|
||||
}
|
||||
|
||||
elmat = 0.0;
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
@@ -882,6 +914,11 @@ void DiffusionIntegrator::AssembleElementMatrix
|
||||
AddMult_a_AAt(w, dshapedxt, elmat);
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteRule)
|
||||
{
|
||||
delete ir;
|
||||
}
|
||||
}
|
||||
|
||||
void DiffusionIntegrator::AssembleElementMatrix2(
|
||||
|
||||
@@ -61,6 +61,11 @@ public:
|
||||
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes);
|
||||
|
||||
/// Method defining partial assembly on NURBS patches.
|
||||
/** The result of the partial assembly is stored internally so that it can be
|
||||
used later in the method AddMultNURBSPA(). */
|
||||
virtual void AssembleNURBSPA(const FiniteElementSpace &fes);
|
||||
|
||||
virtual void AssemblePABoundary(const FiniteElementSpace &fes);
|
||||
|
||||
virtual void AssemblePAInteriorFaces(const FiniteElementSpace &fes);
|
||||
@@ -82,6 +87,9 @@ public:
|
||||
called. */
|
||||
virtual void AddMultPA(const Vector &x, Vector &y) const;
|
||||
|
||||
/// Method for partially assembled action on NURBS patches.
|
||||
virtual void AddMultNURBSPA(const Vector&x, Vector&y) const;
|
||||
|
||||
/// Method for partially assembled transposed action.
|
||||
/** Perform the transpose action of integrator on the input @a x and add the
|
||||
result to the output @a y. Both @a x and @a y are E-vectors, i.e. they
|
||||
@@ -148,6 +156,13 @@ public:
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
/** Given a particular NURBS patch, computes the patch matrix as a
|
||||
SparseMatrix @a smat.
|
||||
*/
|
||||
virtual void AssemblePatchMatrix(const int patch,
|
||||
const FiniteElementSpace &fes,
|
||||
SparseMatrix*& smat);
|
||||
|
||||
virtual void AssembleFaceMatrix(const FiniteElement &el1,
|
||||
const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans,
|
||||
@@ -2111,6 +2126,59 @@ private:
|
||||
Vector pa_data;
|
||||
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
|
||||
|
||||
// Data for NURBS patch PA
|
||||
|
||||
// Type for a variable-row-length 2D array, used for data related to 1D
|
||||
// quadrature rules in each dimension.
|
||||
typedef std::vector<std::vector<int>> IntArrayVar2D;
|
||||
|
||||
int numPatches = 0;
|
||||
static constexpr int numTypes = 2; // Number of rule types
|
||||
|
||||
// In the case integrationMode == Mode::PATCHWISE_REDUCED, an approximate
|
||||
// integration rule with sparse nonzero weights is computed by NNLSSolver,
|
||||
// for each 1D basis function on each patch, in each spatial dimension. For a
|
||||
// fixed 1D basis function b_i with DOF index i, in the tensor product basis
|
||||
// of patch p, the prescribed exact 1D rule is of the form
|
||||
// \sum_k a_{i,j,k} w_k for some integration points indexed by k, with
|
||||
// weights w_k and coefficients a_{i,j,k} depending on Q(x), an element
|
||||
// transformation, b_i, and b_j, for all 1D basis functions b_j whose support
|
||||
// overlaps that of b_i. Define the constraint matrix G = [g_{j,k}] with
|
||||
// g_{j,k} = a_{i,j,k} and the vector of exact weights w = [w_k]. A reduced
|
||||
// rule should have different weights w_r, many of them zero, and should
|
||||
// approximately satisfy Gw_r = Gw. A sparse approximate solution to this
|
||||
// underdetermined system is computed by NNLSSolver, and its data is stored
|
||||
// in the following members.
|
||||
|
||||
// For each patch p, spatial dimension d (total dim), and rule type t (total
|
||||
// numTypes), an std::vector<Vector> of reduced quadrature weights for all
|
||||
// basis functions is stored in reducedWeights[t + numTypes * (d + dim * p)],
|
||||
// reshaped as rw(t,d,p). Note that nd may vary with respect to the patch and
|
||||
// spatial dimension. Array reducedIDs is treated similarly.
|
||||
std::vector<std::vector<Vector>> reducedWeights;
|
||||
std::vector<IntArrayVar2D> reducedIDs;
|
||||
std::vector<Array<int>> pQ1D, pD1D;
|
||||
std::vector<std::vector<Array2D<double>>> pB, pG;
|
||||
std::vector<IntArrayVar2D> pminD, pmaxD, pminQ, pmaxQ, pminDD, pmaxDD;
|
||||
|
||||
std::vector<Array<const IntegrationRule*>> pir1d;
|
||||
|
||||
void SetupPatchPA(const int patch, Mesh *mesh, bool unitWeights=false);
|
||||
|
||||
void SetupPatchBasisData(Mesh *mesh, unsigned int patch);
|
||||
|
||||
/** Called by AssemblePatchMatrix for sparse matrix assembly on a NURBS patch
|
||||
with full 1D quadrature rules. */
|
||||
void AssemblePatchMatrix_fullQuadrature(const int patch,
|
||||
const FiniteElementSpace &fes,
|
||||
SparseMatrix*& smat);
|
||||
|
||||
/** Called by AssemblePatchMatrix for sparse matrix assembly on a NURBS patch
|
||||
with reduced 1D quadrature rules. */
|
||||
void AssemblePatchMatrix_reducedQuadrature(const int patch,
|
||||
const FiniteElementSpace &fes,
|
||||
SparseMatrix*& smat);
|
||||
|
||||
public:
|
||||
/// Construct a diffusion integrator with coefficient Q = 1
|
||||
DiffusionIntegrator(const IntegrationRule *ir = nullptr)
|
||||
@@ -2146,6 +2214,14 @@ public:
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
virtual void AssemblePatchMatrix(const int patch,
|
||||
const FiniteElementSpace &fes,
|
||||
SparseMatrix*& smat);
|
||||
|
||||
virtual void AssembleNURBSPA(const FiniteElementSpace &fes);
|
||||
|
||||
void AssemblePatchPA(const int patch, const FiniteElementSpace &fes);
|
||||
|
||||
/// Perform the local action of the BilinearFormIntegrator
|
||||
virtual void AssembleElementVector(const FiniteElement &el,
|
||||
ElementTransformation &Tr,
|
||||
@@ -2180,6 +2256,10 @@ public:
|
||||
|
||||
virtual void AddMultTransposePA(const Vector&, Vector&) const;
|
||||
|
||||
virtual void AddMultNURBSPA(const Vector&, Vector&) const;
|
||||
|
||||
void AddMultPatchPA(const int patch, const Vector &x, Vector &y) const;
|
||||
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe);
|
||||
|
||||
|
||||
@@ -922,7 +922,7 @@ void ParaViewDataCollection::Save()
|
||||
{
|
||||
const std::string &field_name = qfield.first;
|
||||
std::ofstream os(vtu_prefix + GenerateVTUFileName(field_name, myid));
|
||||
qfield.second->SaveVTU(os, pv_data_format, GetCompressionLevel());
|
||||
qfield.second->SaveVTU(os, pv_data_format, GetCompressionLevel(), field_name);
|
||||
}
|
||||
|
||||
// MPI rank 0 also creates a "PVTU" file that points to all of the separately
|
||||
|
||||
@@ -56,6 +56,10 @@ public:
|
||||
Vector &Weights () const { return weights; }
|
||||
/// Update the NURBSFiniteElement according to the currently set knot vectors
|
||||
virtual void SetOrder () const { }
|
||||
|
||||
/// Returns the indices (i,j) in 2D or (i,j,k) in 3D of this element in the
|
||||
/// tensor product ordering of the patch.
|
||||
const int* GetIJK() const { return ijk; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
+17
-17
@@ -1713,7 +1713,7 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
|
||||
H1_Elements[Geometry::SEGMENT] = new H1_SegmentElement(p, btype);
|
||||
}
|
||||
|
||||
SegDofOrd[0] = new int[2*pm1];
|
||||
SegDofOrd[0] = (pm1 > 0) ? new int[2*pm1] : nullptr;
|
||||
SegDofOrd[1] = SegDofOrd[0] + pm1;
|
||||
for (int i = 0; i < pm1; i++)
|
||||
{
|
||||
@@ -1751,7 +1751,7 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
|
||||
|
||||
const int &TriDof = H1_dof[Geometry::TRIANGLE];
|
||||
const int &QuadDof = H1_dof[Geometry::SQUARE];
|
||||
TriDofOrd[0] = new int[6*TriDof];
|
||||
TriDofOrd[0] = (TriDof > 0) ? new int[6*TriDof] : nullptr;
|
||||
for (int i = 1; i < 6; i++)
|
||||
{
|
||||
TriDofOrd[i] = TriDofOrd[i-1] + TriDof;
|
||||
@@ -1772,7 +1772,7 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
|
||||
}
|
||||
}
|
||||
|
||||
QuadDofOrd[0] = new int[8*QuadDof];
|
||||
QuadDofOrd[0] = (QuadDof > 0) ? new int[8*QuadDof] : nullptr;
|
||||
for (int i = 1; i < 8; i++)
|
||||
{
|
||||
QuadDofOrd[i] = QuadDofOrd[i-1] + QuadDof;
|
||||
@@ -1855,7 +1855,7 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
|
||||
H1_Elements[Geometry::PYRAMID] = new LinearPyramidFiniteElement;
|
||||
|
||||
const int &TetDof = H1_dof[Geometry::TETRAHEDRON];
|
||||
TetDofOrd[0] = new int[24*TetDof];
|
||||
TetDofOrd[0] = (TetDof > 0) ? new int[24*TetDof] : nullptr;
|
||||
for (int i = 1; i < 24; i++)
|
||||
{
|
||||
TetDofOrd[i] = TetDofOrd[i-1] + TetDof;
|
||||
@@ -2127,7 +2127,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
|
||||
// No need to set the map_type for Tr_Elements.
|
||||
|
||||
const int pp1 = p + 1;
|
||||
SegDofOrd[0] = new int[2*pp1];
|
||||
SegDofOrd[0] = (pp1 > 0) ? new int[2*pp1] : nullptr;
|
||||
SegDofOrd[1] = SegDofOrd[0] + pp1;
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
@@ -2160,7 +2160,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
|
||||
}
|
||||
|
||||
const int TriDof = L2_Elements[Geometry::TRIANGLE]->GetDof();
|
||||
TriDofOrd[0] = new int[6*TriDof];
|
||||
TriDofOrd[0] = (TriDof > 0) ? new int[6*TriDof] : nullptr;
|
||||
for (int i = 1; i < 6; i++)
|
||||
{
|
||||
TriDofOrd[i] = TriDofOrd[i-1] + TriDof;
|
||||
@@ -2181,7 +2181,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
|
||||
}
|
||||
}
|
||||
const int QuadDof = L2_Elements[Geometry::SQUARE]->GetDof();
|
||||
OtherDofOrd = new int[QuadDof];
|
||||
OtherDofOrd = (QuadDof > 0) ? new int[QuadDof] : nullptr;
|
||||
for (int j = 0; j < QuadDof; j++)
|
||||
{
|
||||
OtherDofOrd[j] = j; // for Or == 0
|
||||
@@ -2225,7 +2225,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
|
||||
const int PriDof = L2_Elements[Geometry::PRISM]->GetDof();
|
||||
const int MaxDof = std::max(TetDof, std::max(PriDof, HexDof));
|
||||
|
||||
TetDofOrd[0] = new int[24*TetDof];
|
||||
TetDofOrd[0] = (TetDof > 0) ? new int[24*TetDof] : nullptr;
|
||||
for (int i = 1; i < 24; i++)
|
||||
{
|
||||
TetDofOrd[i] = TetDofOrd[i-1] + TetDof;
|
||||
@@ -2314,7 +2314,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
|
||||
}
|
||||
}
|
||||
}
|
||||
OtherDofOrd = new int[MaxDof];
|
||||
OtherDofOrd = (MaxDof > 0) ? new int[MaxDof] : nullptr;
|
||||
for (int j = 0; j < MaxDof; j++)
|
||||
{
|
||||
OtherDofOrd[j] = j; // for Or == 0
|
||||
@@ -2502,7 +2502,7 @@ void RT_FECollection::InitFaces(const int p, const int dim_,
|
||||
RT_Elements[Geometry::SEGMENT] = l2_seg;
|
||||
RT_dof[Geometry::SEGMENT] = pp1;
|
||||
|
||||
SegDofOrd[0] = new int[2*pp1];
|
||||
SegDofOrd[0] = (pp1 > 0) ? new int[2*pp1] : nullptr;
|
||||
SegDofOrd[1] = SegDofOrd[0] + pp1;
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
@@ -2523,7 +2523,7 @@ void RT_FECollection::InitFaces(const int p, const int dim_,
|
||||
RT_dof[Geometry::SQUARE] = pp1*pp1;
|
||||
|
||||
int TriDof = RT_dof[Geometry::TRIANGLE];
|
||||
TriDofOrd[0] = new int[6*TriDof];
|
||||
TriDofOrd[0] = (TriDof > 0) ? new int[6*TriDof] : nullptr;
|
||||
for (int i = 1; i < 6; i++)
|
||||
{
|
||||
TriDofOrd[i] = TriDofOrd[i-1] + TriDof;
|
||||
@@ -2553,7 +2553,7 @@ void RT_FECollection::InitFaces(const int p, const int dim_,
|
||||
}
|
||||
|
||||
int QuadDof = RT_dof[Geometry::SQUARE];
|
||||
QuadDofOrd[0] = new int[8*QuadDof];
|
||||
QuadDofOrd[0] = (QuadDof > 0) ? new int[8*QuadDof] : nullptr;
|
||||
for (int i = 1; i < 8; i++)
|
||||
{
|
||||
QuadDofOrd[i] = QuadDofOrd[i-1] + QuadDof;
|
||||
@@ -2749,7 +2749,7 @@ ND_FECollection::ND_FECollection(const int p, const int dim,
|
||||
ND_Elements[Geometry::SEGMENT] = new ND_SegmentElement(p, ob_type);
|
||||
ND_dof[Geometry::SEGMENT] = p;
|
||||
|
||||
SegDofOrd[0] = new int[2*p];
|
||||
SegDofOrd[0] = (p > 0) ? new int[2*p] : nullptr;
|
||||
SegDofOrd[1] = SegDofOrd[0] + p;
|
||||
for (int i = 0; i < p; i++)
|
||||
{
|
||||
@@ -2769,7 +2769,7 @@ ND_FECollection::ND_FECollection(const int p, const int dim,
|
||||
ND_dof[Geometry::TRIANGLE] = p*pm1;
|
||||
|
||||
int QuadDof = ND_dof[Geometry::SQUARE];
|
||||
QuadDofOrd[0] = new int[8*QuadDof];
|
||||
QuadDofOrd[0] = (QuadDof > 0) ? new int[8*QuadDof] : nullptr;
|
||||
for (int i = 1; i < 8; i++)
|
||||
{
|
||||
QuadDofOrd[i] = QuadDofOrd[i-1] + QuadDof;
|
||||
@@ -2813,7 +2813,7 @@ ND_FECollection::ND_FECollection(const int p, const int dim,
|
||||
}
|
||||
|
||||
int TriDof = ND_dof[Geometry::TRIANGLE];
|
||||
TriDofOrd[0] = new int[6*TriDof];
|
||||
TriDofOrd[0] = (TriDof > 0) ? new int[6*TriDof] : nullptr;
|
||||
for (int i = 1; i < 6; i++)
|
||||
{
|
||||
TriDofOrd[i] = TriDofOrd[i-1] + TriDof;
|
||||
@@ -3163,7 +3163,7 @@ ND_R2D_FECollection::ND_R2D_FECollection(const int p, const int dim,
|
||||
ob_type);
|
||||
ND_dof[Geometry::SEGMENT] = 2 * p - 1;
|
||||
|
||||
SegDofOrd[0] = new int[4 * p - 2];
|
||||
SegDofOrd[0] = (4*p > 2) ? new int[4 * p - 2] : nullptr;
|
||||
SegDofOrd[1] = SegDofOrd[0] + 2 * p - 1;
|
||||
for (int i = 0; i < p; i++)
|
||||
{
|
||||
@@ -3347,7 +3347,7 @@ void RT_R2D_FECollection::InitFaces(const int p, const int dim,
|
||||
RT_Elements[Geometry::SEGMENT] = l2_seg;
|
||||
RT_dof[Geometry::SEGMENT] = pp1;
|
||||
|
||||
SegDofOrd[0] = new int[2*pp1];
|
||||
SegDofOrd[0] = (pp1 > 0) ? new int[2*pp1] : nullptr;
|
||||
SegDofOrd[1] = SegDofOrd[0] + pp1;
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
|
||||
+22
-3
@@ -309,6 +309,12 @@ FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs) const
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetPatchVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
GetPatchDofs(i, vdofs);
|
||||
DofsToVDofs(vdofs);
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetFaceVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
GetFaceDofs(i, vdofs);
|
||||
@@ -2801,11 +2807,24 @@ FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
|
||||
return DoFTrans[mesh->GetElementBaseGeometry(elem)];
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetPatchDofs(int patch, Array<int> &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(NURBSext,
|
||||
"FiniteElementSpace::GetPatchDofs needs a NURBSExtension");
|
||||
NURBSext->GetPatchDofs(patch, dofs);
|
||||
}
|
||||
|
||||
const FiniteElement *FiniteElementSpace::GetFE(int i) const
|
||||
{
|
||||
if (i < 0 || !mesh->GetNE()) { return NULL; }
|
||||
MFEM_VERIFY(i < mesh->GetNE(),
|
||||
"Invalid element id " << i << ", maximum allowed " << mesh->GetNE()-1);
|
||||
if (i < 0 || i >= mesh->GetNE())
|
||||
{
|
||||
if (mesh->GetNE() == 0)
|
||||
{
|
||||
MFEM_ABORT("Empty MPI partitions are not permitted!");
|
||||
}
|
||||
MFEM_ABORT("Invalid element id:" << i << "; minimum allowed:" << 0 <<
|
||||
", maximum allowed:" << mesh->GetNE()-1);
|
||||
}
|
||||
|
||||
const FiniteElement *FE =
|
||||
fec->GetFE(mesh->GetElementGeometry(i), GetElementOrderImpl(i));
|
||||
|
||||
+12
-2
@@ -811,6 +811,11 @@ public:
|
||||
virtual DofTransformation *GetBdrElementDofs(int bel,
|
||||
Array<int> &dofs) const;
|
||||
|
||||
/** @brief Returns indices of degrees of freedom for NURBS patch index
|
||||
@a patch. Cartesian ordering is used, for the tensor-product degrees of
|
||||
freedom. */
|
||||
void GetPatchDofs(int patch, Array<int> &dofs) const;
|
||||
|
||||
/// @brief Returns the indices of the degrees of freedom for the specified
|
||||
/// face, including the DOFs for the edges and the vertices of the face.
|
||||
///
|
||||
@@ -995,7 +1000,7 @@ public:
|
||||
|
||||
/// @brief Returns indices of degrees of freedom for the @a i'th element.
|
||||
/// The returned indices are offsets into an @ref ldof vector with @b vdim
|
||||
/// not necessarily equal to 1. The returned indexes are always ordered
|
||||
/// not necessarily equal to 1. The returned indices are always ordered
|
||||
/// byNODES, irrespective of whether the space is byNODES or byVDIM.
|
||||
/// See also GetElementDofs().
|
||||
///
|
||||
@@ -1024,6 +1029,9 @@ public:
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
DofTransformation *GetBdrElementVDofs(int i, Array<int> &vdofs) const;
|
||||
|
||||
/// Returns indices of degrees of freedom in @a vdofs for NURBS patch @a i.
|
||||
void GetPatchVDofs(int i, Array<int> &vdofs) const;
|
||||
|
||||
/// @brief Returns the indices of the degrees of freedom for the specified
|
||||
/// face, including the DOFs for the edges and the vertices of the face.
|
||||
///
|
||||
@@ -1107,7 +1115,9 @@ public:
|
||||
int GetLocalDofForDof(int i) const { return dof_ldof_array[i]; }
|
||||
|
||||
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
|
||||
associated with i'th element in the mesh object. */
|
||||
associated with i'th element in the mesh object.
|
||||
Note: The method has been updated to abort instead of returning NULL for
|
||||
an empty partition. */
|
||||
virtual const FiniteElement *GetFE(int i) const;
|
||||
|
||||
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
|
||||
|
||||
+3
-295
@@ -1236,7 +1236,7 @@ void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
gsl_ref.SetSize(points_cnt * dim);
|
||||
gsl_dist.SetSize(points_cnt);
|
||||
|
||||
auto xvFill = [&](const double *xv_base[], unsigned xv_stride[], int dim)
|
||||
auto xvFill = [&](const double *xv_base[], unsigned xv_stride[])
|
||||
{
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
@@ -1256,7 +1256,7 @@ void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
{
|
||||
const double *xv_base[2];
|
||||
unsigned xv_stride[2];
|
||||
xvFill(xv_base, xv_stride, dim);
|
||||
xvFill(xv_base, xv_stride);
|
||||
findptsms_2(gsl_code.GetData(), sizeof(unsigned int),
|
||||
gsl_proc.GetData(), sizeof(unsigned int),
|
||||
gsl_elem.GetData(), sizeof(unsigned int),
|
||||
@@ -1270,7 +1270,7 @@ void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
{
|
||||
const double *xv_base[3];
|
||||
unsigned xv_stride[3];
|
||||
xvFill(xv_base, xv_stride, dim);
|
||||
xvFill(xv_base, xv_stride);
|
||||
findptsms_3(gsl_code.GetData(), sizeof(unsigned int),
|
||||
gsl_proc.GetData(), sizeof(unsigned int),
|
||||
gsl_elem.GetData(), sizeof(unsigned int),
|
||||
@@ -1308,298 +1308,6 @@ void OversetFindPointsGSLIB::Interpolate(const Vector &point_pos,
|
||||
Interpolate(field_in, field_out);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
GSLIBCommunicator::GSLIBCommunicator(MPI_Comm comm_)
|
||||
: cr(NULL), gsl_comm(NULL)
|
||||
{
|
||||
gsl_comm = new gslib::comm;
|
||||
cr = new gslib::crystal;
|
||||
comm_init(gsl_comm, comm_);
|
||||
crystal_init(cr, gsl_comm);
|
||||
}
|
||||
|
||||
void GSLIBCommunicator::SendData(int dim, const Array<unsigned int> & gsl_proc,
|
||||
const Array<unsigned int> & elem_send,
|
||||
const Vector &ref_send,
|
||||
const Vector &coords_send,
|
||||
const Array<int> &s_conn_send,
|
||||
Array<unsigned int> & proc_recv,
|
||||
Array<unsigned int> & index_recv,
|
||||
Array<unsigned int> & elem_recv,
|
||||
Vector &ref_recv,
|
||||
Vector &coords_recv,
|
||||
Array<int> &s_conn_recv)
|
||||
{
|
||||
int nptsend = gsl_proc.Size();
|
||||
int nptElem = elem_send.Size();
|
||||
int nptRST = ref_send.Size();
|
||||
|
||||
MFEM_VERIFY(nptElem == nptsend,
|
||||
"Incompatible Elem size.");
|
||||
MFEM_VERIFY(nptsend*dim == nptRST,
|
||||
"Incompatible nptRST size.");
|
||||
MFEM_VERIFY(dim <= 3,
|
||||
"Incompatible dimension.");
|
||||
|
||||
// Pack data to send via crystal router
|
||||
struct gslib::array *outpt = new gslib::array;
|
||||
|
||||
struct out_pt { double rst[3], coords[3]; int s_conn; uint index, elem, proc; };
|
||||
struct out_pt *pt;
|
||||
array_init(struct out_pt, outpt, nptsend);
|
||||
outpt->n=nptsend;
|
||||
pt = (struct out_pt *)outpt->ptr;
|
||||
for (int index = 0; index < nptsend; index++)
|
||||
{
|
||||
pt->index = index;
|
||||
pt->elem = elem_send[index];
|
||||
pt->proc = gsl_proc[index];
|
||||
pt->s_conn = s_conn_send[index];
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
pt->rst[d]= ref_send(index*dim + d);
|
||||
pt->coords[d]= coords_send(index + d*nptsend);
|
||||
}
|
||||
++pt;
|
||||
}
|
||||
|
||||
// Transfer data to target MPI ranks
|
||||
sarray_transfer(struct out_pt, outpt, proc, 1, cr);
|
||||
|
||||
// unpack
|
||||
int npt = outpt->n;
|
||||
proc_recv.SetSize(npt);
|
||||
elem_recv.SetSize(npt);
|
||||
index_recv.SetSize(npt);
|
||||
ref_recv.SetSize(npt*dim);
|
||||
coords_recv.SetSize(npt*dim);
|
||||
s_conn_recv.SetSize(npt);
|
||||
|
||||
pt = (struct out_pt *)outpt->ptr;
|
||||
for (int index = 0; index < npt; index++)
|
||||
{
|
||||
index_recv[index] = pt->index;
|
||||
elem_recv[index] = pt->elem;
|
||||
proc_recv[index] = pt->proc;
|
||||
s_conn_recv[index] = pt->s_conn;
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
ref_recv(index*dim + d)= pt->rst[d]; // by VDIM
|
||||
coords_recv(index + d*npt)= pt->coords[d]; // by NODES
|
||||
}
|
||||
++pt;
|
||||
}
|
||||
|
||||
array_free(outpt);
|
||||
delete outpt;
|
||||
}
|
||||
|
||||
void GSLIBCommunicator::SendData2(int dim,
|
||||
const Array<unsigned int> & gsl_proc,
|
||||
const Vector &xyz_send,
|
||||
const Vector &xi_send,
|
||||
const Array<int> &s_conn_send,
|
||||
const Array<int> &conn_send,
|
||||
const DenseMatrix &coords_send,
|
||||
Vector &xyz_recv,
|
||||
Vector &xi_recv,
|
||||
Array<int> &s_conn_recv,
|
||||
Array<int> &conn_recv,
|
||||
DenseMatrix &coords_recv)
|
||||
{
|
||||
int nptsend = gsl_proc.Size();
|
||||
|
||||
struct gslib::array *outpt = new gslib::array;
|
||||
struct out_pt {double xyz[3], xi[2], coords[12]; int s_conn; int conn[4]; uint proc;};
|
||||
struct out_pt *pt;
|
||||
array_init(struct out_pt, outpt, nptsend);
|
||||
outpt->n=nptsend;
|
||||
pt = (struct out_pt *)outpt->ptr;
|
||||
for (int index = 0; index < nptsend; index++)
|
||||
{
|
||||
pt->proc = gsl_proc[index];
|
||||
pt->s_conn = s_conn_send[index];
|
||||
for (int d = 0; d < dim-1; ++d)
|
||||
{
|
||||
pt->xi[d]= xi_send(index*(dim-1) + d);
|
||||
}
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
pt->xyz[d]= xyz_send(index + d*nptsend);
|
||||
}
|
||||
for (int j = 0; j<4; j++)
|
||||
{
|
||||
pt->conn[j] = conn_send[index*4+j];
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
pt->coords[j*dim+d]= coords_send(index*4+j,d);
|
||||
}
|
||||
}
|
||||
++pt;
|
||||
}
|
||||
|
||||
// Transfer data to target MPI ranks
|
||||
sarray_transfer(struct out_pt, outpt, proc, 1, cr);
|
||||
// unpack
|
||||
int npt = outpt->n;
|
||||
xi_recv.SetSize(npt*(dim-1));
|
||||
xyz_recv.SetSize(npt*dim);
|
||||
s_conn_recv.SetSize(npt);
|
||||
conn_recv.SetSize(npt*4);
|
||||
coords_recv.SetSize(npt*4,dim);
|
||||
|
||||
pt = (struct out_pt *)outpt->ptr;
|
||||
for (int index = 0; index < npt; index++)
|
||||
{
|
||||
s_conn_recv[index] = pt->s_conn;
|
||||
for (int d = 0; d < dim-1; ++d)
|
||||
{
|
||||
xi_recv(index*(dim-1) + d) = pt->xi[d];
|
||||
}
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
xyz_recv(index + d*npt)= pt->xyz[d]; // by NODES
|
||||
}
|
||||
for (int j = 0; j<4; j++)
|
||||
{
|
||||
conn_recv[index*4+j] = pt->conn[j];
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
coords_recv(index*4+j,d) = pt->coords[j*dim+d];
|
||||
}
|
||||
}
|
||||
++pt;
|
||||
}
|
||||
array_free(outpt);
|
||||
delete outpt;
|
||||
}
|
||||
|
||||
|
||||
void GSLIBCommunicator::ExchangeNormal(Mesh & mesh,
|
||||
const Array<unsigned int> &gsl_proc,
|
||||
const Array<unsigned int> &gsl_mfem_elem,
|
||||
const Vector &gsl_mfem_ref,
|
||||
Vector &recv_normals)
|
||||
{
|
||||
int dim = mesh.Dimension();
|
||||
int nptsend = gsl_proc.Size();
|
||||
int nptElem = gsl_mfem_elem.Size();
|
||||
int nptRST = gsl_mfem_ref.Size();
|
||||
|
||||
recv_normals.SetSize(nptRST);
|
||||
int nptNormal = recv_normals.Size();
|
||||
|
||||
MFEM_VERIFY(nptElem == nptsend,
|
||||
"Incompatible Elem size.");
|
||||
MFEM_VERIFY(nptsend*dim == nptRST,
|
||||
"Incompatible nptRST size.");
|
||||
MFEM_VERIFY(dim <= 3,
|
||||
"Incompatible dimension.");
|
||||
|
||||
// Pack data to send via crystal router
|
||||
struct gslib::array *outpt = new gslib::array;
|
||||
|
||||
struct out_pt { double rst[3]; uint index, elem, proc; };
|
||||
struct out_pt *pt;
|
||||
array_init(struct out_pt, outpt, nptsend);
|
||||
outpt->n=nptsend;
|
||||
pt = (struct out_pt *)outpt->ptr;
|
||||
for (int index = 0; index < nptsend; index++)
|
||||
{
|
||||
pt->index = index;
|
||||
pt->elem = gsl_mfem_elem[index];
|
||||
pt->proc = gsl_proc[index];
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
pt->rst[d]= gsl_mfem_ref(index*dim + d);
|
||||
}
|
||||
++pt;
|
||||
}
|
||||
|
||||
// Transfer data to target MPI ranks
|
||||
sarray_transfer(struct out_pt, outpt, proc, 1, cr);
|
||||
|
||||
// Get normal vector
|
||||
int npt = outpt->n;
|
||||
pt = (struct out_pt *)outpt->ptr;
|
||||
Vector normal(npt*dim);
|
||||
for (int index = 0; index < npt; index++)
|
||||
{
|
||||
IntegrationPoint ip;
|
||||
ip.Set3(&pt->rst[0]);
|
||||
Vector localval(normal.GetData()+index*dim, dim);
|
||||
// get the normal at this integration point here
|
||||
// for now I just put back this proc's rank + the input rst coordinates
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
localval(d) = gsl_comm->id + pt->rst[d];
|
||||
}
|
||||
++pt;
|
||||
}
|
||||
|
||||
// Save index and proc data in a struct
|
||||
struct gslib::array *savpt = new gslib::array;
|
||||
struct sav_pt { uint index, proc; };
|
||||
struct sav_pt *spt;
|
||||
array_init(struct sav_pt, savpt, npt);
|
||||
savpt->n=npt;
|
||||
spt = (struct sav_pt *)savpt->ptr;
|
||||
pt = (struct out_pt *)outpt->ptr;
|
||||
for (int index = 0; index < npt; index++)
|
||||
{
|
||||
spt->index = pt->index;
|
||||
spt->proc = pt->proc;
|
||||
++pt; ++spt;
|
||||
}
|
||||
|
||||
array_free(outpt);
|
||||
delete outpt;
|
||||
|
||||
// Copy data from save struct to send struct and send component wise
|
||||
struct gslib::array *sendpt = new gslib::array;
|
||||
struct send_pt { double ival; uint index, proc; };
|
||||
struct send_pt *sdpt;
|
||||
for (int j = 0; j < dim; j++)
|
||||
{
|
||||
array_init(struct send_pt, sendpt, npt);
|
||||
sendpt->n=npt;
|
||||
spt = (struct sav_pt *)savpt->ptr;
|
||||
sdpt = (struct send_pt *)sendpt->ptr;
|
||||
for (int index = 0; index < npt; index++)
|
||||
{
|
||||
sdpt->index = spt->index;
|
||||
sdpt->proc = spt->proc;
|
||||
sdpt->ival = normal(j + index*dim);
|
||||
++sdpt; ++spt;
|
||||
}
|
||||
|
||||
sarray_transfer(struct send_pt, sendpt, proc, 1, cr);
|
||||
sdpt = (struct send_pt *)sendpt->ptr;
|
||||
for (int index = 0; index < static_cast<int>(sendpt->n); index++)
|
||||
{
|
||||
int idx = sdpt->index*dim + j;
|
||||
recv_normals(idx) = sdpt->ival;
|
||||
++sdpt;
|
||||
}
|
||||
array_free(sendpt);
|
||||
}
|
||||
array_free(savpt);
|
||||
delete sendpt;
|
||||
delete savpt;
|
||||
}
|
||||
|
||||
void GSLIBCommunicator::FreeData()
|
||||
{
|
||||
crystal_free(cr);
|
||||
}
|
||||
|
||||
GSLIBCommunicator::~GSLIBCommunicator()
|
||||
{
|
||||
delete gsl_comm;
|
||||
delete cr;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
@@ -290,55 +290,6 @@ public:
|
||||
using FindPointsGSLIB::Interpolate;
|
||||
};
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
// Use to send info to certain processes
|
||||
class GSLIBCommunicator
|
||||
{
|
||||
protected:
|
||||
struct gslib::crystal *cr; // gslib's internal data
|
||||
struct gslib::comm *gsl_comm; // gslib's internal data
|
||||
|
||||
public:
|
||||
GSLIBCommunicator(MPI_Comm comm_);
|
||||
|
||||
virtual ~GSLIBCommunicator();
|
||||
|
||||
void ExchangeNormal(Mesh& mesh,
|
||||
const Array<unsigned int> &gsl_proc,
|
||||
const Array<unsigned int> &gsl_mfem_elem,
|
||||
const Vector &gsl_mfem_ref,
|
||||
Vector &recv_normals); //npt*dim
|
||||
|
||||
void SendData(int dim,
|
||||
const Array<unsigned int> & gsl_proc,
|
||||
const Array<unsigned int> & elem_send,
|
||||
const Vector &ref_send,
|
||||
const Vector &coords_send,
|
||||
const Array<int> &s_conn_send,
|
||||
Array<unsigned int> & proc_recv,
|
||||
Array<unsigned int> & index_recv,
|
||||
Array<unsigned int> & elem_recv,
|
||||
Vector &ref_recv,
|
||||
Vector &coords_recv,
|
||||
Array<int> & s_conn_recv);
|
||||
|
||||
void SendData2(int dim,
|
||||
const Array<unsigned int> & gsl_proc,
|
||||
const Vector &xyz_send,
|
||||
const Vector &xi_send,
|
||||
const Array<int> &s_conn_send,
|
||||
const Array<int> &conn_send,
|
||||
const DenseMatrix &coords_send,
|
||||
Vector &xyz_recv,
|
||||
Vector &ref_recv,
|
||||
Array<int> &s_conn_recv,
|
||||
Array<int> &conn_recv,
|
||||
DenseMatrix &coords_recv);
|
||||
|
||||
virtual void FreeData();
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_GSLIB
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "../bilininteg.hpp"
|
||||
#include "../gridfunc.hpp"
|
||||
#include "../qfunction.hpp"
|
||||
#include "../../mesh/nurbs.hpp"
|
||||
#include "../ceed/integrators/diffusion/diffusion.hpp"
|
||||
#include "bilininteg_diffusion_kernels.hpp"
|
||||
|
||||
@@ -74,6 +75,29 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
ir->GetWeights(), geom->J, coeff, pa_data);
|
||||
}
|
||||
|
||||
void DiffusionIntegrator::AssembleNURBSPA(const FiniteElementSpace &fes)
|
||||
{
|
||||
fespace = &fes;
|
||||
Mesh *mesh = fes.GetMesh();
|
||||
dim = mesh->Dimension();
|
||||
MFEM_VERIFY(3 == dim, "Only 3D so far");
|
||||
|
||||
numPatches = mesh->NURBSext->GetNP();
|
||||
for (int p=0; p<numPatches; ++p)
|
||||
{
|
||||
AssemblePatchPA(p, fes);
|
||||
}
|
||||
}
|
||||
|
||||
void DiffusionIntegrator::AssemblePatchPA(const int patch,
|
||||
const FiniteElementSpace &fes)
|
||||
{
|
||||
Mesh *mesh = fes.GetMesh();
|
||||
SetupPatchBasisData(mesh, patch);
|
||||
|
||||
SetupPatchPA(patch, mesh); // For full quadrature, unitWeights = false
|
||||
}
|
||||
|
||||
void DiffusionIntegrator::AssembleDiagonalPA(Vector &diag)
|
||||
{
|
||||
if (DeviceCanUseCeed())
|
||||
@@ -115,4 +139,221 @@ void DiffusionIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
// This version uses full 1D quadrature rules, taking into account the
|
||||
// minimum interaction between basis functions and integration points.
|
||||
void DiffusionIntegrator::AddMultPatchPA(const int patch, const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
MFEM_VERIFY(3 == dim, "Only 3D so far");
|
||||
|
||||
const Array<int>& Q1D = pQ1D[patch];
|
||||
const Array<int>& D1D = pD1D[patch];
|
||||
|
||||
const std::vector<Array2D<double>>& B = pB[patch];
|
||||
const std::vector<Array2D<double>>& G = pG[patch];
|
||||
|
||||
const IntArrayVar2D& minD = pminD[patch];
|
||||
const IntArrayVar2D& maxD = pmaxD[patch];
|
||||
const IntArrayVar2D& minQ = pminQ[patch];
|
||||
const IntArrayVar2D& maxQ = pmaxQ[patch];
|
||||
|
||||
auto X = Reshape(x.Read(), D1D[0], D1D[1], D1D[2]);
|
||||
auto Y = Reshape(y.ReadWrite(), D1D[0], D1D[1], D1D[2]);
|
||||
|
||||
const auto qd = Reshape(pa_data.Read(), Q1D[0]*Q1D[1]*Q1D[2],
|
||||
(symmetric ? 6 : 9));
|
||||
|
||||
// NOTE: the following is adapted from AssemblePatchMatrix_fullQuadrature
|
||||
std::vector<Array3D<double>> grad(dim);
|
||||
// TODO: Can an optimal order of dimensions be determined, for each patch?
|
||||
Array3D<double> gradXY(3, std::max(Q1D[0], D1D[0]), std::max(Q1D[1], D1D[1]));
|
||||
Array2D<double> gradX(3, std::max(Q1D[0], D1D[0]));
|
||||
|
||||
for (int d=0; d<dim; ++d)
|
||||
{
|
||||
grad[d].SetSize(Q1D[0], Q1D[1], Q1D[2]);
|
||||
|
||||
for (int qz = 0; qz < Q1D[2]; ++qz)
|
||||
{
|
||||
for (int qy = 0; qy < Q1D[1]; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < Q1D[0]; ++qx)
|
||||
{
|
||||
grad[d](qx,qy,qz) = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int dz = 0; dz < D1D[2]; ++dz)
|
||||
{
|
||||
for (int qy = 0; qy < Q1D[1]; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < Q1D[0]; ++qx)
|
||||
{
|
||||
for (int d=0; d<dim; ++d)
|
||||
{
|
||||
gradXY(d,qx,qy) = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int dy = 0; dy < D1D[1]; ++dy)
|
||||
{
|
||||
for (int qx = 0; qx < Q1D[0]; ++qx)
|
||||
{
|
||||
gradX(0,qx) = 0.0;
|
||||
gradX(1,qx) = 0.0;
|
||||
}
|
||||
for (int dx = 0; dx < D1D[0]; ++dx)
|
||||
{
|
||||
const double s = X(dx,dy,dz);
|
||||
for (int qx = minD[0][dx]; qx <= maxD[0][dx]; ++qx)
|
||||
{
|
||||
gradX(0,qx) += s * B[0](qx,dx);
|
||||
gradX(1,qx) += s * G[0](qx,dx);
|
||||
}
|
||||
}
|
||||
for (int qy = minD[1][dy]; qy <= maxD[1][dy]; ++qy)
|
||||
{
|
||||
const double wy = B[1](qy,dy);
|
||||
const double wDy = G[1](qy,dy);
|
||||
// This full range of qx values is generally necessary.
|
||||
for (int qx = 0; qx < Q1D[0]; ++qx)
|
||||
{
|
||||
const double wx = gradX(0,qx);
|
||||
const double wDx = gradX(1,qx);
|
||||
gradXY(0,qx,qy) += wDx * wy;
|
||||
gradXY(1,qx,qy) += wx * wDy;
|
||||
gradXY(2,qx,qy) += wx * wy;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int qz = minD[2][dz]; qz <= maxD[2][dz]; ++qz)
|
||||
{
|
||||
const double wz = B[2](qz,dz);
|
||||
const double wDz = G[2](qz,dz);
|
||||
for (int qy = 0; qy < Q1D[1]; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < Q1D[0]; ++qx)
|
||||
{
|
||||
grad[0](qx,qy,qz) += gradXY(0,qx,qy) * wz;
|
||||
grad[1](qx,qy,qz) += gradXY(1,qx,qy) * wz;
|
||||
grad[2](qx,qy,qz) += gradXY(2,qx,qy) * wDz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int qz = 0; qz < Q1D[2]; ++qz)
|
||||
{
|
||||
for (int qy = 0; qy < Q1D[1]; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < Q1D[0]; ++qx)
|
||||
{
|
||||
const int q = qx + ((qy + (qz * Q1D[1])) * Q1D[0]);
|
||||
const double O00 = qd(q,0);
|
||||
const double O01 = qd(q,1);
|
||||
const double O02 = qd(q,2);
|
||||
const double O10 = symmetric ? O01 : qd(q,3);
|
||||
const double O11 = symmetric ? qd(q,3) : qd(q,4);
|
||||
const double O12 = symmetric ? qd(q,4) : qd(q,5);
|
||||
const double O20 = symmetric ? O02 : qd(q,6);
|
||||
const double O21 = symmetric ? O12 : qd(q,7);
|
||||
const double O22 = symmetric ? qd(q,5) : qd(q,8);
|
||||
|
||||
const double grad0 = grad[0](qx,qy,qz);
|
||||
const double grad1 = grad[1](qx,qy,qz);
|
||||
const double grad2 = grad[2](qx,qy,qz);
|
||||
|
||||
grad[0](qx,qy,qz) = (O00*grad0)+(O01*grad1)+(O02*grad2);
|
||||
grad[1](qx,qy,qz) = (O10*grad0)+(O11*grad1)+(O12*grad2);
|
||||
grad[2](qx,qy,qz) = (O20*grad0)+(O21*grad1)+(O22*grad2);
|
||||
} // qx
|
||||
} // qy
|
||||
} // qz
|
||||
|
||||
for (int qz = 0; qz < Q1D[2]; ++qz)
|
||||
{
|
||||
for (int dy = 0; dy < D1D[1]; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < D1D[0]; ++dx)
|
||||
{
|
||||
for (int d=0; d<3; ++d)
|
||||
{
|
||||
gradXY(d,dx,dy) = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int qy = 0; qy < Q1D[1]; ++qy)
|
||||
{
|
||||
for (int dx = 0; dx < D1D[0]; ++dx)
|
||||
{
|
||||
for (int d=0; d<3; ++d)
|
||||
{
|
||||
gradX(d,dx) = 0.0;
|
||||
}
|
||||
}
|
||||
for (int qx = 0; qx < Q1D[0]; ++qx)
|
||||
{
|
||||
const double gX = grad[0](qx,qy,qz);
|
||||
const double gY = grad[1](qx,qy,qz);
|
||||
const double gZ = grad[2](qx,qy,qz);
|
||||
for (int dx = minQ[0][qx]; dx <= maxQ[0][qx]; ++dx)
|
||||
{
|
||||
const double wx = B[0](qx,dx);
|
||||
const double wDx = G[0](qx,dx);
|
||||
gradX(0,dx) += gX * wDx;
|
||||
gradX(1,dx) += gY * wx;
|
||||
gradX(2,dx) += gZ * wx;
|
||||
}
|
||||
}
|
||||
for (int dy = minQ[1][qy]; dy <= maxQ[1][qy]; ++dy)
|
||||
{
|
||||
const double wy = B[1](qy,dy);
|
||||
const double wDy = G[1](qy,dy);
|
||||
for (int dx = 0; dx < D1D[0]; ++dx)
|
||||
{
|
||||
gradXY(0,dx,dy) += gradX(0,dx) * wy;
|
||||
gradXY(1,dx,dy) += gradX(1,dx) * wDy;
|
||||
gradXY(2,dx,dy) += gradX(2,dx) * wy;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int dz = minQ[2][qz]; dz <= maxQ[2][qz]; ++dz)
|
||||
{
|
||||
const double wz = B[2](qz,dz);
|
||||
const double wDz = G[2](qz,dz);
|
||||
for (int dy = 0; dy < D1D[1]; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < D1D[0]; ++dx)
|
||||
{
|
||||
Y(dx,dy,dz) +=
|
||||
((gradXY(0,dx,dy) * wz) +
|
||||
(gradXY(1,dx,dy) * wz) +
|
||||
(gradXY(2,dx,dy) * wDz));
|
||||
}
|
||||
}
|
||||
} // dz
|
||||
} // qz
|
||||
}
|
||||
|
||||
void DiffusionIntegrator::AddMultNURBSPA(const Vector &x, Vector &y) const
|
||||
{
|
||||
Vector xp, yp;
|
||||
|
||||
for (int p=0; p<numPatches; ++p)
|
||||
{
|
||||
Array<int> vdofs;
|
||||
fespace->GetPatchVDofs(p, vdofs);
|
||||
|
||||
x.GetSubVector(vdofs, xp);
|
||||
yp.SetSize(vdofs.Size());
|
||||
yp = 0.0;
|
||||
|
||||
AddMultPatchPA(p, xp, yp);
|
||||
|
||||
y.AddElementVector(vdofs, yp);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@
|
||||
// Formulas at http://nines.cs.kuleuven.be/research/ecf/ecf.html
|
||||
|
||||
#include "fem.hpp"
|
||||
#include "../mesh/nurbs.hpp"
|
||||
#include <cmath>
|
||||
|
||||
#ifdef MFEM_USE_MPFR
|
||||
@@ -173,6 +174,51 @@ void IntegrationRule::GrundmannMollerSimplexRule(int s, int n)
|
||||
}
|
||||
}
|
||||
|
||||
IntegrationRule*
|
||||
IntegrationRule::ApplyToKnotIntervals(KnotVector const& kv) const
|
||||
{
|
||||
const int np = this->GetNPoints();
|
||||
const int ne = kv.GetNE();
|
||||
|
||||
IntegrationRule *kvir = new IntegrationRule(ne * np);
|
||||
|
||||
double x0 = kv[0];
|
||||
double x1 = x0;
|
||||
|
||||
int id = 0;
|
||||
for (int e=0; e<ne; ++e)
|
||||
{
|
||||
x0 = x1;
|
||||
|
||||
if (e == ne-1)
|
||||
{
|
||||
x1 = kv[kv.Size() - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find the next unique knot
|
||||
while (id < kv.Size() - 1)
|
||||
{
|
||||
id++;
|
||||
if (kv[id] != x0)
|
||||
{
|
||||
x1 = kv[id];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const double s = x1 - x0;
|
||||
|
||||
for (int j=0; j<this->GetNPoints(); ++j)
|
||||
{
|
||||
const double x = x0 + (s * (*this)[j].x);
|
||||
(*kvir)[(e * np) + j].Set1w(x, (*this)[j].weight);
|
||||
}
|
||||
}
|
||||
|
||||
return kvir;
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPFR
|
||||
|
||||
@@ -1725,4 +1771,235 @@ IntegrationRule *IntegrationRules::CubeIntegrationRule(int Order)
|
||||
return CubeIntRules[Order];
|
||||
}
|
||||
|
||||
IntegrationRule& NURBSMeshRules::GetElementRule(const int elem,
|
||||
const int patch, const int *ijk,
|
||||
Array<const KnotVector*> const& kv,
|
||||
bool & deleteRule) const
|
||||
{
|
||||
deleteRule = false;
|
||||
|
||||
// First check whether a rule has been assigned to element index elem.
|
||||
auto search = elementToRule.find(elem);
|
||||
if (search != elementToRule.end())
|
||||
{
|
||||
return *elementRule[search->second];
|
||||
}
|
||||
|
||||
MFEM_VERIFY(patchRules1D.NumRows(),
|
||||
"Undefined rule in NURBSMeshRules::GetElementRule");
|
||||
|
||||
// Use a tensor product of rules on the patch.
|
||||
MFEM_VERIFY(kv.Size() == dim, "");
|
||||
|
||||
int np = 1;
|
||||
std::vector<std::vector<double>> el(dim);
|
||||
|
||||
std::vector<int> npd;
|
||||
npd.assign(3, 0);
|
||||
|
||||
for (int d=0; d<dim; ++d)
|
||||
{
|
||||
const int order = kv[d]->GetOrder();
|
||||
|
||||
const double kv0 = (*kv[d])[order + ijk[d]];
|
||||
const double kv1 = (*kv[d])[order + ijk[d] + 1];
|
||||
|
||||
const bool rightEnd = (order + ijk[d] + 1) == (kv[d]->Size() - 1);
|
||||
|
||||
for (int i=0; i<patchRules1D(patch,d)->Size(); ++i)
|
||||
{
|
||||
const IntegrationPoint& ip = (*patchRules1D(patch,d))[i];
|
||||
if (kv0 <= ip.x && (ip.x < kv1 || rightEnd))
|
||||
{
|
||||
const double x = (ip.x - kv0) / (kv1 - kv0);
|
||||
el[d].push_back(x);
|
||||
el[d].push_back(ip.weight);
|
||||
}
|
||||
}
|
||||
|
||||
npd[d] = el[d].size() / 2;
|
||||
np *= npd[d];
|
||||
}
|
||||
|
||||
IntegrationRule *irp = new IntegrationRule(np);
|
||||
deleteRule = true;
|
||||
|
||||
// Set (*irp)[i + j*npd[0] + k*npd[0]*npd[1]] =
|
||||
// (el[0][2*i], el[1][2*j], el[2][2*k])
|
||||
|
||||
MFEM_VERIFY(npd[0] > 0 && npd[1] > 0, "Assuming 2D or 3D");
|
||||
|
||||
for (int i = 0; i < npd[0]; ++i)
|
||||
{
|
||||
for (int j = 0; j < npd[1]; ++j)
|
||||
{
|
||||
for (int k = 0; k < std::max(npd[2], 1); ++k)
|
||||
{
|
||||
const int id = i + j*npd[0] + k*npd[0]*npd[1];
|
||||
(*irp)[id].x = el[0][2*i];
|
||||
(*irp)[id].y = el[1][2*j];
|
||||
|
||||
(*irp)[id].weight = el[0][(2*i)+1];
|
||||
(*irp)[id].weight *= el[1][(2*j)+1];
|
||||
|
||||
if (npd[2] > 0)
|
||||
{
|
||||
(*irp)[id].z = el[2][2*k];
|
||||
(*irp)[id].weight *= el[2][(2*k)+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *irp;
|
||||
}
|
||||
|
||||
void NURBSMeshRules::GetIntegrationPointFrom1D(const int patch, int i, int j,
|
||||
int k, IntegrationPoint & ip)
|
||||
{
|
||||
MFEM_VERIFY(patchRules1D.NumRows() > 0,
|
||||
"Assuming patchRules1D is set.");
|
||||
|
||||
ip.weight = (*patchRules1D(patch,0))[i].weight;
|
||||
ip.x = (*patchRules1D(patch,0))[i].x;
|
||||
|
||||
if (dim > 1)
|
||||
{
|
||||
ip.weight *= (*patchRules1D(patch,1))[j].weight;
|
||||
ip.y = (*patchRules1D(patch,1))[j].x; // 1D rule only has x
|
||||
}
|
||||
|
||||
if (dim > 2)
|
||||
{
|
||||
ip.weight *= (*patchRules1D(patch,2))[k].weight;
|
||||
ip.z = (*patchRules1D(patch,2))[k].x; // 1D rule only has x
|
||||
}
|
||||
}
|
||||
|
||||
void NURBSMeshRules::Finalize(Mesh const& mesh)
|
||||
{
|
||||
if ((int) pointToElem.size() == npatches) { return; } // Already set
|
||||
|
||||
MFEM_VERIFY(elementToRule.empty() && patchRules1D.NumRows() > 0
|
||||
&& npatches > 0, "Assuming patchRules1D is set.");
|
||||
MFEM_VERIFY(mesh.NURBSext, "");
|
||||
MFEM_VERIFY(mesh.Dimension() == dim, "");
|
||||
|
||||
pointToElem.resize(npatches);
|
||||
patchRules1D_KnotSpan.resize(npatches);
|
||||
|
||||
// First, find all the elements in each patch.
|
||||
std::vector<std::vector<int>> patchElements(npatches);
|
||||
|
||||
for (int e=0; e<mesh.GetNE(); ++e)
|
||||
{
|
||||
patchElements[mesh.NURBSext->GetElementPatch(e)].push_back(e);
|
||||
}
|
||||
|
||||
Array<int> ijk(3);
|
||||
Array<int> maxijk(3);
|
||||
Array<int> np(3); // Number of points in each dimension
|
||||
ijk = 0;
|
||||
|
||||
Array<const KnotVector*> pkv;
|
||||
|
||||
for (int p=0; p<npatches; ++p)
|
||||
{
|
||||
patchRules1D_KnotSpan[p].resize(dim);
|
||||
|
||||
// For each patch, get the range of ijk.
|
||||
mesh.NURBSext->GetPatchKnotVectors(p, pkv);
|
||||
MFEM_VERIFY((int) pkv.Size() == dim, "");
|
||||
|
||||
maxijk = 1;
|
||||
np = 1;
|
||||
for (int d=0; d<dim; ++d)
|
||||
{
|
||||
maxijk[d] = pkv[d]->GetNKS();
|
||||
np[d] = patchRules1D(p,d)->Size();
|
||||
}
|
||||
|
||||
// For each patch, set a map from ijk to element index.
|
||||
Array3D<int> ijk2elem(maxijk[0], maxijk[1], maxijk[2]);
|
||||
ijk2elem = -1;
|
||||
|
||||
for (auto elem : patchElements[p])
|
||||
{
|
||||
mesh.NURBSext->GetElementIJK(elem, ijk);
|
||||
MFEM_VERIFY(ijk2elem(ijk[0], ijk[1], ijk[2]) == -1, "");
|
||||
ijk2elem(ijk[0], ijk[1], ijk[2]) = elem;
|
||||
}
|
||||
|
||||
// For each point, find its ijk and from that its element index.
|
||||
// It is assumed here that the NURBSFiniteElement kv the same as the
|
||||
// patch kv.
|
||||
|
||||
for (int d=0; d<dim; ++d)
|
||||
{
|
||||
patchRules1D_KnotSpan[p][d].SetSize(patchRules1D(p,d)->Size());
|
||||
|
||||
for (int r=0; r<patchRules1D(p,d)->Size(); ++r)
|
||||
{
|
||||
const IntegrationPoint& ip = (*patchRules1D(p,d))[r];
|
||||
|
||||
const int order = pkv[d]->GetOrder();
|
||||
|
||||
// Find ijk_d such that ip.x is in the corresponding knot-span.
|
||||
int ijk_d = 0;
|
||||
bool found = false;
|
||||
while (!found)
|
||||
{
|
||||
const double kv0 = (*pkv[d])[order + ijk_d];
|
||||
const double kv1 = (*pkv[d])[order + ijk_d + 1];
|
||||
|
||||
const bool rightEnd = (order + ijk_d + 1) == (pkv[d]->Size() - 1);
|
||||
|
||||
if (kv0 <= ip.x && (ip.x < kv1 || rightEnd))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ijk_d++;
|
||||
}
|
||||
}
|
||||
|
||||
patchRules1D_KnotSpan[p][d][r] = ijk_d;
|
||||
}
|
||||
}
|
||||
|
||||
pointToElem[p].SetSize(np[0], np[1], np[2]);
|
||||
for (int i=0; i<np[0]; ++i)
|
||||
for (int j=0; j<np[1]; ++j)
|
||||
for (int k=0; k<np[2]; ++k)
|
||||
{
|
||||
const int elem = ijk2elem(patchRules1D_KnotSpan[p][0][i],
|
||||
patchRules1D_KnotSpan[p][1][j],
|
||||
patchRules1D_KnotSpan[p][2][k]);
|
||||
MFEM_VERIFY(elem >= 0, "");
|
||||
pointToElem[p](i,j,k) = elem;
|
||||
}
|
||||
} // Loop (p) over patches
|
||||
}
|
||||
|
||||
void NURBSMeshRules::SetPatchRules1D(const int patch,
|
||||
std::vector<const IntegrationRule*> & ir1D)
|
||||
{
|
||||
MFEM_VERIFY((int) ir1D.size() == dim, "Wrong dimension");
|
||||
|
||||
for (int i=0; i<dim; ++i)
|
||||
{
|
||||
patchRules1D(patch,i) = ir1D[i];
|
||||
}
|
||||
}
|
||||
|
||||
NURBSMeshRules::~NURBSMeshRules()
|
||||
{
|
||||
for (int i=0; i<patchRules1D.NumRows(); ++i)
|
||||
for (int j=0; j<patchRules1D.NumCols(); ++j)
|
||||
{
|
||||
delete patchRules1D(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,9 +15,15 @@
|
||||
#include "../config/config.hpp"
|
||||
#include "../general/array.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class KnotVector;
|
||||
class Mesh;
|
||||
|
||||
/* Classes for IntegrationPoint, IntegrationRule, and container class
|
||||
IntegrationRules. Declares the global variable IntRules */
|
||||
|
||||
@@ -257,10 +263,105 @@ public:
|
||||
a call like this: `IntPoint(i).weight`. */
|
||||
const Array<double> &GetWeights() const;
|
||||
|
||||
/// @brief Return an integration rule for KnotVector @a kv, defined by
|
||||
/// applying this rule on each knot interval.
|
||||
IntegrationRule* ApplyToKnotIntervals(KnotVector const& kv) const;
|
||||
|
||||
/// Destroys an IntegrationRule object
|
||||
~IntegrationRule() { }
|
||||
};
|
||||
|
||||
/// Class for defining different integration rules on each NURBS patch.
|
||||
class NURBSMeshRules
|
||||
{
|
||||
public:
|
||||
/// Construct a rule for each patch, using SetPatchRules1D.
|
||||
NURBSMeshRules(const int numPatches, const int dim_) :
|
||||
patchRules1D(numPatches, dim_),
|
||||
npatches(numPatches), dim(dim_) { }
|
||||
|
||||
/// Returns a rule for the element.
|
||||
IntegrationRule &GetElementRule(const int elem, const int patch,
|
||||
const int *ijk,
|
||||
Array<const KnotVector*> const& kv,
|
||||
bool & deleteRule) const;
|
||||
|
||||
/// Add a rule to be used for individual elements. Returns the rule index.
|
||||
std::size_t AddElementRule(IntegrationRule *ir_element)
|
||||
{
|
||||
elementRule.push_back(ir_element);
|
||||
return elementRule.size() - 1;
|
||||
}
|
||||
|
||||
/// @brief Set the integration rule for the element of the given index. This
|
||||
/// rule is used instead of the rule for the patch containing the element.
|
||||
void SetElementRule(const std::size_t element,
|
||||
const std::size_t elementRuleIndex)
|
||||
{
|
||||
elementToRule[element] = elementRuleIndex;
|
||||
}
|
||||
|
||||
/// @brief Set 1D integration rules to be used as a tensor product rule on
|
||||
/// the patch with index @a patch. This class takes ownership of these rules.
|
||||
void SetPatchRules1D(const int patch,
|
||||
std::vector<const IntegrationRule*> & ir1D);
|
||||
|
||||
/// @brief For tensor product rules defined on each patch by
|
||||
/// SetPatchRules1D(), return a pointer to the 1D rule in the specified
|
||||
/// @a dimension.
|
||||
const IntegrationRule* GetPatchRule1D(const int patch,
|
||||
const int dimension) const
|
||||
{
|
||||
return patchRules1D(patch, dimension);
|
||||
}
|
||||
|
||||
/// @brief For tensor product rules defined on each patch by
|
||||
/// SetPatchRules1D(), return the integration point with index (i,j,k).
|
||||
void GetIntegrationPointFrom1D(const int patch, int i, int j, int k,
|
||||
IntegrationPoint & ip);
|
||||
|
||||
/// @brief Finalize() must be called before this class can be used for
|
||||
/// assembly. In particular, it defines data used by GetPointElement().
|
||||
void Finalize(Mesh const& mesh);
|
||||
|
||||
/// @brief For tensor product rules defined on each patch by
|
||||
/// SetPatchRules1D(), returns the index of the element containing
|
||||
/// integration point (i,j,k) for patch index @a patch. Finalize() must be
|
||||
/// called first.
|
||||
int GetPointElement(int patch, int i, int j, int k) const
|
||||
{
|
||||
return pointToElem[patch](i,j,k);
|
||||
}
|
||||
|
||||
int GetDim() const { return dim; }
|
||||
|
||||
/// @brief For tensor product rules defined on each patch by
|
||||
/// SetPatchRules1D(), returns an array of knot span indices for each
|
||||
/// integration point in the specified @a dimension.
|
||||
const Array<int>& GetPatchRule1D_KnotSpan(const int patch,
|
||||
const int dimension) const
|
||||
{
|
||||
return patchRules1D_KnotSpan[patch][dimension];
|
||||
}
|
||||
|
||||
~NURBSMeshRules();
|
||||
|
||||
private:
|
||||
/// Tensor-product rules defined on all patches independently.
|
||||
Array2D<const IntegrationRule*> patchRules1D;
|
||||
|
||||
/// Integration rules defined on elements.
|
||||
std::vector<IntegrationRule*> elementRule;
|
||||
|
||||
std::map<std::size_t, std::size_t> elementToRule;
|
||||
|
||||
std::vector<Array3D<int>> pointToElem;
|
||||
std::vector<std::vector<Array<int>>> patchRules1D_KnotSpan;
|
||||
|
||||
const int npatches;
|
||||
const int dim;
|
||||
};
|
||||
|
||||
/// A Class that defines 1-D numerical quadrature rules on [0,1].
|
||||
class QuadratureFunctions1D
|
||||
{
|
||||
|
||||
@@ -26,9 +26,23 @@ namespace mfem
|
||||
assemble the local gradient operator and to compute the local energy. */
|
||||
class NonlinearFormIntegrator
|
||||
{
|
||||
public:
|
||||
enum Mode
|
||||
{
|
||||
ELEMENTWISE = 0, /**< Element-wise integration (default) */
|
||||
PATCHWISE = 1, /**< Patch-wise integration (NURBS meshes) */
|
||||
PATCHWISE_REDUCED = 2, /**< Patch-wise integration (NURBS meshes) with
|
||||
reduced integration rules. */
|
||||
};
|
||||
|
||||
protected:
|
||||
const IntegrationRule *IntRule;
|
||||
|
||||
Mode integrationMode = Mode::ELEMENTWISE;
|
||||
|
||||
// Prescribed integration rules (not reduced approximate rules).
|
||||
NURBSMeshRules *patchRules = nullptr;
|
||||
|
||||
// CEED extension
|
||||
ceed::Operator* ceedOp;
|
||||
|
||||
@@ -42,6 +56,14 @@ public:
|
||||
let the integrator choose (when @a ir == NULL). */
|
||||
virtual void SetIntRule(const IntegrationRule *ir) { IntRule = ir; }
|
||||
|
||||
void SetIntegrationMode(Mode m) { integrationMode = m; }
|
||||
|
||||
/// For patchwise integration, SetNURBSPatchIntRule must be called.
|
||||
void SetNURBSPatchIntRule(NURBSMeshRules *pr) { patchRules = pr; }
|
||||
bool HasNURBSPatchIntRule() const { return patchRules != nullptr; }
|
||||
|
||||
bool Patchwise() const { return integrationMode != Mode::ELEMENTWISE; }
|
||||
|
||||
/// Prescribe a fixed IntegrationRule to use.
|
||||
void SetIntegrationRule(const IntegrationRule &ir) { SetIntRule(&ir); }
|
||||
|
||||
|
||||
+121
-80
@@ -163,18 +163,18 @@ void ParFiniteElementSpace::Construct()
|
||||
|
||||
// calculate number of ghost DOFs
|
||||
ngvdofs = pncmesh->GetNGhostVertices()
|
||||
* fec->DofForGeometry(Geometry::POINT);
|
||||
* fec->DofForGeometry(Geometry::Type::POINT);
|
||||
|
||||
if (pmesh->Dimension() > 1)
|
||||
{
|
||||
ngedofs = pncmesh->GetNGhostEdges()
|
||||
* fec->DofForGeometry(Geometry::SEGMENT);
|
||||
* fec->DofForGeometry(Geometry::Type::SEGMENT);
|
||||
}
|
||||
|
||||
if (pmesh->Dimension() > 2)
|
||||
{
|
||||
int stride = fec->DofForGeometry(Geometry::SQUARE);
|
||||
ngfdofs = pncmesh->GetNGhostFaces() * stride;
|
||||
ngfdofs = pncmesh->GetNGhostFaces()
|
||||
* fec->DofForGeometry(Geometry::Type::SQUARE);
|
||||
}
|
||||
|
||||
// total number of ghost DOFs. Ghost DOFs start at index 'ndofs', i.e.,
|
||||
@@ -1842,9 +1842,13 @@ int ParFiniteElementSpace::PackDof(int entity, int index, int edof) const
|
||||
static int bisect(const int* array, int size, int value)
|
||||
{
|
||||
const int* end = array + size;
|
||||
const int* pos = std::lower_bound(array, end, value);
|
||||
MFEM_VERIFY(pos != end, "value not found");
|
||||
return pos - array;
|
||||
const int* pos = std::upper_bound(array, end, value);
|
||||
MFEM_VERIFY(pos != array, "value not found");
|
||||
if (pos == end)
|
||||
{
|
||||
MFEM_VERIFY(*(array+size - 1) == value, "Last entry must be exact")
|
||||
}
|
||||
return pos - array - 1;
|
||||
}
|
||||
|
||||
/** Dissect a DOF number to obtain the entity type (0=vertex, 1=edge, 2=face),
|
||||
@@ -1880,7 +1884,8 @@ void ParFiniteElementSpace::UnpackDof(int dof,
|
||||
else // mixed faces or var-order space
|
||||
{
|
||||
const Table &table = var_face_dofs;
|
||||
MFEM_ASSERT(table.Size(), "");
|
||||
|
||||
MFEM_ASSERT(table.Size() > 0, "");
|
||||
int jpos = bisect(table.GetJ(), table.Size_of_connections(), dof);
|
||||
index = bisect(table.GetI(), table.Size(), jpos);
|
||||
edof = dof - table.GetRow(index)[0];
|
||||
@@ -2010,7 +2015,6 @@ class NeighborRowMessage : public VarMessage<314>
|
||||
public:
|
||||
typedef NCMesh::MeshId MeshId;
|
||||
typedef ParNCMesh::GroupId GroupId;
|
||||
|
||||
struct RowInfo
|
||||
{
|
||||
int entity, index, edof;
|
||||
@@ -2022,8 +2026,6 @@ public:
|
||||
|
||||
RowInfo(int ent, int idx, int edof, GroupId grp)
|
||||
: entity(ent), index(idx), edof(edof), group(grp) {}
|
||||
|
||||
typedef std::vector<RowInfo> List;
|
||||
};
|
||||
|
||||
NeighborRowMessage() : pncmesh(NULL) {}
|
||||
@@ -2034,7 +2036,7 @@ public:
|
||||
rows.push_back(RowInfo(entity, index, edof, group, row));
|
||||
}
|
||||
|
||||
const RowInfo::List& GetRows() const { return rows; }
|
||||
const std::vector<RowInfo>& GetRows() const { return rows; }
|
||||
|
||||
void SetNCMesh(ParNCMesh* pnc) { pncmesh = pnc; }
|
||||
void SetFEC(const FiniteElementCollection* fec_) { this->fec = fec_; }
|
||||
@@ -2042,7 +2044,7 @@ public:
|
||||
typedef std::map<int, NeighborRowMessage> Map;
|
||||
|
||||
protected:
|
||||
RowInfo::List rows;
|
||||
std::vector<RowInfo> rows;
|
||||
|
||||
ParNCMesh *pncmesh;
|
||||
const FiniteElementCollection* fec;
|
||||
@@ -2051,7 +2053,6 @@ protected:
|
||||
virtual void Decode(int);
|
||||
};
|
||||
|
||||
|
||||
void NeighborRowMessage::Encode(int rank)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
@@ -2161,11 +2162,21 @@ void NeighborRowMessage::Decode(int rank)
|
||||
ind = fec->DofOrderForOrientation(geom, fo);
|
||||
}
|
||||
|
||||
double s = 1.0;
|
||||
#ifdef MFEM_DEBUG_PMATRIX
|
||||
mfem::out << "Rank " << pncmesh->MyRank << " receiving from " << rank
|
||||
<< ": ent " << ent << ", index " << id.index
|
||||
<< ", edof " << edof << " (id " << id.element << "/"
|
||||
<< int(id.local) << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
// If edof arrived with a negative index, flip it, and the scaling.
|
||||
double s = (edof < 0) ? -1.0 : 1.0;
|
||||
edof = (edof < 0) ? -1 - edof : edof;
|
||||
|
||||
if (ind && (edof = ind[edof]) < 0)
|
||||
{
|
||||
edof = -1 - edof;
|
||||
s = -1.0;
|
||||
s *= -1.0;
|
||||
}
|
||||
|
||||
rows.push_back(RowInfo(ent, id.index, edof, group_ids[gi++]));
|
||||
@@ -2189,10 +2200,8 @@ ParFiniteElementSpace::ScheduleSendRow(const PMatrixRow &row, int dof,
|
||||
int ent, idx, edof;
|
||||
UnpackDof(dof, ent, idx, edof);
|
||||
|
||||
const ParNCMesh::CommGroup &group = pncmesh->GetGroup(group_id);
|
||||
for (unsigned i = 0; i < group.size(); i++)
|
||||
for (const auto &rank : pncmesh->GetGroup(group_id))
|
||||
{
|
||||
int rank = group[i];
|
||||
if (rank != MyRank)
|
||||
{
|
||||
NeighborRowMessage &msg = send_msg[rank];
|
||||
@@ -2312,7 +2321,7 @@ int ParFiniteElementSpace
|
||||
&& fec->GetContType() == FiniteElementCollection::TANGENTIAL),
|
||||
"Nedelec NC tets of order >= 2 are not supported yet.");
|
||||
|
||||
bool dg = (nvdofs == 0 && nedofs == 0 && nfdofs == 0);
|
||||
const bool dg = (nvdofs == 0 && nedofs == 0 && nfdofs == 0);
|
||||
|
||||
#ifdef MFEM_PMATRIX_STATS
|
||||
n_msgs_sent = n_msgs_recv = 0;
|
||||
@@ -2321,7 +2330,7 @@ int ParFiniteElementSpace
|
||||
|
||||
// *** STEP 1: build master-slave dependency lists ***
|
||||
|
||||
int total_dofs = ndofs + ngdofs;
|
||||
const int total_dofs = ndofs + ngdofs;
|
||||
SparseMatrix deps(ndofs, total_dofs);
|
||||
|
||||
if (!dg && !partial)
|
||||
@@ -2332,16 +2341,14 @@ int ParFiniteElementSpace
|
||||
for (int entity = 0; entity <= 2; entity++)
|
||||
{
|
||||
const NCMesh::NCList &list = pncmesh->GetNCList(entity);
|
||||
if (!list.masters.Size()) { continue; }
|
||||
if (list.masters.Size() == 0) { continue; }
|
||||
|
||||
IsoparametricTransformation T;
|
||||
DenseMatrix I;
|
||||
|
||||
// process masters that we own or that affect our edges/faces
|
||||
for (int mi = 0; mi < list.masters.Size(); mi++)
|
||||
for (const auto &mf : list.masters)
|
||||
{
|
||||
const NCMesh::Master &mf = list.masters[mi];
|
||||
|
||||
// get master DOFs
|
||||
if (pncmesh->IsGhost(entity, mf.index))
|
||||
{
|
||||
@@ -2352,10 +2359,10 @@ int ParFiniteElementSpace
|
||||
GetEntityDofs(entity, mf.index, master_dofs, mf.Geom());
|
||||
}
|
||||
|
||||
if (!master_dofs.Size()) { continue; }
|
||||
if (master_dofs.Size() == 0) { continue; }
|
||||
|
||||
const FiniteElement* fe = fec->FiniteElementForGeometry(mf.Geom());
|
||||
if (!fe) { continue; }
|
||||
if (fe == nullptr) { continue; }
|
||||
|
||||
switch (mf.Geom())
|
||||
{
|
||||
@@ -2371,7 +2378,7 @@ int ParFiniteElementSpace
|
||||
const NCMesh::Slave &sf = list.slaves[si];
|
||||
if (pncmesh->IsGhost(entity, sf.index)) { continue; }
|
||||
|
||||
const int variant = 0; // TODO parallel var-order
|
||||
constexpr int variant = 0; // TODO parallel var-order
|
||||
GetEntityDofs(entity, sf.index, slave_dofs, mf.Geom(), variant);
|
||||
if (!slave_dofs.Size()) { continue; }
|
||||
|
||||
@@ -2398,37 +2405,37 @@ int ParFiniteElementSpace
|
||||
{
|
||||
Array<int> dofs;
|
||||
|
||||
// initialize dof_group[], dof_owner[]
|
||||
for (int entity = 0; entity <= 2; entity++)
|
||||
auto initialize_group_and_owner = [&dof_group, &dof_owner, &dofs,
|
||||
this](int entity, const MeshId &id)
|
||||
{
|
||||
const NCMesh::NCList &list = pncmesh->GetNCList(entity);
|
||||
if (id.index < 0) { return; }
|
||||
|
||||
int lsize[3] =
|
||||
{ list.conforming.Size(), list.masters.Size(), list.slaves.Size() };
|
||||
GroupId owner = pncmesh->GetEntityOwnerId(entity, id.index);
|
||||
GroupId group = pncmesh->GetEntityGroupId(entity, id.index);
|
||||
|
||||
for (int l = 0; l < 3; l++)
|
||||
GetBareDofs(entity, id.index, dofs);
|
||||
|
||||
for (auto dof : dofs)
|
||||
{
|
||||
for (int i = 0; i < lsize[l]; i++)
|
||||
{
|
||||
const MeshId &id =
|
||||
(l == 0) ? list.conforming[i] :
|
||||
(l == 1) ? (const MeshId&) list.masters[i]
|
||||
/* */ : (const MeshId&) list.slaves[i];
|
||||
dof_owner[dof] = owner;
|
||||
dof_group[dof] = group;
|
||||
}
|
||||
};
|
||||
|
||||
if (id.index < 0) { continue; }
|
||||
|
||||
GroupId owner = pncmesh->GetEntityOwnerId(entity, id.index);
|
||||
GroupId group = pncmesh->GetEntityGroupId(entity, id.index);
|
||||
|
||||
GetBareDofs(entity, id.index, dofs);
|
||||
|
||||
for (int j = 0; j < dofs.Size(); j++)
|
||||
{
|
||||
int dof = dofs[j];
|
||||
dof_owner[dof] = owner;
|
||||
dof_group[dof] = group;
|
||||
}
|
||||
}
|
||||
// initialize dof_group[], dof_owner[] in sequence
|
||||
for (int entity : {0,1,2})
|
||||
{
|
||||
for (const auto &id : pncmesh->GetNCList(entity).conforming)
|
||||
{
|
||||
initialize_group_and_owner(entity, id);
|
||||
}
|
||||
for (const auto &id : pncmesh->GetNCList(entity).masters)
|
||||
{
|
||||
initialize_group_and_owner(entity, id);
|
||||
}
|
||||
for (const auto &id : pncmesh->GetNCList(entity).slaves)
|
||||
{
|
||||
initialize_group_and_owner(entity, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2440,15 +2447,50 @@ int ParFiniteElementSpace
|
||||
|
||||
// DOFs that stayed independent and are ours are true DOFs
|
||||
int num_true_dofs = 0;
|
||||
for (int i = 0; i < ndofs; i++)
|
||||
for (int i = 0; i < ndofs; ++i)
|
||||
{
|
||||
if (dof_owner[i] == 0 && deps.RowSize(i) == 0)
|
||||
{
|
||||
num_true_dofs++;
|
||||
++num_true_dofs;
|
||||
finalized[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_DEBUG_PMATRIX
|
||||
// Helper for dumping diagnostics on one dof
|
||||
auto dof_diagnostics = [&](int dof, bool print_diagnostic)
|
||||
{
|
||||
const auto &comm_group = pncmesh->GetGroup(dof_group[dof]);
|
||||
std::stringstream msg;
|
||||
msg << std::boolalpha;
|
||||
msg << "R" << Mpi::WorldRank() << " dof " << dof
|
||||
<< " owner_rank " << pncmesh->GetGroup(dof_owner[dof])[0] << " CommGroup {";
|
||||
for (const auto &x : comm_group)
|
||||
{
|
||||
msg << x << ' ';
|
||||
}
|
||||
msg << "} finalized " << finalized[dof];
|
||||
|
||||
Array<int> cols;
|
||||
if (dof < ndofs)
|
||||
{
|
||||
Vector row;
|
||||
deps.GetRow(dof, cols, row);
|
||||
msg << " deps cols {";
|
||||
for (const auto &x : cols)
|
||||
{
|
||||
msg << x << ' ';
|
||||
}
|
||||
msg << '}';
|
||||
}
|
||||
|
||||
int entity, index, edof;
|
||||
UnpackDof(dof, entity, index, edof);
|
||||
msg << " entity " << entity << " index " << index << " edof " << edof;
|
||||
return msg.str();
|
||||
};
|
||||
#endif
|
||||
|
||||
// calculate global offsets
|
||||
HYPRE_BigInt loc_sizes[2] = { ndofs*vdim, num_true_dofs*vdim };
|
||||
Array<HYPRE_BigInt>* offsets[2] = { &dof_offs, &tdof_offs };
|
||||
@@ -2470,10 +2512,10 @@ int ParFiniteElementSpace
|
||||
|
||||
std::vector<PMatrixRow> pmatrix(total_dofs);
|
||||
|
||||
bool bynodes = (ordering == Ordering::byNODES);
|
||||
int vdim_factor = bynodes ? 1 : vdim;
|
||||
int dof_stride = bynodes ? ndofs : 1;
|
||||
int tdof_stride = bynodes ? num_true_dofs : 1;
|
||||
const bool bynodes = (ordering == Ordering::byNODES);
|
||||
const int vdim_factor = bynodes ? 1 : vdim;
|
||||
const int dof_stride = bynodes ? ndofs : 1;
|
||||
const int tdof_stride = bynodes ? num_true_dofs : 1;
|
||||
|
||||
// big container for all messages we send (the list is for iterations)
|
||||
std::list<NeighborRowMessage::Map> send_msg;
|
||||
@@ -2495,13 +2537,13 @@ int ParFiniteElementSpace
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
int vdof = dof*vdim_factor + vd*dof_stride;
|
||||
int vtdof = tdof*vdim_factor + vd*tdof_stride;
|
||||
const int vdof = dof*vdim_factor + vd*dof_stride;
|
||||
const int vtdof = tdof*vdim_factor + vd*tdof_stride;
|
||||
|
||||
if (R_) { (*R_)->Add(vtdof, vdof, 1.0); }
|
||||
if (dof_tdof) { (*dof_tdof)[vdof] = vtdof; }
|
||||
}
|
||||
tdof++;
|
||||
++tdof;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2542,14 +2584,12 @@ int ParFiniteElementSpace
|
||||
n_rows_recv += recv_msg.GetRows().size();
|
||||
#endif
|
||||
|
||||
const NeighborRowMessage::RowInfo::List &rows = recv_msg.GetRows();
|
||||
for (unsigned i = 0; i < rows.size(); i++)
|
||||
for (const auto &ri : recv_msg.GetRows())
|
||||
{
|
||||
const NeighborRowMessage::RowInfo &ri = rows[i];
|
||||
int dof = PackDof(ri.entity, ri.index, ri.edof);
|
||||
const int dof = PackDof(ri.entity, ri.index, ri.edof);
|
||||
pmatrix[dof] = ri.row;
|
||||
|
||||
if (dof < ndofs && !finalized[dof]) { num_finalized++; }
|
||||
if (dof < ndofs && !finalized[dof]) { ++num_finalized; }
|
||||
finalized[dof] = true;
|
||||
|
||||
if (ri.group >= 0 && dof_group[dof] != ri.group)
|
||||
@@ -2567,13 +2607,14 @@ int ParFiniteElementSpace
|
||||
done = true;
|
||||
for (int dof = 0; dof < ndofs; dof++)
|
||||
{
|
||||
if (finalized[dof]) { continue; }
|
||||
|
||||
bool owned = (dof_owner[dof] == 0);
|
||||
bool shared = (dof_group[dof] != 0);
|
||||
|
||||
if (owned && DofFinalizable(dof, finalized, deps))
|
||||
const bool owned = (dof_owner[dof] == 0);
|
||||
if (!finalized[dof]
|
||||
&& owned
|
||||
&& DofFinalizable(dof, finalized, deps))
|
||||
{
|
||||
int ent, idx, edof;
|
||||
UnpackDof(dof, ent, idx, edof);
|
||||
|
||||
const int* dep_col = deps.GetRowColumns(dof);
|
||||
const double* dep_coef = deps.GetRowEntries(dof);
|
||||
int num_dep = deps.RowSize(dof);
|
||||
@@ -2588,10 +2629,11 @@ int ParFiniteElementSpace
|
||||
pmatrix[dof] = buffer;
|
||||
|
||||
finalized[dof] = true;
|
||||
num_finalized++;
|
||||
++num_finalized;
|
||||
done = false;
|
||||
|
||||
// send row to neighbors who need it
|
||||
const bool shared = (dof_group[dof] != 0);
|
||||
if (shared)
|
||||
{
|
||||
ScheduleSendRow(pmatrix[dof], dof, dof_group[dof],
|
||||
@@ -2602,7 +2644,7 @@ int ParFiniteElementSpace
|
||||
}
|
||||
|
||||
#ifdef MFEM_DEBUG_PMATRIX
|
||||
/*static int dump = 0;
|
||||
static int dump = 0;
|
||||
if (dump < 10)
|
||||
{
|
||||
char fname[100];
|
||||
@@ -2610,7 +2652,7 @@ int ParFiniteElementSpace
|
||||
std::ofstream f(fname);
|
||||
DebugDumpDOFs(f, deps, dof_group, dof_owner, finalized);
|
||||
dump++;
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
|
||||
// send current batch of messages
|
||||
@@ -2635,10 +2677,9 @@ int ParFiniteElementSpace
|
||||
}
|
||||
|
||||
// make sure we can discard all send buffers
|
||||
for (std::list<NeighborRowMessage::Map>::iterator
|
||||
it = send_msg.begin(); it != send_msg.end(); ++it)
|
||||
for (auto &msg : send_msg)
|
||||
{
|
||||
NeighborRowMessage::WaitAllSent(*it);
|
||||
NeighborRowMessage::WaitAllSent(msg);
|
||||
}
|
||||
|
||||
#ifdef MFEM_PMATRIX_STATS
|
||||
|
||||
+12
-11
@@ -115,7 +115,8 @@ std::ostream &operator<<(std::ostream &os, const QuadratureFunction &qf)
|
||||
}
|
||||
|
||||
void QuadratureFunction::SaveVTU(std::ostream &os, VTKFormat format,
|
||||
int compression_level) const
|
||||
int compression_level,
|
||||
const std::string &field_name) const
|
||||
{
|
||||
os << R"(<VTKFile type="UnstructuredGrid" version="0.1")";
|
||||
if (compression_level != 0)
|
||||
@@ -129,11 +130,9 @@ void QuadratureFunction::SaveVTU(std::ostream &os, VTKFormat format,
|
||||
const char *type_str = (format != VTKFormat::BINARY32) ? "Float64" : "Float32";
|
||||
std::vector<char> buf;
|
||||
|
||||
Mesh &mesh = *qspace->GetMesh();
|
||||
|
||||
int np = qspace->GetSize();
|
||||
int ne = mesh.GetNE();
|
||||
int sdim = mesh.SpaceDimension();
|
||||
const int np = qspace->GetSize();
|
||||
const int ne = qspace->GetNE();
|
||||
const int sdim = qspace->GetMesh()->SpaceDimension();
|
||||
|
||||
// For quadrature functions, each point is a vertex cell, so number of cells
|
||||
// is equal to number of points
|
||||
@@ -148,7 +147,7 @@ void QuadratureFunction::SaveVTU(std::ostream &os, VTKFormat format,
|
||||
Vector pt(sdim);
|
||||
for (int i = 0; i < ne; i++)
|
||||
{
|
||||
ElementTransformation &T = *mesh.GetElementTransformation(i);
|
||||
ElementTransformation &T = *qspace->GetTransformation(i);
|
||||
const IntegrationRule &ir = GetIntRule(i);
|
||||
for (int j = 0; j < ir.Size(); j++)
|
||||
{
|
||||
@@ -205,8 +204,9 @@ void QuadratureFunction::SaveVTU(std::ostream &os, VTKFormat format,
|
||||
os << "</Cells>\n";
|
||||
|
||||
os << "<PointData>\n";
|
||||
os << "<DataArray type=\"" << type_str << "\" Name=\"u\" format=\""
|
||||
<< fmt_str << "\" NumberOfComponents=\"" << vdim << "\">\n";
|
||||
os << "<DataArray type=\"" << type_str << "\" Name=\"" << field_name
|
||||
<< "\" format=\"" << fmt_str << "\" NumberOfComponents=\"" << vdim
|
||||
<< "\">\n";
|
||||
for (int i = 0; i < ne; i++)
|
||||
{
|
||||
DenseMatrix vals;
|
||||
@@ -233,10 +233,11 @@ void QuadratureFunction::SaveVTU(std::ostream &os, VTKFormat format,
|
||||
}
|
||||
|
||||
void QuadratureFunction::SaveVTU(const std::string &filename, VTKFormat format,
|
||||
int compression_level) const
|
||||
int compression_level,
|
||||
const std::string &field_name) const
|
||||
{
|
||||
std::ofstream f(filename + ".vtu");
|
||||
SaveVTU(f, format, compression_level);
|
||||
SaveVTU(f, format, compression_level, field_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -185,7 +185,7 @@ public:
|
||||
/// format is VTKFormat::ASCII. Otherwise, zlib compression will be used for
|
||||
/// binary data.
|
||||
void SaveVTU(std::ostream &out, VTKFormat format=VTKFormat::ASCII,
|
||||
int compression_level=0) const;
|
||||
int compression_level=0, const std::string &field_name="u") const;
|
||||
|
||||
/// @brief Save the QuadratureFunction to a VTU (ParaView) file.
|
||||
///
|
||||
@@ -193,7 +193,7 @@ public:
|
||||
/// @sa SaveVTU(std::ostream &out, VTKFormat format=VTKFormat::ASCII,
|
||||
/// int compression_level=0)
|
||||
void SaveVTU(const std::string &filename, VTKFormat format=VTKFormat::ASCII,
|
||||
int compression_level=0) const;
|
||||
int compression_level=0, const std::string &field_name="u") const;
|
||||
|
||||
virtual ~QuadratureFunction()
|
||||
{
|
||||
|
||||
+237
-107
@@ -2838,6 +2838,7 @@ TMOP_Integrator::~TMOP_Integrator()
|
||||
delete lim_func;
|
||||
delete adapt_lim_gf;
|
||||
delete surf_fit_gf;
|
||||
delete surf_fit_limiter;
|
||||
delete surf_fit_grad;
|
||||
delete surf_fit_hess;
|
||||
for (int i = 0; i < ElemDer.Size(); i++)
|
||||
@@ -2912,6 +2913,10 @@ void TMOP_Integrator::EnableSurfaceFitting(const GridFunction &s0,
|
||||
Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae)
|
||||
{
|
||||
// To have both we must duplicate the markers.
|
||||
MFEM_VERIFY(surf_fit_pos == NULL,
|
||||
"Using both fitting approaches is not supported.");
|
||||
|
||||
delete surf_fit_gf;
|
||||
surf_fit_gf = new GridFunction(s0);
|
||||
surf_fit_gf->CountElementsPerVDof(surf_fit_dof_count);
|
||||
@@ -2925,12 +2930,37 @@ void TMOP_Integrator::EnableSurfaceFitting(const GridFunction &s0,
|
||||
(*surf_fit_gf->FESpace()->GetMesh()->GetNodes(), *surf_fit_gf);
|
||||
}
|
||||
|
||||
void TMOP_Integrator::EnableSurfaceFitting(const GridFunction &pos,
|
||||
const Array<bool> &smarker,
|
||||
Coefficient &coeff)
|
||||
{
|
||||
// To have both we must duplicate the markers.
|
||||
MFEM_VERIFY(surf_fit_gf == NULL,
|
||||
"Using both fitting approaches is not supported.");
|
||||
MFEM_VERIFY(pos.FESpace()->GetMesh()->GetNodes(),
|
||||
"Positions on a mesh without Nodes is not supported.");
|
||||
MFEM_VERIFY(pos.FESpace()->GetOrdering() ==
|
||||
pos.FESpace()->GetMesh()->GetNodes()->FESpace()->GetOrdering(),
|
||||
"Incompatible ordering of spaces!");
|
||||
|
||||
surf_fit_pos = &pos;
|
||||
pos.CountElementsPerVDof(surf_fit_dof_count);
|
||||
surf_fit_marker = &smarker;
|
||||
surf_fit_coeff = &coeff;
|
||||
delete surf_fit_limiter;
|
||||
surf_fit_limiter = new TMOP_QuadraticLimiter;
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0,
|
||||
const Array<bool> &smarker,
|
||||
Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae)
|
||||
{
|
||||
// To have both we must duplicate the markers.
|
||||
MFEM_VERIFY(surf_fit_pos == NULL,
|
||||
"Using both fitting approaches is not supported.");
|
||||
|
||||
delete surf_fit_gf;
|
||||
surf_fit_gf = new GridFunction(s0);
|
||||
s0.CountElementsPerVDof(surf_fit_dof_count);
|
||||
@@ -3014,31 +3044,53 @@ void TMOP_Integrator::EnableSurfaceFittingFromSource(
|
||||
}
|
||||
#endif
|
||||
|
||||
void TMOP_Integrator::GetSurfaceFittingErrors(double &err_avg, double &err_max)
|
||||
void TMOP_Integrator::GetSurfaceFittingErrors(const Vector &pos,
|
||||
double &err_avg, double &err_max)
|
||||
{
|
||||
MFEM_VERIFY(surf_fit_gf, "Surface fitting has not been enabled.");
|
||||
MFEM_VERIFY(surf_fit_marker, "Surface fitting has not been enabled.");
|
||||
|
||||
const FiniteElementSpace *fes =
|
||||
(surf_fit_gf) ? surf_fit_gf->FESpace() : surf_fit_pos->FESpace();
|
||||
#ifdef MFEM_USE_MPI
|
||||
auto pfes =
|
||||
dynamic_cast<const ParFiniteElementSpace *>(surf_fit_gf->FESpace());
|
||||
auto pfes = dynamic_cast<const ParFiniteElementSpace *>(fes);
|
||||
bool parallel = (pfes) ? true : false;
|
||||
#endif
|
||||
|
||||
int dim = fes->GetMesh()->Dimension();
|
||||
const int node_cnt = surf_fit_marker->Size();
|
||||
err_max = 0.0;
|
||||
int dof_cnt = 0;
|
||||
double err_sum = 0.0;
|
||||
for (int i = 0; i < surf_fit_marker->Size(); i++)
|
||||
for (int i = 0; i < node_cnt; i++)
|
||||
{
|
||||
if ((*surf_fit_marker)[i] == true)
|
||||
{
|
||||
if ((*surf_fit_marker)[i] == false) { continue; }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
// Don't count the overlapping DOFs in parallel.
|
||||
if (parallel && pfes->GetLocalTDofNumber(i) < 0) { continue; }
|
||||
// Don't count the overlapping DOFs in parallel.
|
||||
// The pfes might be ordered byVDIM, while the loop goes consecutively.
|
||||
const int dof_i = pfes->DofToVDof(i, 0);
|
||||
if (parallel && pfes->GetLocalTDofNumber(dof_i) < 0) { continue; }
|
||||
#endif
|
||||
dof_cnt++;
|
||||
err_max = fmax(err_max, fabs((*surf_fit_gf)(i)));
|
||||
err_sum += fabs((*surf_fit_gf)(i));
|
||||
|
||||
dof_cnt++;
|
||||
double sigma_s = 0.0;
|
||||
if (surf_fit_gf) { sigma_s = fabs((*surf_fit_gf)(i)); }
|
||||
if (surf_fit_pos)
|
||||
{
|
||||
Vector pos_s(dim), pos_s_target(dim);
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
pos_s(d) = (fes->GetOrdering() == Ordering::byNODES) ?
|
||||
pos(d*node_cnt + i) : pos(i*dim + d);
|
||||
pos_s_target(d) = (fes->GetOrdering() == Ordering::byNODES)
|
||||
? (*surf_fit_pos)(d*node_cnt + i)
|
||||
: (*surf_fit_pos)(i*dim + d);
|
||||
}
|
||||
sigma_s = pos_s.DistanceTo(pos_s_target);
|
||||
}
|
||||
|
||||
err_max = fmax(err_max, sigma_s);
|
||||
err_sum += sigma_s;
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
@@ -3092,7 +3144,7 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
// as part of a FD derivative computation (because we include the exact
|
||||
// derivatives of these terms in FD computations).
|
||||
const bool adaptive_limiting = (adapt_lim_gf && fd_call_flag == false);
|
||||
const bool surface_fit = (surf_fit_gf && fd_call_flag == false);
|
||||
const bool surface_fit = (surf_fit_marker && fd_call_flag == false);
|
||||
|
||||
DSh.SetSize(dof, dim);
|
||||
Jrt.SetSize(dim);
|
||||
@@ -3195,21 +3247,42 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
// Contribution from the surface fitting term.
|
||||
if (surface_fit)
|
||||
{
|
||||
const IntegrationRule &ir_s =
|
||||
surf_fit_gf->FESpace()->GetFE(el_id)->GetNodes();
|
||||
Array<int> dofs;
|
||||
Vector sigma_e;
|
||||
surf_fit_gf->FESpace()->GetElementDofs(el_id, dofs);
|
||||
surf_fit_gf->GetSubVector(dofs, sigma_e);
|
||||
for (int s = 0; s < dofs.Size(); s++)
|
||||
// Scalar for surf_fit_gf, vector for surf_fit_pos, but that's ok.
|
||||
const FiniteElementSpace *fes_fit =
|
||||
(surf_fit_gf) ? surf_fit_gf->FESpace() : surf_fit_pos->FESpace();
|
||||
const IntegrationRule *ir_s = &fes_fit->GetFE(el_id)->GetNodes();
|
||||
Array<int> vdofs;
|
||||
fes_fit->GetElementVDofs(el_id, vdofs);
|
||||
|
||||
Vector sigma_e(dof);
|
||||
if (surf_fit_gf) { surf_fit_gf->GetSubVector(vdofs, sigma_e); }
|
||||
|
||||
for (int s = 0; s < dof; s++)
|
||||
{
|
||||
if ((*surf_fit_marker)[dofs[s]] == true)
|
||||
// Because surf_fit_pos.fes might be ordered byVDIM.
|
||||
const int scalar_dof_id = fes_fit->VDofToDof(vdofs[s]);
|
||||
if ((*surf_fit_marker)[scalar_dof_id] == false) { continue; }
|
||||
|
||||
const IntegrationPoint &ip_s = ir_s->IntPoint(s);
|
||||
Tpr->SetIntPoint(&ip_s);
|
||||
|
||||
if (surf_fit_gf)
|
||||
{
|
||||
const IntegrationPoint &ip_s = ir_s.IntPoint(s);
|
||||
Tpr->SetIntPoint(&ip_s);
|
||||
energy += surf_fit_coeff->Eval(*Tpr, ip_s) * surf_fit_normal *
|
||||
sigma_e(s) * sigma_e(s);
|
||||
}
|
||||
if (surf_fit_pos)
|
||||
{
|
||||
// Fitting to exact positions.
|
||||
Vector pos(dim), pos_target(dim);
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
pos(d) = PMatI(s, d);
|
||||
pos_target(d) = (*surf_fit_pos)(vdofs[d*dof + s]);
|
||||
}
|
||||
energy += surf_fit_coeff->Eval(*Tpr, ip_s) * surf_fit_normal *
|
||||
surf_fit_limiter->Eval(pos, pos_target, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3437,7 +3510,8 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el,
|
||||
|
||||
// Define ref->physical transformation, when a Coefficient is specified.
|
||||
IsoparametricTransformation *Tpr = NULL;
|
||||
if (metric_coeff || lim_coeff || adapt_lim_gf || surf_fit_gf || exact_action)
|
||||
if (metric_coeff || lim_coeff || adapt_lim_gf ||
|
||||
surf_fit_gf || surf_fit_pos || exact_action)
|
||||
{
|
||||
Tpr = new IsoparametricTransformation;
|
||||
Tpr->SetFE(&el);
|
||||
@@ -3519,7 +3593,7 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el,
|
||||
}
|
||||
|
||||
if (adapt_lim_gf) { AssembleElemVecAdaptLim(el, *Tpr, ir, weights, PMatO); }
|
||||
if (surf_fit_gf) { AssembleElemVecSurfFit(el, *Tpr, PMatO); }
|
||||
if (surf_fit_gf || surf_fit_pos) { AssembleElemVecSurfFit(el, *Tpr, PMatO); }
|
||||
|
||||
delete Tpr;
|
||||
}
|
||||
@@ -3571,7 +3645,7 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el,
|
||||
|
||||
// Define ref->physical transformation, when a Coefficient is specified.
|
||||
IsoparametricTransformation *Tpr = NULL;
|
||||
if (metric_coeff || lim_coeff || adapt_lim_gf || surf_fit_gf)
|
||||
if (metric_coeff || lim_coeff || adapt_lim_gf || surf_fit_gf || surf_fit_pos)
|
||||
{
|
||||
Tpr = new IsoparametricTransformation;
|
||||
Tpr->SetFE(&el);
|
||||
@@ -3627,7 +3701,7 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el,
|
||||
}
|
||||
|
||||
if (adapt_lim_gf) { AssembleElemGradAdaptLim(el, *Tpr, ir, weights, elmat); }
|
||||
if (surf_fit_gf) { AssembleElemGradSurfFit(el, *Tpr, elmat); }
|
||||
if (surf_fit_gf || surf_fit_pos) { AssembleElemGradSurfFit(el, *Tpr, elmat);}
|
||||
|
||||
delete Tpr;
|
||||
}
|
||||
@@ -3734,49 +3808,75 @@ void TMOP_Integrator::AssembleElemVecSurfFit(const FiniteElement &el_x,
|
||||
DenseMatrix &mat)
|
||||
{
|
||||
const int el_id = Tpr.ElementNo;
|
||||
|
||||
// Scalar for surf_fit_gf, vector for surf_fit_pos, but that's ok.
|
||||
const FiniteElementSpace *fes_fit =
|
||||
(surf_fit_gf) ? surf_fit_gf->FESpace() : surf_fit_pos->FESpace();
|
||||
const FiniteElement &el_s = *fes_fit->GetFE(el_id);
|
||||
const int dof_s = el_s.GetDof(), dim = el_x.GetDim();
|
||||
|
||||
// Check if the element has any DOFs marked for surface fitting.
|
||||
Array<int> sdofs, dofs;
|
||||
surf_fit_gf->FESpace()->GetElementDofs(el_id, sdofs);
|
||||
Array<int> dofs, vdofs;
|
||||
fes_fit->GetElementVDofs(el_id, vdofs);
|
||||
int count = 0;
|
||||
for (int s = 0; s < sdofs.Size(); s++)
|
||||
for (int s = 0; s < dof_s; s++)
|
||||
{
|
||||
count += ((*surf_fit_marker)[sdofs[s]]) ? 1 : 0;
|
||||
// Because surf_fit_pos.fes might be ordered byVDIM.
|
||||
const int scalar_dof_id = fes_fit->VDofToDof(vdofs[s]);
|
||||
count += ((*surf_fit_marker)[scalar_dof_id]) ? 1 : 0;
|
||||
}
|
||||
if (count == 0) { return; }
|
||||
|
||||
const FiniteElement &el_s = *surf_fit_gf->FESpace()->GetFE(el_id);
|
||||
|
||||
const int dof_s = el_s.GetDof(), dim = el_x.GetDim();
|
||||
|
||||
Vector sigma_e;
|
||||
surf_fit_gf->GetSubVector(sdofs, sigma_e);
|
||||
|
||||
// Project the gradient of sigma in the same space.
|
||||
// The FE coefficients of the gradient go in surf_fit_grad_e.
|
||||
Vector sigma_e(dof_s);
|
||||
DenseMatrix surf_fit_grad_e(dof_s, dim);
|
||||
Vector grad_ptr(surf_fit_grad_e.GetData(), dof_s * dim);
|
||||
DenseMatrix grad_phys; // This will be (dof x dim, dof).
|
||||
if (surf_fit_gf_bg)
|
||||
if (surf_fit_gf || surf_fit_gf_bg)
|
||||
{
|
||||
surf_fit_grad->FESpace()->GetElementVDofs(el_id, dofs);
|
||||
surf_fit_grad->GetSubVector(dofs, grad_ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
el_s.ProjectGrad(el_s, Tpr, grad_phys);
|
||||
grad_phys.Mult(sigma_e, grad_ptr);
|
||||
surf_fit_gf->GetSubVector(vdofs, sigma_e);
|
||||
|
||||
// Project the gradient of sigma in the same space.
|
||||
// The FE coefficients of the gradient go in surf_fit_grad_e.
|
||||
Vector grad_ptr(surf_fit_grad_e.GetData(), dof_s * dim);
|
||||
DenseMatrix grad_phys; // This will be (dof x dim, dof).
|
||||
if (surf_fit_gf_bg)
|
||||
{
|
||||
surf_fit_grad->FESpace()->GetElementVDofs(el_id, dofs);
|
||||
surf_fit_grad->GetSubVector(dofs, grad_ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
el_s.ProjectGrad(el_s, Tpr, grad_phys);
|
||||
grad_phys.Mult(sigma_e, grad_ptr);
|
||||
}
|
||||
}
|
||||
else { Tpr.GetPointMat().Transpose(PMatI); }
|
||||
|
||||
const IntegrationRule &ir = el_s.GetNodes();
|
||||
|
||||
for (int s = 0; s < dof_s; s++)
|
||||
{
|
||||
if ((*surf_fit_marker)[sdofs[s]] == false) { continue; }
|
||||
// Because surf_fit_pos.fes might be ordered byVDIM.
|
||||
const int scalar_dof_id = fes_fit->VDofToDof(vdofs[s]);
|
||||
if ((*surf_fit_marker)[scalar_dof_id] == false) { continue; }
|
||||
|
||||
const IntegrationPoint &ip = ir.IntPoint(s);
|
||||
Tpr.SetIntPoint(&ip);
|
||||
const double w = 2.0 * surf_fit_normal *
|
||||
surf_fit_coeff->Eval(Tpr, ip) * sigma_e(s) *
|
||||
1.0/surf_fit_dof_count[sdofs[s]];
|
||||
double w = surf_fit_normal * surf_fit_coeff->Eval(Tpr, ip) *
|
||||
1.0 / surf_fit_dof_count[vdofs[s]];
|
||||
|
||||
if (surf_fit_gf) { w *= 2.0 * sigma_e(s); }
|
||||
if (surf_fit_pos)
|
||||
{
|
||||
Vector pos(dim), pos_target(dim);
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
pos(d) = PMatI(s, d);
|
||||
pos_target(d) = (*surf_fit_pos)(vdofs[d*dof_s + s]);
|
||||
}
|
||||
Vector grad_s(dim);
|
||||
surf_fit_limiter->Eval_d1(pos, pos_target, 1.0, grad_s);
|
||||
for (int d = 0; d < dim; d++) { surf_fit_grad_e(s, d) = grad_s(d); }
|
||||
}
|
||||
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
mat(s, d) += w * surf_fit_grad_e(s, d);
|
||||
@@ -3789,79 +3889,109 @@ void TMOP_Integrator::AssembleElemGradSurfFit(const FiniteElement &el_x,
|
||||
DenseMatrix &mat)
|
||||
{
|
||||
const int el_id = Tpr.ElementNo;
|
||||
|
||||
// Scalar for surf_fit_gf, vector for surf_fit_pos, but that's ok.
|
||||
const FiniteElementSpace *fes_fit =
|
||||
(surf_fit_gf) ? surf_fit_gf->FESpace() : surf_fit_pos->FESpace();
|
||||
const FiniteElement &el_s = *fes_fit->GetFE(el_id);
|
||||
const int dof_s = el_s.GetDof(), dim = el_x.GetDim();
|
||||
|
||||
// Check if the element has any DOFs marked for surface fitting.
|
||||
Array<int> dofs, sdofs;
|
||||
surf_fit_gf->FESpace()->GetElementDofs(el_id, sdofs);
|
||||
int ndofs = sdofs.Size();
|
||||
Array<int> dofs, vdofs;
|
||||
fes_fit->GetElementVDofs(el_id, vdofs);
|
||||
int count = 0;
|
||||
for (int s = 0; s < ndofs; s++)
|
||||
for (int s = 0; s < dof_s; s++)
|
||||
{
|
||||
count += ((*surf_fit_marker)[sdofs[s]]) ? 1 : 0;
|
||||
// Because surf_fit_pos.fes might be ordered byVDIM.
|
||||
const int scalar_dof_id = fes_fit->VDofToDof(vdofs[s]);
|
||||
count += ((*surf_fit_marker)[scalar_dof_id]) ? 1 : 0;
|
||||
}
|
||||
if (count == 0) { return; }
|
||||
|
||||
const FiniteElement &el_s = *surf_fit_gf->FESpace()->GetFE(el_id);
|
||||
|
||||
const int dof_s = el_s.GetDof(), dim = el_x.GetDim();
|
||||
|
||||
Vector sigma_e;
|
||||
surf_fit_gf->GetSubVector(sdofs, sigma_e);
|
||||
|
||||
Vector sigma_e(dof_s);
|
||||
DenseMatrix surf_fit_grad_e(dof_s, dim);
|
||||
Vector grad_ptr(surf_fit_grad_e.GetData(), dof_s * dim);
|
||||
DenseMatrix grad_phys;
|
||||
if (surf_fit_gf_bg)
|
||||
{
|
||||
surf_fit_grad->FESpace()->GetElementVDofs(el_id, dofs);
|
||||
surf_fit_grad->GetSubVector(dofs, grad_ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
el_s.ProjectGrad(el_s, Tpr, grad_phys);
|
||||
grad_phys.Mult(sigma_e, grad_ptr);
|
||||
}
|
||||
|
||||
DenseMatrix surf_fit_hess_e(dof_s, dim*dim);
|
||||
Vector hess_ptr(surf_fit_hess_e.GetData(), dof_s*dim*dim);
|
||||
if (surf_fit_gf_bg)
|
||||
if (surf_fit_gf || surf_fit_gf_bg)
|
||||
{
|
||||
surf_fit_hess->FESpace()->GetElementVDofs(el_id, dofs);
|
||||
surf_fit_hess->GetSubVector(dofs, hess_ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
surf_fit_hess_e.SetSize(dof_s*dim, dim);
|
||||
Mult(grad_phys, surf_fit_grad_e, surf_fit_hess_e);
|
||||
surf_fit_hess_e.SetSize(dof_s, dim * dim);
|
||||
surf_fit_gf->GetSubVector(vdofs, sigma_e);
|
||||
|
||||
// Project the gradient of sigma in the same space.
|
||||
// The FE coefficients of the gradient go in surf_fit_grad_e.
|
||||
Vector grad_ptr(surf_fit_grad_e.GetData(), dof_s * dim);
|
||||
DenseMatrix grad_phys; // This will be (dof x dim, dof).
|
||||
if (surf_fit_gf_bg)
|
||||
{
|
||||
surf_fit_grad->FESpace()->GetElementVDofs(el_id, dofs);
|
||||
surf_fit_grad->GetSubVector(dofs, grad_ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
el_s.ProjectGrad(el_s, Tpr, grad_phys);
|
||||
grad_phys.Mult(sigma_e, grad_ptr);
|
||||
}
|
||||
|
||||
// Project the Hessian of sigma in the same space.
|
||||
// The FE coefficients of the Hessian go in surf_fit_hess_e.
|
||||
Vector hess_ptr(surf_fit_hess_e.GetData(), dof_s*dim*dim);
|
||||
if (surf_fit_gf_bg)
|
||||
{
|
||||
surf_fit_hess->FESpace()->GetElementVDofs(el_id, dofs);
|
||||
surf_fit_hess->GetSubVector(dofs, hess_ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
surf_fit_hess_e.SetSize(dof_s*dim, dim);
|
||||
Mult(grad_phys, surf_fit_grad_e, surf_fit_hess_e);
|
||||
surf_fit_hess_e.SetSize(dof_s, dim * dim);
|
||||
}
|
||||
}
|
||||
else { Tpr.GetPointMat().Transpose(PMatI); }
|
||||
|
||||
const IntegrationRule &ir = el_s.GetNodes();
|
||||
|
||||
Vector surf_fit_grad_s(dim);
|
||||
DenseMatrix surf_fit_hess_s(dim, dim);
|
||||
|
||||
for (int s = 0; s < dof_s; s++)
|
||||
{
|
||||
if ((*surf_fit_marker)[sdofs[s]] == false) { continue; }
|
||||
// Because surf_fit_pos.fes might be ordered byVDIM.
|
||||
const int scalar_dof_id = fes_fit->VDofToDof(vdofs[s]);
|
||||
if ((*surf_fit_marker)[scalar_dof_id] == false) { continue; }
|
||||
|
||||
const IntegrationPoint &ip = ir.IntPoint(s);
|
||||
Tpr.SetIntPoint(&ip);
|
||||
double w = surf_fit_normal * surf_fit_coeff->Eval(Tpr, ip);
|
||||
|
||||
Vector gg_ptr(surf_fit_hess_s.GetData(), dim * dim);
|
||||
surf_fit_hess_e.GetRow(s, gg_ptr);
|
||||
if (surf_fit_gf || surf_fit_gf_bg)
|
||||
{
|
||||
Vector gg_ptr(surf_fit_hess_s.GetData(), dim * dim);
|
||||
surf_fit_hess_e.GetRow(s, gg_ptr);
|
||||
w *= 2.0;
|
||||
}
|
||||
if (surf_fit_pos)
|
||||
{
|
||||
Vector pos(dim), pos_target(dim);
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
pos(d) = PMatI(s, d);
|
||||
pos_target(d) = (*surf_fit_pos)(vdofs[d*dof_s + s]);
|
||||
}
|
||||
// Eval_d2 returns the full Hessian, but we still use the general
|
||||
// computation that's in the dim x dim loop below.
|
||||
sigma_e(s) = 1.0;
|
||||
for (int d = 0; d < dim; d++) { surf_fit_grad_e(s, d) = 0.0; }
|
||||
surf_fit_limiter->Eval_d2(pos, pos_target, 1.0, surf_fit_hess_s);
|
||||
}
|
||||
|
||||
// Loops over the local matrix.
|
||||
const double w = surf_fit_normal * surf_fit_coeff->Eval(Tpr, ip);
|
||||
for (int idim = 0; idim < dim; idim++)
|
||||
{
|
||||
for (int jdim = 0; jdim <= idim; jdim++)
|
||||
{
|
||||
double entry = w * ( 2.0 * surf_fit_grad_e(s, idim) *
|
||||
/* */ surf_fit_grad_e(s, jdim) +
|
||||
2.0 * sigma_e(s) * surf_fit_hess_s(idim, jdim));
|
||||
entry *= 1.0/surf_fit_dof_count[sdofs[s]];
|
||||
int idx = s + idim*ndofs;
|
||||
int jdx = s + jdim*ndofs;
|
||||
double entry = w * ( surf_fit_grad_e(s, idim) *
|
||||
surf_fit_grad_e(s, jdim) +
|
||||
sigma_e(s) * surf_fit_hess_s(idim, jdim));
|
||||
entry *= 1.0 / surf_fit_dof_count[vdofs[s]];
|
||||
int idx = s + idim*dof_s;
|
||||
int jdx = s + jdim*dof_s;
|
||||
mat(idx, jdx) += entry;
|
||||
if (idx != jdx) { mat(jdx, idx) += entry; }
|
||||
}
|
||||
@@ -3930,7 +4060,7 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el,
|
||||
fd_call_flag = false;
|
||||
|
||||
// Contributions from adaptive limiting, surface fitting (exact derivatives).
|
||||
if (adapt_lim_gf || surf_fit_gf)
|
||||
if (adapt_lim_gf || surf_fit_gf || surf_fit_pos)
|
||||
{
|
||||
const IntegrationRule &ir = ActionIntegrationRule(el);
|
||||
const int nqp = ir.GetNPoints();
|
||||
@@ -3955,7 +4085,7 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el,
|
||||
|
||||
PMatO.UseExternalData(elvect.GetData(), dof, dim);
|
||||
if (adapt_lim_gf) { AssembleElemVecAdaptLim(el, Tpr, ir, weights, PMatO); }
|
||||
if (surf_fit_gf) { AssembleElemVecSurfFit(el, Tpr, PMatO); }
|
||||
if (surf_fit_gf || surf_fit_pos) { AssembleElemVecSurfFit(el, Tpr, PMatO); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4030,7 +4160,7 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el,
|
||||
fd_call_flag = false;
|
||||
|
||||
// Contributions from adaptive limiting.
|
||||
if (adapt_lim_gf || surf_fit_gf)
|
||||
if (adapt_lim_gf || surf_fit_gf || surf_fit_pos)
|
||||
{
|
||||
const IntegrationRule &ir = GradientIntegrationRule(el);
|
||||
const int nqp = ir.GetNPoints();
|
||||
@@ -4054,7 +4184,7 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el,
|
||||
}
|
||||
|
||||
if (adapt_lim_gf) { AssembleElemGradAdaptLim(el, Tpr, ir, weights, elmat); }
|
||||
if (surf_fit_gf) { AssembleElemGradSurfFit(el, Tpr, elmat); }
|
||||
if (surf_fit_gf || surf_fit_pos) { AssembleElemGradSurfFit(el, Tpr, elmat); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4087,7 +4217,7 @@ void TMOP_Integrator::EnableNormalization(const GridFunction &x)
|
||||
metric_normal = 1.0 / metric_normal;
|
||||
lim_normal = 1.0 / lim_normal;
|
||||
//if (surf_fit_gf) { surf_fit_normal = 1.0 / surf_fit_normal; }
|
||||
if (surf_fit_gf) { surf_fit_normal = lim_normal; }
|
||||
if (surf_fit_gf || surf_fit_pos) { surf_fit_normal = lim_normal; }
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
@@ -4100,7 +4230,7 @@ void TMOP_Integrator::ParEnableNormalization(const ParGridFunction &x)
|
||||
metric_normal = 1.0 / rdc[0];
|
||||
lim_normal = 1.0 / rdc[1];
|
||||
// if (surf_fit_gf) { surf_fit_normal = 1.0 / rdc[2]; }
|
||||
if (surf_fit_gf) { surf_fit_normal = lim_normal; }
|
||||
if (surf_fit_gf || surf_fit_pos) { surf_fit_normal = lim_normal; }
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+34
-10
@@ -1772,10 +1772,14 @@ protected:
|
||||
AdaptivityEvaluator *adapt_lim_eval; // Not owned.
|
||||
|
||||
// Surface fitting.
|
||||
GridFunction *surf_fit_gf; // Owned, Updated by surf_fit_eval.
|
||||
const Array<bool> *surf_fit_marker; // Not owned.
|
||||
Coefficient *surf_fit_coeff; // Not owned.
|
||||
AdaptivityEvaluator *surf_fit_eval; // Not owned.
|
||||
const Array<bool> *surf_fit_marker; // Not owned. Nodes to fit.
|
||||
Coefficient *surf_fit_coeff; // Not owned. Fitting term scaling.
|
||||
// Fitting to a discrete level set.
|
||||
GridFunction *surf_fit_gf; // Owned. Updated by surf_fit_eval.
|
||||
AdaptivityEvaluator *surf_fit_eval; // Not owned.
|
||||
// Fitting to given physical positions.
|
||||
TMOP_QuadraticLimiter *surf_fit_limiter; // Owned. Created internally.
|
||||
const GridFunction *surf_fit_pos; // Not owned. Positions to fit.
|
||||
double surf_fit_normal;
|
||||
bool surf_fit_gf_bg;
|
||||
GridFunction *surf_fit_grad, *surf_fit_hess;
|
||||
@@ -1976,9 +1980,10 @@ public:
|
||||
lim_dist(NULL), lim_func(NULL), lim_normal(1.0),
|
||||
adapt_lim_gf0(NULL), adapt_lim_gf(NULL), adapt_lim_coeff(NULL),
|
||||
adapt_lim_eval(NULL),
|
||||
surf_fit_gf(NULL), surf_fit_marker(NULL),
|
||||
surf_fit_coeff(NULL),
|
||||
surf_fit_eval(NULL), surf_fit_normal(1.0),
|
||||
surf_fit_marker(NULL), surf_fit_coeff(NULL),
|
||||
surf_fit_gf(NULL), surf_fit_eval(NULL),
|
||||
surf_fit_limiter(NULL), surf_fit_pos(NULL),
|
||||
surf_fit_normal(1.0),
|
||||
surf_fit_gf_bg(false), surf_fit_grad(NULL), surf_fit_hess(NULL),
|
||||
surf_fit_eval_bg_grad(NULL), surf_fit_eval_bg_hess(NULL),
|
||||
discr_tc(dynamic_cast<DiscreteAdaptTC *>(tc)),
|
||||
@@ -2080,7 +2085,7 @@ public:
|
||||
AdaptivityEvaluator &ae);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// Parallel support for surface fitting.
|
||||
/// Parallel support for surface fitting to the zero level set of a function.
|
||||
void EnableSurfaceFitting(const ParGridFunction &s0,
|
||||
const Array<bool> &smarker, Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae);
|
||||
@@ -2118,8 +2123,27 @@ public:
|
||||
ParGridFunction &s0_hess,
|
||||
AdaptivityEvaluator &ahe);
|
||||
#endif
|
||||
void GetSurfaceFittingErrors(double &err_avg, double &err_max);
|
||||
bool IsSurfaceFittingEnabled() { return (surf_fit_gf != NULL); }
|
||||
/** @brief Fitting of certain DOFs to given positions in physical space.
|
||||
|
||||
Having a set S of marked nodes (or DOFs) and their target positions in
|
||||
physical space x_t, we move these nodes to the target positions during
|
||||
the optimization process.
|
||||
This function adds to the TMOP functional the term
|
||||
@f$ \sum_{i \in S} c \frac{1}{2} (x_i - x_{t,i})^2 @f$,
|
||||
where @f$c@f$ corresponds to @a coeff below and is evaluated at the
|
||||
DOF locations.
|
||||
|
||||
@param[in] pos The desired positions for the mesh nodes.
|
||||
@param[in] smarker Indicates which DOFs will be aligned.
|
||||
@param[in] coeff Coefficient c for the above integral. */
|
||||
void EnableSurfaceFitting(const GridFunction &pos,
|
||||
const Array<bool> &smarker, Coefficient &coeff);
|
||||
void GetSurfaceFittingErrors(const Vector &pos,
|
||||
double &err_avg, double &err_max);
|
||||
bool IsSurfaceFittingEnabled()
|
||||
{
|
||||
return surf_fit_gf != NULL || surf_fit_pos != NULL;
|
||||
}
|
||||
|
||||
/// Update the original/reference nodes used for limiting.
|
||||
void SetLimitingNodes(const GridFunction &n0) { lim_nodes0 = &n0; }
|
||||
|
||||
+9
-7
@@ -419,7 +419,7 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
double avg_surf_fit_err, max_surf_fit_err = 0.0;
|
||||
if (surf_fit_max_threshold > 0.0)
|
||||
{
|
||||
GetSurfaceFittingError(avg_surf_fit_err, max_surf_fit_err);
|
||||
GetSurfaceFittingError(x_out_loc, avg_surf_fit_err, max_surf_fit_err);
|
||||
if (max_surf_fit_err < surf_fit_max_threshold)
|
||||
{
|
||||
if (print_options.iterations)
|
||||
@@ -519,7 +519,7 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
double avg_fit_err, max_fit_err = 0.0;
|
||||
if (surf_fit_max_threshold > 0.0)
|
||||
{
|
||||
GetSurfaceFittingError(avg_fit_err, max_fit_err);
|
||||
GetSurfaceFittingError(x_out_loc, avg_fit_err, max_fit_err);
|
||||
}
|
||||
if (surf_fit_max_threshold > 0.0 && max_fit_err >= 1.2*max_surf_fit_err)
|
||||
{
|
||||
@@ -662,7 +662,8 @@ void TMOPNewtonSolver::GetSurfaceFittingWeight(Array<double> &weights) const
|
||||
}
|
||||
}
|
||||
|
||||
void TMOPNewtonSolver::GetSurfaceFittingError(double &err_avg,
|
||||
void TMOPNewtonSolver::GetSurfaceFittingError(const Vector &x_loc,
|
||||
double &err_avg,
|
||||
double &err_max) const
|
||||
{
|
||||
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
|
||||
@@ -680,7 +681,7 @@ void TMOPNewtonSolver::GetSurfaceFittingError(double &err_avg,
|
||||
{
|
||||
if (ti->IsSurfaceFittingEnabled())
|
||||
{
|
||||
ti->GetSurfaceFittingErrors(err_avg_loc, err_max_loc);
|
||||
ti->GetSurfaceFittingErrors(x_loc, err_avg_loc, err_max_loc);
|
||||
err_avg = std::fmax(err_avg_loc, err_avg);
|
||||
err_max = std::fmax(err_max_loc, err_max);
|
||||
}
|
||||
@@ -693,7 +694,7 @@ void TMOPNewtonSolver::GetSurfaceFittingError(double &err_avg,
|
||||
{
|
||||
if (ati[j]->IsSurfaceFittingEnabled())
|
||||
{
|
||||
ati[j]->GetSurfaceFittingErrors(err_avg_loc, err_max_loc);
|
||||
ati[j]->GetSurfaceFittingErrors(x_loc, err_avg_loc, err_max_loc);
|
||||
err_avg = std::fmax(err_avg_loc, err_avg);
|
||||
err_max = std::fmax(err_max_loc, err_max);
|
||||
}
|
||||
@@ -733,12 +734,13 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
|
||||
}
|
||||
|
||||
Vector x_loc;
|
||||
const FiniteElementSpace *x_fes;
|
||||
const FiniteElementSpace *x_fes = nullptr;
|
||||
if (parallel)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
const ParNonlinearForm *pnlf =
|
||||
dynamic_cast<const ParNonlinearForm *>(oper);
|
||||
|
||||
x_fes = pnlf->ParFESpace();
|
||||
x_loc.SetSize(x_fes->GetVSize());
|
||||
x_fes->GetProlongationMatrix()->Mult(x, x_loc);
|
||||
@@ -789,7 +791,7 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
|
||||
if (update_surf_fit_coeff)
|
||||
{
|
||||
// Get surface fitting errors.
|
||||
GetSurfaceFittingError(surf_fit_err_avg, surf_fit_err_max);
|
||||
GetSurfaceFittingError(x_loc, surf_fit_err_avg, surf_fit_err_max);
|
||||
// Get array with surface fitting weights.
|
||||
Array<double> weights;
|
||||
GetSurfaceFittingWeight(weights);
|
||||
|
||||
+2
-1
@@ -181,7 +181,8 @@ protected:
|
||||
/// Get the average and maximum surface fitting error at the marked nodes.
|
||||
/// If there is more than 1 TMOP integrator, we get the maximum of the
|
||||
/// average and maximum error over all integrators.
|
||||
virtual void GetSurfaceFittingError(double &err_avg, double &err_max) const;
|
||||
virtual void GetSurfaceFittingError(const Vector &x_loc,
|
||||
double &err_avg, double &err_max) const;
|
||||
|
||||
/// Update surface fitting weight as surf_fit_weight *= factor.
|
||||
void UpdateSurfaceFittingWeight(double factor) const;
|
||||
|
||||
+344
-106
@@ -546,6 +546,234 @@ L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
|
||||
const FiniteElementSpace& fes_ho_, const FiniteElementSpace& fes_lor_)
|
||||
: L2Projection(fes_ho_, fes_lor_)
|
||||
{
|
||||
std::unique_ptr<SparseMatrix> R_mat, M_LH_mat;
|
||||
std::tie(R_mat, M_LH_mat) = ComputeSparseRAndM_LH();
|
||||
|
||||
FiniteElementSpace fes_ho_scalar(fes_ho.GetMesh(), fes_ho.FEColl(), 1);
|
||||
FiniteElementSpace fes_lor_scalar(fes_lor.GetMesh(), fes_lor.FEColl(), 1);
|
||||
|
||||
const SparseMatrix *P_ho = fes_ho_scalar.GetConformingProlongation();
|
||||
const SparseMatrix *P_lor = fes_lor_scalar.GetConformingProlongation();
|
||||
|
||||
if (P_ho || P_lor)
|
||||
{
|
||||
if (P_ho && P_lor)
|
||||
{
|
||||
R_mat.reset(RAP(*P_lor, *R_mat, *P_ho));
|
||||
M_LH_mat.reset(RAP(*P_lor, *M_LH_mat, *P_ho));
|
||||
}
|
||||
else if (P_ho)
|
||||
{
|
||||
R_mat.reset(mfem::Mult(*R_mat, *P_ho));
|
||||
M_LH_mat.reset(mfem::Mult(*M_LH_mat, *P_ho));
|
||||
}
|
||||
else // P_lor != nullptr
|
||||
{
|
||||
R_mat.reset(mfem::Mult(*P_lor, *R_mat));
|
||||
M_LH_mat.reset(mfem::Mult(*P_lor, *M_LH_mat));
|
||||
}
|
||||
}
|
||||
|
||||
SparseMatrix *RTxM_LH_mat = TransposeMult(*R_mat, *M_LH_mat);
|
||||
precon.reset(new DSmoother(*RTxM_LH_mat));
|
||||
|
||||
// Set ownership
|
||||
RTxM_LH.reset(RTxM_LH_mat);
|
||||
R = std::move(R_mat);
|
||||
M_LH = std::move(M_LH_mat);
|
||||
|
||||
SetupPCG();
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
|
||||
const ParFiniteElementSpace& pfes_ho, const ParFiniteElementSpace& pfes_lor)
|
||||
: L2Projection(pfes_ho, pfes_lor),
|
||||
pcg(pfes_ho.GetComm())
|
||||
{
|
||||
std::tie(R, M_LH) = ComputeSparseRAndM_LH();
|
||||
|
||||
ParFiniteElementSpace pfes_ho_scalar(pfes_ho.GetParMesh(),
|
||||
pfes_ho.FEColl(), 1);
|
||||
ParFiniteElementSpace pfes_lor_scalar(pfes_lor.GetParMesh(),
|
||||
pfes_lor.FEColl(), 1);
|
||||
|
||||
HypreParMatrix R_local = HypreParMatrix(pfes_ho.GetComm(),
|
||||
pfes_lor_scalar.GlobalVSize(),
|
||||
pfes_ho_scalar.GlobalVSize(),
|
||||
pfes_lor_scalar.GetDofOffsets(),
|
||||
pfes_ho_scalar.GetDofOffsets(),
|
||||
static_cast<SparseMatrix*>(R.get()));
|
||||
HypreParMatrix M_LH_local = HypreParMatrix(pfes_ho.GetComm(),
|
||||
pfes_lor_scalar.GlobalVSize(),
|
||||
pfes_ho_scalar.GlobalVSize(),
|
||||
pfes_lor_scalar.GetDofOffsets(),
|
||||
pfes_ho_scalar.GetDofOffsets(),
|
||||
static_cast<SparseMatrix*>(M_LH.get()));
|
||||
|
||||
HypreParMatrix *R_mat = RAP(pfes_lor_scalar.Dof_TrueDof_Matrix(),
|
||||
&R_local, pfes_ho_scalar.Dof_TrueDof_Matrix());
|
||||
HypreParMatrix *M_LH_mat = RAP(pfes_lor_scalar.Dof_TrueDof_Matrix(),
|
||||
&M_LH_local, pfes_ho_scalar.Dof_TrueDof_Matrix());
|
||||
|
||||
std::unique_ptr<HypreParMatrix> R_T(R_mat->Transpose());
|
||||
HypreParMatrix *RTxM_LH_mat = ParMult(R_T.get(), M_LH_mat, true);
|
||||
|
||||
HypreBoomerAMG *amg = new HypreBoomerAMG(*RTxM_LH_mat);
|
||||
amg->SetPrintLevel(0);
|
||||
|
||||
R.reset(R_mat);
|
||||
M_LH.reset(M_LH_mat);
|
||||
RTxM_LH.reset(RTxM_LH_mat);
|
||||
precon.reset(amg);
|
||||
|
||||
SetupPCG();
|
||||
pcg.SetPreconditioner(*precon);
|
||||
pcg.SetOperator(*RTxM_LH);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetupPCG()
|
||||
{
|
||||
// Basic PCG solver setup
|
||||
pcg.SetPrintLevel(0);
|
||||
// pcg.SetPrintLevel(IterativeSolver::PrintLevel().Summary());
|
||||
pcg.SetMaxIter(1000);
|
||||
// initial values for relative and absolute tolerance
|
||||
pcg.SetRelTol(1e-13);
|
||||
pcg.SetAbsTol(1e-13);
|
||||
pcg.SetPreconditioner(*precon);
|
||||
pcg.SetOperator(*RTxM_LH);
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::Mult(
|
||||
const Vector& x, Vector& y) const
|
||||
{
|
||||
Vector X(fes_ho.GetTrueVSize());
|
||||
Vector X_dim(R->Width());
|
||||
|
||||
Vector Y_dim(R->Height());
|
||||
Vector Y(fes_lor.GetTrueVSize());
|
||||
|
||||
Array<int> vdofs_list;
|
||||
|
||||
GetTDofs(fes_ho, x, X);
|
||||
|
||||
for (int d = 0; d < fes_ho.GetVDim(); ++d)
|
||||
{
|
||||
TDofsListByVDim(fes_ho, d, vdofs_list);
|
||||
X.GetSubVector(vdofs_list, X_dim);
|
||||
R->Mult(X_dim, Y_dim);
|
||||
TDofsListByVDim(fes_lor, d, vdofs_list);
|
||||
Y.SetSubVector(vdofs_list, Y_dim);
|
||||
}
|
||||
|
||||
SetFromTDofs(fes_lor, Y, y);
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::MultTranspose(
|
||||
const Vector& x, Vector& y) const
|
||||
{
|
||||
Vector X(fes_lor.GetTrueVSize());
|
||||
Vector X_dim(R->Height());
|
||||
|
||||
Vector Y_dim(R->Width());
|
||||
Vector Y(fes_ho.GetTrueVSize());
|
||||
|
||||
Array<int> vdofs_list;
|
||||
|
||||
GetTDofsTranspose(fes_lor, x, X);
|
||||
|
||||
for (int d = 0; d < fes_ho.GetVDim(); ++d)
|
||||
{
|
||||
TDofsListByVDim(fes_lor, d, vdofs_list);
|
||||
X.GetSubVector(vdofs_list, X_dim);
|
||||
R->MultTranspose(X_dim, Y_dim);
|
||||
TDofsListByVDim(fes_ho, d, vdofs_list);
|
||||
Y.SetSubVector(vdofs_list, Y_dim);
|
||||
}
|
||||
|
||||
SetFromTDofsTranspose(fes_ho, Y, y);
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::Prolongate(
|
||||
const Vector& x, Vector& y) const
|
||||
{
|
||||
Vector X(fes_lor.GetTrueVSize());
|
||||
Vector X_dim(M_LH->Height());
|
||||
Vector Xbar(pcg.Width());
|
||||
|
||||
Vector Y_dim(pcg.Height());
|
||||
Vector Y(fes_ho.GetTrueVSize());
|
||||
|
||||
Array<int> vdofs_list;
|
||||
|
||||
GetTDofs(fes_lor, x, X);
|
||||
|
||||
for (int d = 0; d < fes_ho.GetVDim(); ++d)
|
||||
{
|
||||
TDofsListByVDim(fes_lor, d, vdofs_list);
|
||||
X.GetSubVector(vdofs_list, X_dim);
|
||||
// Compute y = P x = (R^T M_LH)^(-1) M_LH^T X = (R^T M_LH)^(-1) Xbar
|
||||
M_LH->MultTranspose(X_dim, Xbar);
|
||||
Y_dim = 0.0;
|
||||
pcg.Mult(Xbar, Y_dim);
|
||||
TDofsListByVDim(fes_ho, d, vdofs_list);
|
||||
Y.SetSubVector(vdofs_list, Y_dim);
|
||||
}
|
||||
|
||||
SetFromTDofs(fes_ho, Y, y);
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::ProlongateTranspose(
|
||||
const Vector& x, Vector& y) const
|
||||
{
|
||||
Vector X(fes_ho.GetTrueVSize());
|
||||
Vector X_dim(pcg.Width());
|
||||
Vector Xbar(pcg.Height());
|
||||
|
||||
Vector Y_dim(M_LH->Height());
|
||||
Vector Y(fes_lor.GetTrueVSize());
|
||||
|
||||
Array<int> vdofs_list;
|
||||
|
||||
GetTDofsTranspose(fes_ho, x, X);
|
||||
|
||||
for (int d = 0; d < fes_ho.GetVDim(); ++d)
|
||||
{
|
||||
TDofsListByVDim(fes_ho, d, vdofs_list);
|
||||
X.GetSubVector(vdofs_list, X_dim);
|
||||
// Compute y = P^T x = M_LH (R^T M_LH)^(-1) X = M_LH Xbar
|
||||
Xbar = 0.0;
|
||||
pcg.Mult(X_dim, Xbar);
|
||||
M_LH->Mult(Xbar, Y_dim);
|
||||
TDofsListByVDim(fes_lor, d, vdofs_list);
|
||||
Y.SetSubVector(vdofs_list, Y_dim);
|
||||
}
|
||||
|
||||
SetFromTDofsTranspose(fes_lor, Y, y);
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetRelTol(double p_rtol_)
|
||||
{
|
||||
pcg.SetRelTol(p_rtol_);
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetAbsTol(double p_atol_)
|
||||
{
|
||||
pcg.SetAbsTol(p_atol_);
|
||||
}
|
||||
|
||||
std::pair<
|
||||
std::unique_ptr<SparseMatrix>,
|
||||
std::unique_ptr<SparseMatrix>>
|
||||
L2ProjectionGridTransfer::L2ProjectionH1Space::ComputeSparseRAndM_LH()
|
||||
{
|
||||
std::pair<std::unique_ptr<SparseMatrix>,
|
||||
std::unique_ptr<SparseMatrix>> r_and_mlh;
|
||||
|
||||
Mesh* mesh_ho = fes_ho.GetMesh();
|
||||
Mesh* mesh_lor = fes_lor.GetMesh();
|
||||
int nel_ho = mesh_ho->GetNE();
|
||||
@@ -553,7 +781,7 @@ L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
|
||||
int ndof_lor = fes_lor.GetNDofs();
|
||||
|
||||
// If the local mesh is empty, skip all computations
|
||||
if (nel_ho == 0) { return; }
|
||||
if (nel_ho == 0) { return {nullptr, nullptr}; }
|
||||
|
||||
const CoarseFineTransformations& cf_tr = mesh_lor->GetRefinementTransforms();
|
||||
|
||||
@@ -611,18 +839,26 @@ L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
|
||||
}
|
||||
}
|
||||
// DOF by DOF inverse of non-zero entries
|
||||
for (int i = 0; i < ndof_lor; ++i)
|
||||
{
|
||||
ML_inv[i] = 1.0 / ML_inv[i];
|
||||
}
|
||||
LumpedMassInverse(ML_inv);
|
||||
|
||||
// Compute sparsity pattern for R = M_L^(-1) M_LH and allocate
|
||||
AllocR();
|
||||
r_and_mlh.first = AllocR();
|
||||
// Allocate M_LH (same sparsity pattern as R)
|
||||
// L refers to the low-order refined mesh (DOFs correspond to rows)
|
||||
// H refers to the higher-order mesh (DOFs correspond to columns)
|
||||
M_LH = SparseMatrix(R.GetI(), R.GetJ(), NULL,
|
||||
R.Height(), R.Width(), false, true, true);
|
||||
Memory<int> I(r_and_mlh.first->Height() + 1);
|
||||
for (int icol = 0; icol < r_and_mlh.first->Height() + 1; ++icol)
|
||||
{
|
||||
I[icol] = r_and_mlh.first->GetI()[icol];
|
||||
}
|
||||
Memory<int> J(r_and_mlh.first->NumNonZeroElems());
|
||||
for (int jcol = 0; jcol < r_and_mlh.first->NumNonZeroElems(); ++jcol)
|
||||
{
|
||||
J[jcol] = r_and_mlh.first->GetJ()[jcol];
|
||||
}
|
||||
r_and_mlh.second = std::unique_ptr<SparseMatrix>(new SparseMatrix(
|
||||
I, J, NULL,
|
||||
r_and_mlh.first->Height(), r_and_mlh.first->Width(), true, true, true));
|
||||
|
||||
IntegrationPointTransformation ip_tr;
|
||||
IsoparametricTransformation& emb_tr = ip_tr.Transf;
|
||||
@@ -667,131 +903,118 @@ L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
|
||||
}
|
||||
Array<int> dofs_ho(nedof_ho);
|
||||
fes_ho.GetElementDofs(iho, dofs_ho);
|
||||
M_LH.AddSubMatrix(dofs_lor, dofs_ho, M_LH_el);
|
||||
R.AddSubMatrix(dofs_lor, dofs_ho, R_el);
|
||||
r_and_mlh.second->AddSubMatrix(dofs_lor, dofs_ho, M_LH_el);
|
||||
r_and_mlh.first->AddSubMatrix(dofs_lor, dofs_ho, R_el);
|
||||
}
|
||||
}
|
||||
|
||||
// Create PCG solver
|
||||
RTxM_LH = TransposeMult(R, M_LH);
|
||||
pcg.SetPrintLevel(0);
|
||||
pcg.SetMaxIter(1000);
|
||||
// initial values for relative and absolute tolerance
|
||||
SetRelTol(1e-13);
|
||||
SetAbsTol(1e-13);
|
||||
Ds = DSmoother(*RTxM_LH);
|
||||
pcg.SetPreconditioner(Ds);
|
||||
pcg.SetOperator(*RTxM_LH);
|
||||
return r_and_mlh;
|
||||
}
|
||||
|
||||
L2ProjectionGridTransfer::L2ProjectionH1Space::~L2ProjectionH1Space()
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::GetTDofs(
|
||||
const FiniteElementSpace& fes, const Vector& x, Vector& X) const
|
||||
{
|
||||
delete RTxM_LH;
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::Mult(
|
||||
const Vector& x, Vector& y) const
|
||||
{
|
||||
int vdim = fes_ho.GetVDim();
|
||||
const int ndof_ho = fes_ho.GetNDofs();
|
||||
const int ndof_lor = fes_lor.GetNDofs();
|
||||
Array<int> dofs_ho(ndof_ho);
|
||||
Array<int> dofs_lor(ndof_lor);
|
||||
Vector x_dim(ndof_ho);
|
||||
Vector y_dim(ndof_lor);
|
||||
|
||||
for (int d = 0; d < vdim; ++d)
|
||||
const Operator* res = fes.GetRestrictionOperator();
|
||||
if (res)
|
||||
{
|
||||
fes_ho.GetVDofs(d, dofs_ho);
|
||||
fes_lor.GetVDofs(d, dofs_lor);
|
||||
x.GetSubVector(dofs_ho, x_dim);
|
||||
R.Mult(x_dim, y_dim);
|
||||
y.SetSubVector(dofs_lor, y_dim);
|
||||
res->Mult(x, X);
|
||||
}
|
||||
else
|
||||
{
|
||||
X = x;
|
||||
}
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::MultTranspose(
|
||||
const Vector& x, Vector& y) const
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetFromTDofs(
|
||||
const FiniteElementSpace& fes, const Vector &X, Vector& x) const
|
||||
{
|
||||
int vdim = fes_ho.GetVDim();
|
||||
const int ndof_ho = fes_ho.GetNDofs();
|
||||
const int ndof_lor = fes_lor.GetNDofs();
|
||||
Array<int> dofs_ho(ndof_ho);
|
||||
Array<int> dofs_lor(ndof_lor);
|
||||
Vector x_dim(ndof_lor);
|
||||
Vector y_dim(ndof_ho);
|
||||
|
||||
for (int d = 0; d < vdim; ++d)
|
||||
const Operator* P = fes.GetProlongationMatrix();
|
||||
if (P)
|
||||
{
|
||||
fes_ho.GetVDofs(d, dofs_ho);
|
||||
fes_lor.GetVDofs(d, dofs_lor);
|
||||
x.GetSubVector(dofs_lor, x_dim);
|
||||
R.MultTranspose(x_dim, y_dim);
|
||||
y.SetSubVector(dofs_ho, y_dim);
|
||||
P->Mult(X, x);
|
||||
}
|
||||
else
|
||||
{
|
||||
x = X;
|
||||
}
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::Prolongate(
|
||||
const Vector& x, Vector& y) const
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::GetTDofsTranspose(
|
||||
const FiniteElementSpace& fes, const Vector& x, Vector& X) const
|
||||
{
|
||||
int vdim = fes_ho.GetVDim();
|
||||
const int ndof_ho = fes_ho.GetNDofs();
|
||||
const int ndof_lor = fes_lor.GetNDofs();
|
||||
Array<int> dofs_ho(ndof_ho);
|
||||
Array<int> dofs_lor(ndof_lor);
|
||||
Vector x_dim(ndof_lor);
|
||||
Vector y_dim(ndof_ho);
|
||||
Vector xbar(ndof_ho);
|
||||
|
||||
for (int d = 0; d < vdim; ++d)
|
||||
const Operator* P = fes.GetProlongationMatrix();
|
||||
if (P)
|
||||
{
|
||||
fes_lor.GetVDofs(d, dofs_lor);
|
||||
x.GetSubVector(dofs_lor, x_dim);
|
||||
// Compute y = P x = (R^T M_LH)^(-1) M_LH^T x = (R^T M_LH)^(-1) xbar
|
||||
M_LH.MultTranspose(x_dim, xbar);
|
||||
y_dim = 0.0;
|
||||
pcg.Mult(xbar, y_dim);
|
||||
fes_ho.GetVDofs(d, dofs_ho);
|
||||
y.SetSubVector(dofs_ho, y_dim);
|
||||
P->MultTranspose(x, X);
|
||||
}
|
||||
else
|
||||
{
|
||||
X = x;
|
||||
}
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::ProlongateTranspose(
|
||||
const Vector& x, Vector& y) const
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetFromTDofsTranspose(
|
||||
const FiniteElementSpace& fes, const Vector &X, Vector& x) const
|
||||
{
|
||||
int vdim = fes_ho.GetVDim();
|
||||
const int ndof_ho = fes_ho.GetNDofs();
|
||||
const int ndof_lor = fes_lor.GetNDofs();
|
||||
Array<int> dofs_ho(ndof_ho);
|
||||
Array<int> dofs_lor(ndof_lor);
|
||||
Vector x_dim(ndof_ho);
|
||||
Vector y_dim(ndof_lor);
|
||||
Vector xbar(ndof_ho);
|
||||
|
||||
for (int d = 0; d < vdim; ++d)
|
||||
const Operator *R_op = fes.GetRestrictionOperator();
|
||||
if (R_op)
|
||||
{
|
||||
fes_ho.GetVDofs(d, dofs_ho);
|
||||
x.GetSubVector(dofs_ho, x_dim);
|
||||
// Compute y = P^T x = M_LH (R^T M_LH)^(-1) x = M_LH xbar
|
||||
xbar = 0.0;
|
||||
pcg.Mult(x_dim, xbar);
|
||||
M_LH.Mult(xbar, y_dim);
|
||||
fes_lor.GetVDofs(d, dofs_lor);
|
||||
y.SetSubVector(dofs_lor, y_dim);
|
||||
R_op->MultTranspose(X, x);
|
||||
}
|
||||
else
|
||||
{
|
||||
x = X;
|
||||
}
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetRelTol(double p_rtol_)
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::TDofsListByVDim(
|
||||
const FiniteElementSpace& fes, int vdim, Array<int>& vdofs_list) const
|
||||
{
|
||||
pcg.SetRelTol(p_rtol_);
|
||||
const SparseMatrix *R_mat = fes.GetRestrictionMatrix();
|
||||
if (R_mat)
|
||||
{
|
||||
Array<int> x_vdofs_list(fes.GetNDofs());
|
||||
Array<int> x_vdofs_marker(fes.GetVSize());
|
||||
Array<int> X_vdofs_marker(fes.GetTrueVSize());
|
||||
fes.GetVDofs(vdim, x_vdofs_list);
|
||||
FiniteElementSpace::ListToMarker(x_vdofs_list, fes.GetVSize(), x_vdofs_marker);
|
||||
R_mat->BooleanMult(x_vdofs_marker, X_vdofs_marker);
|
||||
FiniteElementSpace::MarkerToList(X_vdofs_marker, vdofs_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
vdofs_list.SetSize(fes.GetNDofs());
|
||||
fes.GetVDofs(vdim, vdofs_list);
|
||||
}
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetAbsTol(double p_atol_)
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::LumpedMassInverse(
|
||||
Vector& ML_inv) const
|
||||
{
|
||||
pcg.SetAbsTol(p_atol_);
|
||||
Vector ML_inv_full(fes_lor.GetVSize());
|
||||
// set ML_inv on dofs for vdim = 0
|
||||
Array<int> vdofs_list(fes_lor.GetNDofs());
|
||||
fes_lor.GetVDofs(0, vdofs_list);
|
||||
ML_inv_full.SetSubVector(vdofs_list, ML_inv);
|
||||
|
||||
Vector ML_inv_true(fes_lor.GetTrueVSize());
|
||||
const Operator *P = fes_lor.GetProlongationMatrix();
|
||||
if (P) { P->MultTranspose(ML_inv_full, ML_inv_true); }
|
||||
else { ML_inv_true = ML_inv_full; }
|
||||
|
||||
for (int i = 0; i < ML_inv_true.Size(); ++i)
|
||||
{
|
||||
ML_inv_true[i] = 1.0 / ML_inv_true[i];
|
||||
}
|
||||
|
||||
if (P) { P->Mult(ML_inv_true, ML_inv_full); }
|
||||
else { ML_inv_full = ML_inv_true; }
|
||||
|
||||
ML_inv_full.GetSubVector(vdofs_list, ML_inv);
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::AllocR()
|
||||
std::unique_ptr<SparseMatrix>
|
||||
L2ProjectionGridTransfer::L2ProjectionH1Space::AllocR()
|
||||
{
|
||||
const Table& elem_dof_ho = fes_ho.GetElementToDofTable();
|
||||
const Table& elem_dof_lor = fes_lor.GetElementToDofTable();
|
||||
@@ -871,11 +1094,13 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::AllocR()
|
||||
dof_lor_dof_ho.SortRows();
|
||||
double* data = Memory<double>(dof_dofI[ndof_lor]);
|
||||
|
||||
R = SparseMatrix(dof_dofI, dof_dofJ, data, ndof_lor, ndof_ho,
|
||||
true, true, true);
|
||||
R = 0.0;
|
||||
std::unique_ptr<SparseMatrix> R_local(new SparseMatrix(
|
||||
dof_dofI, dof_dofJ, data, ndof_lor, ndof_ho, true, true, true));
|
||||
(*R_local) = 0.0;
|
||||
|
||||
dof_lor_dof_ho.LoseData();
|
||||
|
||||
return R_local;
|
||||
}
|
||||
|
||||
L2ProjectionGridTransfer::~L2ProjectionGridTransfer()
|
||||
@@ -905,7 +1130,20 @@ void L2ProjectionGridTransfer::BuildF()
|
||||
if (!force_l2_space &&
|
||||
dom_fes.FEColl()->GetContType() == FiniteElementCollection::CONTINUOUS)
|
||||
{
|
||||
F = new L2ProjectionH1Space(dom_fes, ran_fes);
|
||||
if (!Parallel())
|
||||
{
|
||||
F = new L2ProjectionH1Space(dom_fes, ran_fes);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
const mfem::ParFiniteElementSpace& dom_pfes =
|
||||
static_cast<mfem::ParFiniteElementSpace&>(dom_fes);
|
||||
const mfem::ParFiniteElementSpace& ran_pfes =
|
||||
static_cast<mfem::ParFiniteElementSpace&>(ran_fes);
|
||||
F = new L2ProjectionH1Space(dom_pfes, ran_pfes);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+64
-25
@@ -180,9 +180,15 @@ protected:
|
||||
public:
|
||||
virtual void Prolongate(const Vector& x, Vector& y) const = 0;
|
||||
virtual void ProlongateTranspose(const Vector& x, Vector& y) const = 0;
|
||||
/// Sets relative tolerance and absolute tolerance in preconditioned
|
||||
/// conjugate gradient solver. Only used for H1 spaces.
|
||||
/// @brief Sets relative tolerance in preconditioned conjugate gradient
|
||||
/// solver.
|
||||
///
|
||||
/// Only used for H1 spaces.
|
||||
virtual void SetRelTol(double p_rtol_) = 0;
|
||||
/// @brief Sets absolute tolerance in preconditioned conjugate gradient
|
||||
/// solver.
|
||||
///
|
||||
/// Only used for H1 spaces.
|
||||
virtual void SetAbsTol(double p_atol_) = 0;
|
||||
protected:
|
||||
const FiniteElementSpace& fes_ho;
|
||||
@@ -249,29 +255,22 @@ protected:
|
||||
/// conservative left-inverse prolongation operation. This functionality
|
||||
/// is also provided as an Operator by L2Prolongation.
|
||||
virtual void ProlongateTranspose(const Vector& x, Vector& y) const;
|
||||
virtual void SetRelTol(double p_rtol_) {}
|
||||
virtual void SetAbsTol(double p_atol_) {}
|
||||
virtual void SetRelTol(double p_rtol_) { } ///< No-op.
|
||||
virtual void SetAbsTol(double p_atol_) { } ///< No-op.
|
||||
};
|
||||
|
||||
/** Class for projection operator between a H1 high-order finite element
|
||||
space on a coarse mesh, and a H1 low-order finite element space on a
|
||||
refined mesh (LOR). */
|
||||
/** Projection operator between a H1 high-order finite element space on a
|
||||
coarse mesh, and a H1 low-order finite element space on a refined mesh
|
||||
(LOR). */
|
||||
class L2ProjectionH1Space : public L2Projection
|
||||
{
|
||||
// The restriction operator is represented as a SparseMatrix R. The
|
||||
// prolongation operator is a dense matrix computed as the inverse of (R^T
|
||||
// M_L R), and hence, is not stored.
|
||||
SparseMatrix R;
|
||||
// Used to compute P = (RTxM_LH)^(-1) M_LH^T
|
||||
SparseMatrix M_LH;
|
||||
SparseMatrix* RTxM_LH;
|
||||
CGSolver pcg;
|
||||
DSmoother Ds;
|
||||
|
||||
public:
|
||||
L2ProjectionH1Space(const FiniteElementSpace& fes_ho_,
|
||||
const FiniteElementSpace& fes_lor_);
|
||||
virtual ~L2ProjectionH1Space();
|
||||
L2ProjectionH1Space(const FiniteElementSpace &fes_ho_,
|
||||
const FiniteElementSpace &fes_lor_);
|
||||
#ifdef MFEM_USE_MPI
|
||||
L2ProjectionH1Space(const ParFiniteElementSpace &pfes_ho_,
|
||||
const ParFiniteElementSpace &pfes_lor_);
|
||||
#endif
|
||||
/// Maps <tt>x</tt>, primal field coefficients defined on a coarse mesh
|
||||
/// with a higher order H1 finite element space, to <tt>y</tt>, primal
|
||||
/// field coefficients defined on a refined mesh with a low order H1
|
||||
@@ -305,11 +304,51 @@ protected:
|
||||
virtual void ProlongateTranspose(const Vector& x, Vector& y) const;
|
||||
virtual void SetRelTol(double p_rtol_);
|
||||
virtual void SetAbsTol(double p_atol_);
|
||||
private:
|
||||
/// Computes sparsity pattern and initializes R matrix. Based on
|
||||
/// BilinearForm::AllocMat() except maps between HO elements and LOR
|
||||
/// elements.
|
||||
void AllocR();
|
||||
protected:
|
||||
/// Sets up the PCG solver (sets parameters, operator, and preconditioner)
|
||||
void SetupPCG();
|
||||
/// Computes on-rank R and M_LH matrices.
|
||||
std::pair<std::unique_ptr<SparseMatrix>,
|
||||
std::unique_ptr<SparseMatrix>> ComputeSparseRAndM_LH();
|
||||
/// @brief Recovers vector of tdofs given a vector of dofs and a finite
|
||||
/// element space
|
||||
void GetTDofs(const FiniteElementSpace& fes, const Vector& x, Vector& X) const;
|
||||
/// Sets dof values given a vector of tdofs and a finite element space
|
||||
void SetFromTDofs(const FiniteElementSpace& fes,
|
||||
const Vector& X,
|
||||
Vector& x) const;
|
||||
/// @brief Recovers a vector of dual field coefficients on the tdofs given
|
||||
/// a vector of dual coefficients and a finite element space
|
||||
void GetTDofsTranspose(const FiniteElementSpace& fes,
|
||||
const Vector& x,
|
||||
Vector& X) const;
|
||||
/// @brief Sets dual field coefficients given a vector of dual field
|
||||
/// coefficients on the tdofs and a finite element space
|
||||
void SetFromTDofsTranspose(const FiniteElementSpace& fes,
|
||||
const Vector& X,
|
||||
Vector& x) const;
|
||||
/// @brief Fills the vdofs_list array with a list of vdofs for a given
|
||||
/// vdim and a given finite element space
|
||||
void TDofsListByVDim(const FiniteElementSpace& fes,
|
||||
int vdim,
|
||||
Array<int>& vdofs_list) const;
|
||||
/// Returns the inverse of an on-rank lumped mass matrix
|
||||
void LumpedMassInverse(Vector& ML_inv) const;
|
||||
/// @brief Computes sparsity pattern and initializes R matrix.
|
||||
///
|
||||
/// Based on BilinearForm::AllocMat(), except maps between coarse HO
|
||||
/// elements and refined LOR elements.
|
||||
std::unique_ptr<SparseMatrix> AllocR();
|
||||
|
||||
CGSolver pcg;
|
||||
std::unique_ptr<Solver> precon;
|
||||
// The restriction operator is represented as an Operator R. The
|
||||
// prolongation operator is a dense matrix computed as the inverse of (R^T
|
||||
// M_L R), and hence, is not stored.
|
||||
std::unique_ptr<Operator> R;
|
||||
// Used to compute P = (RT*M_LH)^(-1) M_LH^T
|
||||
std::unique_ptr<Operator> M_LH;
|
||||
std::unique_ptr<Operator> RTxM_LH;
|
||||
};
|
||||
|
||||
/** Mass-conservative prolongation operator going in the opposite direction
|
||||
|
||||
@@ -465,6 +465,9 @@ public:
|
||||
|
||||
inline const T &operator()(int i, int j, int k) const;
|
||||
inline T &operator()(int i, int j, int k);
|
||||
|
||||
inline void operator=(const T &a)
|
||||
{ array1d = a; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
+10
-2
@@ -207,6 +207,9 @@ void OptionsParser::Parse()
|
||||
case STRING:
|
||||
*(const char **)(options[j].var_ptr) = argv[i++];
|
||||
break;
|
||||
case STD_STRING:
|
||||
*(std::string *)(options[j].var_ptr) = argv[i++];
|
||||
break;
|
||||
case ENABLE:
|
||||
*(bool *)(options[j].var_ptr) = true;
|
||||
option_check[j+1] = 1; // Do not allow the DISABLE Option
|
||||
@@ -284,6 +287,10 @@ void OptionsParser::WriteValue(const Option &opt, std::ostream &os)
|
||||
os << *(const char **)(opt.var_ptr);
|
||||
break;
|
||||
|
||||
case STD_STRING:
|
||||
out << *(std::string *)(opt.var_ptr);
|
||||
break;
|
||||
|
||||
case ARRAY:
|
||||
{
|
||||
Array<int> &list = *(Array<int>*)(opt.var_ptr);
|
||||
@@ -401,8 +408,9 @@ void OptionsParser::PrintHelp(ostream &os) const
|
||||
static const char *seprtr = ", ";
|
||||
static const char *descr_sep = "\n\t";
|
||||
static const char *line_sep = "";
|
||||
static const char *types[] = { " <int>", " <double>", " <string>", "", "",
|
||||
" '<int>...'", " '<double>...'"
|
||||
static const char *types[] = { " <int>", " <double>", " <string>",
|
||||
" <string>", "", "", " '<int>...'",
|
||||
" '<double>...'"
|
||||
};
|
||||
|
||||
os << indent << "-h" << seprtr << "--help" << descr_sep
|
||||
|
||||
+10
-1
@@ -31,7 +31,7 @@ class Vector;
|
||||
class OptionsParser
|
||||
{
|
||||
public:
|
||||
enum OptionType { INT, DOUBLE, STRING, ENABLE, DISABLE, ARRAY, VECTOR };
|
||||
enum OptionType { INT, DOUBLE, STRING, STD_STRING, ENABLE, DISABLE, ARRAY, VECTOR };
|
||||
|
||||
private:
|
||||
struct Option
|
||||
@@ -115,6 +115,15 @@ public:
|
||||
required));
|
||||
}
|
||||
|
||||
/// Add a string (std::string) option and set 'var' to receive the value.
|
||||
void AddOption(std::string *var, const char *short_name,
|
||||
const char *long_name, const char *description,
|
||||
bool required = false)
|
||||
{
|
||||
options.Append(Option(STD_STRING, var, short_name, long_name, description,
|
||||
required));
|
||||
}
|
||||
|
||||
/** Add an integer array (separated by spaces) option and set 'var' to
|
||||
receive the values. */
|
||||
void AddOption(Array<int> * var, const char *short_name,
|
||||
|
||||
+34
-2
@@ -4173,6 +4173,28 @@ DenseMatrixSVD::DenseMatrixSVD(int h, int w,
|
||||
Init();
|
||||
}
|
||||
|
||||
DenseMatrixSVD::DenseMatrixSVD(DenseMatrix &M,
|
||||
char left_singular_vectors,
|
||||
char right_singular_vectors)
|
||||
{
|
||||
m = M.Height();
|
||||
n = M.Width();
|
||||
jobu = left_singular_vectors;
|
||||
jobvt = right_singular_vectors;
|
||||
Init();
|
||||
}
|
||||
|
||||
DenseMatrixSVD::DenseMatrixSVD(int h, int w,
|
||||
char left_singular_vectors,
|
||||
char right_singular_vectors)
|
||||
{
|
||||
m = h;
|
||||
n = w;
|
||||
jobu = left_singular_vectors;
|
||||
jobvt = right_singular_vectors;
|
||||
Init();
|
||||
}
|
||||
|
||||
void DenseMatrixSVD::Init()
|
||||
{
|
||||
sv.SetSize(min(m, n));
|
||||
@@ -4195,12 +4217,22 @@ void DenseMatrixSVD::Eval(DenseMatrix &M)
|
||||
#endif
|
||||
double * datau = nullptr;
|
||||
double * datavt = nullptr;
|
||||
if (jobu == 'S')
|
||||
if (jobu == 'A')
|
||||
{
|
||||
U.SetSize(m,m);
|
||||
datau = U.Data();
|
||||
}
|
||||
else if (jobu == 'S')
|
||||
{
|
||||
U.SetSize(m,min(m,n));
|
||||
datau = U.Data();
|
||||
}
|
||||
if (jobvt == 'S')
|
||||
if (jobvt == 'A')
|
||||
{
|
||||
Vt.SetSize(n,n);
|
||||
datavt = Vt.Data();
|
||||
}
|
||||
else if (jobvt == 'S')
|
||||
{
|
||||
Vt.SetSize(min(m,n),n);
|
||||
datavt = Vt.Data();
|
||||
|
||||
+122
-4
@@ -939,7 +939,12 @@ public:
|
||||
~DenseMatrixGeneralizedEigensystem();
|
||||
};
|
||||
|
||||
/**
|
||||
@brief Class for Singular Value Decomposition of a DenseMatrix
|
||||
|
||||
Singular Value Decomposition (SVD) of a DenseMatrix with the use of the DGESVD
|
||||
driver from LAPACK.
|
||||
*/
|
||||
class DenseMatrixSVD
|
||||
{
|
||||
DenseMatrix Mc;
|
||||
@@ -955,16 +960,129 @@ class DenseMatrixSVD
|
||||
|
||||
void Init();
|
||||
public:
|
||||
|
||||
/**
|
||||
@brief Constructor for the DenseMatrixSVD
|
||||
|
||||
Constructor for the DenseMatrixSVD with LAPACK. The parameters for the left
|
||||
and right singular vectors can be choosen according to the parameters for
|
||||
the LAPACK DGESVD.
|
||||
|
||||
@param [in] M matrix to set the size to n=M.Height(), m=M.Width()
|
||||
@param [in] left_singular_vectors optional parameter to define if first
|
||||
left singular vectors should be computed
|
||||
@param [in] right_singular_vectors optional parameter to define if first
|
||||
right singular vectors should be computed
|
||||
*/
|
||||
MFEM_DEPRECATED DenseMatrixSVD(DenseMatrix &M,
|
||||
bool left_singular_vectors=false,
|
||||
bool right_singular_vectors=false);
|
||||
|
||||
/**
|
||||
@brief Constructor for the DenseMatrixSVD
|
||||
|
||||
Constructor for the DenseMatrixSVD with LAPACK. The parameters for the left
|
||||
and right singular
|
||||
vectors can be choosen according to the parameters for the LAPACK DGESVD.
|
||||
|
||||
@param [in] h height of the matrix
|
||||
@param [in] w width of the matrix
|
||||
@param [in] left_singular_vectors optional parameter to define if first
|
||||
left singular vectors should be computed
|
||||
@param [in] right_singular_vectors optional parameter to define if first
|
||||
right singular vectors should be computed
|
||||
*/
|
||||
MFEM_DEPRECATED DenseMatrixSVD(int h, int w,
|
||||
bool left_singular_vectors=false,
|
||||
bool right_singular_vectors=false);
|
||||
|
||||
/**
|
||||
@brief Constructor for the DenseMatrixSVD
|
||||
|
||||
Constructor for the DenseMatrixSVD with LAPACK. The parameters for the left
|
||||
and right singular vectors can be choosen according to the parameters for
|
||||
the LAPACK DGESVD.
|
||||
|
||||
@param [in] M matrix to set the size to n=M.Height(), m=M.Width()
|
||||
@param [in] left_singular_vectors optional parameter to define which left
|
||||
singular vectors should be computed
|
||||
@param [in] right_singular_vectors optional parameter to define which right
|
||||
singular vectors should be computed
|
||||
|
||||
Options for computation of singular vectors:
|
||||
|
||||
'A': All singular vectors are computed (default)
|
||||
|
||||
'S': The first min(n,m) singular vectors are computed
|
||||
|
||||
'N': No singular vectors are computed
|
||||
*/
|
||||
DenseMatrixSVD(DenseMatrix &M,
|
||||
bool left_singular_vectors=false,
|
||||
bool right_singlular_vectors=false);
|
||||
char left_singular_vectors='A',
|
||||
char right_singular_vectors='A');
|
||||
|
||||
/**
|
||||
@brief Constructor for the DenseMatrixSVD
|
||||
|
||||
Constructor for the DenseMatrixSVD with LAPACK. The parameters for the left
|
||||
and right singular vectors can be choosen according to the
|
||||
parameters for the LAPACK DGESVD.
|
||||
|
||||
@param [in] h height of the matrix
|
||||
@param [in] w width of the matrix
|
||||
@param [in] left_singular_vectors optional parameter to define which left
|
||||
singular vectors should be computed
|
||||
@param [in] right_singular_vectors optional parameter to define which right
|
||||
singular vectors should be computed
|
||||
|
||||
Options for computation of singular vectors:
|
||||
|
||||
'A': All singular vectors are computed (default)
|
||||
|
||||
'S': The first min(n,m) singular vectors are computed
|
||||
|
||||
'N': No singular vectors are computed
|
||||
*/
|
||||
DenseMatrixSVD(int h, int w,
|
||||
bool left_singular_vectors=false,
|
||||
bool right_singlular_vectors=false);
|
||||
char left_singular_vectors='A',
|
||||
char right_singular_vectors='A');
|
||||
|
||||
/**
|
||||
@brief Evaluate the SVD
|
||||
|
||||
Call of the DGESVD driver from LAPACK for the DenseMatrix M. The singular
|
||||
vectors are computed according to the setup in the call of the constructor.
|
||||
|
||||
@param [in] M DenseMatrix the SVD should be evaluated for
|
||||
*/
|
||||
void Eval(DenseMatrix &M);
|
||||
|
||||
/**
|
||||
@brief Return singular values
|
||||
|
||||
@return sv Vector containing all singular values
|
||||
*/
|
||||
Vector &Singularvalues() { return sv; }
|
||||
|
||||
/**
|
||||
@brief Return specific singular value
|
||||
|
||||
@return sv(i) i-th singular value
|
||||
*/
|
||||
double Singularvalue(int i) { return sv(i); }
|
||||
|
||||
/**
|
||||
@brief Return left singular vectors
|
||||
|
||||
@return U DenseMatrix containing left singular vectors
|
||||
*/
|
||||
DenseMatrix &LeftSingularvectors() { return U; }
|
||||
|
||||
/**
|
||||
@brief Return right singular vectors
|
||||
|
||||
@return Vt DenseMatrix containing right singular vectors
|
||||
*/
|
||||
DenseMatrix &RightSingularvectors() { return Vt; }
|
||||
~DenseMatrixSVD();
|
||||
};
|
||||
|
||||
+1
-1
@@ -5138,7 +5138,7 @@ void HypreBoomerAMG::SetAdvectiveOptions(int distanceR,
|
||||
double filterA_tol = 0.0;
|
||||
|
||||
// Set relaxation on specified grid points
|
||||
int ns_down, ns_up, ns_coarse;
|
||||
int ns_down = 0, ns_up = 0, ns_coarse; // init to suppress gcc warnings
|
||||
if (distanceR > 0)
|
||||
{
|
||||
ns_down = prerelax.length();
|
||||
|
||||
+829
-10
@@ -989,7 +989,6 @@ void GMRESSolver::Mult(const Vector &b, Vector &x) const
|
||||
Vector r(n), w(n);
|
||||
Array<Vector *> v;
|
||||
|
||||
double resid;
|
||||
int i, j, k;
|
||||
|
||||
if (iterative_mode)
|
||||
@@ -1035,7 +1034,6 @@ void GMRESSolver::Mult(const Vector &b, Vector &x) const
|
||||
final_iter = 0;
|
||||
converged = true;
|
||||
j = 0;
|
||||
resid = beta;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@@ -1089,7 +1087,7 @@ void GMRESSolver::Mult(const Vector &b, Vector &x) const
|
||||
ApplyPlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i));
|
||||
ApplyPlaneRotation(s(i), s(i+1), cs(i), sn(i));
|
||||
|
||||
resid = fabs(s(i+1));
|
||||
const double resid = fabs(s(i+1));
|
||||
MFEM_ASSERT(IsFinite(resid), "resid = " << resid);
|
||||
|
||||
if (resid <= final_norm)
|
||||
@@ -1148,7 +1146,7 @@ finish:
|
||||
{
|
||||
mfem::out << " Pass : " << setw(2) << (j-1)/m+1
|
||||
<< " Iteration : " << setw(3) << final_iter
|
||||
<< " ||B r|| = " << resid << '\n';
|
||||
<< " ||B r|| = " << final_norm << '\n';
|
||||
}
|
||||
if (print_options.summary || (print_options.warnings && !converged))
|
||||
{
|
||||
@@ -1175,7 +1173,6 @@ void FGMRESSolver::Mult(const Vector &b, Vector &x) const
|
||||
|
||||
int i, j, k;
|
||||
|
||||
|
||||
if (iterative_mode)
|
||||
{
|
||||
oper->Mult(x, r);
|
||||
@@ -1187,9 +1184,6 @@ void FGMRESSolver::Mult(const Vector &b, Vector &x) const
|
||||
r = b;
|
||||
}
|
||||
double beta = initial_norm = Norm(r); // beta = ||r||
|
||||
// We need to preallocate this to report the correct result in the case of
|
||||
// no convergence.
|
||||
double resid;
|
||||
MFEM_ASSERT(IsFinite(beta), "beta = " << beta);
|
||||
|
||||
final_norm = std::max(rel_tol*beta, abs_tol);
|
||||
@@ -1264,7 +1258,7 @@ void FGMRESSolver::Mult(const Vector &b, Vector &x) const
|
||||
ApplyPlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i));
|
||||
ApplyPlaneRotation(s(i), s(i+1), cs(i), sn(i));
|
||||
|
||||
resid = fabs(s(i+1));
|
||||
const double resid = fabs(s(i+1));
|
||||
MFEM_ASSERT(IsFinite(resid), "resid = " << resid);
|
||||
if (print_options.iterations || (print_options.first_and_last &&
|
||||
resid <= final_norm))
|
||||
@@ -1330,7 +1324,7 @@ void FGMRESSolver::Mult(const Vector &b, Vector &x) const
|
||||
{
|
||||
mfem::out << " Pass : " << setw(2) << (j-1)/m+1
|
||||
<< " Iteration : " << setw(3) << j-1
|
||||
<< " || r || = " << resid << endl;
|
||||
<< " || r || = " << final_norm << endl;
|
||||
}
|
||||
if (print_options.summary || (print_options.warnings && !converged))
|
||||
{
|
||||
@@ -3544,4 +3538,829 @@ void AuxSpaceSmoother::Mult(const Vector &x, Vector &y, bool transpose) const
|
||||
}
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
// LAPACK routines for NNLSSolver
|
||||
extern "C" void
|
||||
dormqr_(char *, char *, int *, int *, int *, double *, int*, double *,
|
||||
double *, int *, double *, int*, int*);
|
||||
|
||||
extern "C" void
|
||||
dgeqrf_(int *, int *, double *, int *, double *, double *, int *, int *);
|
||||
|
||||
extern "C" void
|
||||
dgemv_(char *, int *, int *, double *, double *, int *, double *, int *,
|
||||
double *, double *, int *);
|
||||
|
||||
extern "C" void
|
||||
dtrsm_(char *side, char *uplo, char *transa, char *diag, int *m, int *n,
|
||||
double *alpha, double *a, int *lda, double *b, int *ldb);
|
||||
|
||||
NNLSSolver::NNLSSolver()
|
||||
: Solver(0), mat(nullptr), const_tol_(1.0e-14), min_nnz_(0),
|
||||
max_nnz_(0), verbosity_(0), res_change_termination_tol_(1.0e-4),
|
||||
zero_tol_(1.0e-14), rhs_delta_(1.0e-11), n_outer_(100000),
|
||||
n_inner_(100000), nStallCheck_(100), normalize_(true),
|
||||
NNLS_qrres_on_(false), qr_residual_mode_(QRresidualMode::hybrid)
|
||||
{}
|
||||
|
||||
void NNLSSolver::SetOperator(const Operator &op)
|
||||
{
|
||||
mat = dynamic_cast<const DenseMatrix*>(&op);
|
||||
MFEM_VERIFY(mat, "NNLSSolver operator must be of type DenseMatrix");
|
||||
|
||||
// The size of this operator is that of the transpose of op.
|
||||
height = op.Width();
|
||||
width = op.Height();
|
||||
|
||||
row_scaling_.SetSize(mat->NumRows());
|
||||
row_scaling_ = 1.0;
|
||||
}
|
||||
|
||||
void NNLSSolver::SetQRResidualMode(const QRresidualMode qr_residual_mode)
|
||||
{
|
||||
qr_residual_mode_ = qr_residual_mode;
|
||||
if (qr_residual_mode_ == QRresidualMode::on)
|
||||
{
|
||||
NNLS_qrres_on_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void NNLSSolver::NormalizeConstraints(Vector& rhs_lb, Vector& rhs_ub) const
|
||||
{
|
||||
// Scale everything so that rescaled half gap is the same for all constraints
|
||||
const int m = mat->NumRows();
|
||||
|
||||
MFEM_VERIFY(rhs_lb.Size() == m && rhs_ub.Size() == m, "");
|
||||
|
||||
Vector rhs_avg = rhs_ub;
|
||||
rhs_avg += rhs_lb;
|
||||
rhs_avg *= 0.5;
|
||||
|
||||
Vector rhs_halfgap = rhs_ub;
|
||||
rhs_halfgap -= rhs_lb;
|
||||
rhs_halfgap *= 0.5;
|
||||
|
||||
Vector rhs_avg_glob = rhs_avg;
|
||||
Vector rhs_halfgap_glob = rhs_halfgap;
|
||||
Vector halfgap_target(m);
|
||||
halfgap_target = 1.0e3 * const_tol_;
|
||||
|
||||
row_scaling_.SetSize(m);
|
||||
|
||||
for (int i=0; i<m; ++i)
|
||||
{
|
||||
const double s = halfgap_target(i) / rhs_halfgap_glob(i);
|
||||
row_scaling_[i] = s;
|
||||
|
||||
rhs_lb(i) = (rhs_avg(i) * s) - halfgap_target(i);
|
||||
rhs_ub(i) = (rhs_avg(i) * s) + halfgap_target(i);
|
||||
}
|
||||
}
|
||||
|
||||
void NNLSSolver::Mult(const Vector &w, Vector &sol) const
|
||||
{
|
||||
MFEM_VERIFY(mat, "NNLSSolver operator must be of type DenseMatrix");
|
||||
Vector rhs_ub(mat->NumRows());
|
||||
mat->Mult(w, rhs_ub);
|
||||
rhs_ub *= row_scaling_;
|
||||
|
||||
Vector rhs_lb(rhs_ub);
|
||||
Vector rhs_Gw(rhs_ub);
|
||||
|
||||
for (int i=0; i<rhs_ub.Size(); ++i)
|
||||
{
|
||||
rhs_lb(i) -= rhs_delta_;
|
||||
rhs_ub(i) += rhs_delta_;
|
||||
}
|
||||
|
||||
if (normalize_) { NormalizeConstraints(rhs_lb, rhs_ub); }
|
||||
Solve(rhs_lb, rhs_ub, sol);
|
||||
|
||||
if (verbosity_ > 1)
|
||||
{
|
||||
int nnz = 0;
|
||||
for (int i=0; i<sol.Size(); ++i)
|
||||
{
|
||||
if (sol(i) != 0.0)
|
||||
{
|
||||
nnz++;
|
||||
}
|
||||
}
|
||||
|
||||
mfem::out << "Number of nonzeros in NNLSSolver solution: " << nnz
|
||||
<< ", out of " << sol.Size() << endl;
|
||||
|
||||
// Check residual of NNLS solution
|
||||
Vector res(mat->NumRows());
|
||||
mat->Mult(sol, res);
|
||||
res *= row_scaling_;
|
||||
|
||||
const double normGsol = res.Norml2();
|
||||
const double normRHS = rhs_Gw.Norml2();
|
||||
|
||||
res -= rhs_Gw;
|
||||
const double relNorm = res.Norml2() / std::max(normGsol, normRHS);
|
||||
mfem::out << "Relative residual norm for NNLSSolver solution of Gs = Gw: "
|
||||
<< relNorm << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void NNLSSolver::Solve(const Vector& rhs_lb, const Vector& rhs_ub,
|
||||
Vector& soln) const
|
||||
{
|
||||
int m = mat->NumRows();
|
||||
int n = mat->NumCols();
|
||||
|
||||
MFEM_VERIFY(rhs_lb.Size() == m && rhs_lb.Size() == m && soln.Size() == n, "");
|
||||
MFEM_VERIFY(n >= m, "NNLSSolver system cannot be over-determined.");
|
||||
|
||||
if (max_nnz_ == 0)
|
||||
{
|
||||
max_nnz_ = mat->NumCols();
|
||||
}
|
||||
|
||||
// Prepare right hand side
|
||||
Vector rhs_avg(rhs_ub);
|
||||
rhs_avg += rhs_lb;
|
||||
rhs_avg *= 0.5;
|
||||
|
||||
Vector rhs_halfgap(rhs_ub);
|
||||
rhs_halfgap -= rhs_lb;
|
||||
rhs_halfgap *= 0.5;
|
||||
|
||||
Vector rhs_avg_glob(rhs_avg);
|
||||
Vector rhs_halfgap_glob(rhs_halfgap);
|
||||
|
||||
int ione = 1;
|
||||
double fone = 1.0;
|
||||
|
||||
char lside = 'L';
|
||||
char trans = 'T';
|
||||
char notrans = 'N';
|
||||
|
||||
std::vector<unsigned int> nz_ind(m);
|
||||
Vector res_glob(m);
|
||||
Vector mu(n);
|
||||
Vector mu2(n);
|
||||
int n_nz_ind = 0;
|
||||
int n_glob = 0;
|
||||
int m_update;
|
||||
int min_nnz_cap = std::min(static_cast<int>(min_nnz_), std::min(m,n));
|
||||
int info;
|
||||
std::vector<double> l2_res_hist;
|
||||
std::vector<unsigned int> stalled_indices;
|
||||
int stalledFlag = 0;
|
||||
int num_stalled = 0;
|
||||
int nz_ind_zero = 0;
|
||||
|
||||
Vector soln_nz_glob(m);
|
||||
Vector soln_nz_glob_up(m);
|
||||
|
||||
// The following matrices are stored in column-major format as Vectors
|
||||
Vector mat_0_data(m * n);
|
||||
Vector mat_qr_data(m * n);
|
||||
Vector submat_data(m * n);
|
||||
|
||||
Vector tau(n);
|
||||
Vector sub_tau = tau;
|
||||
Vector vec1(m);
|
||||
|
||||
// Temporary work arrays
|
||||
int lwork;
|
||||
std::vector<double> work;
|
||||
int n_outer_iter = 0;
|
||||
int n_total_inner_iter = 0;
|
||||
int i_qr_start;
|
||||
int n_update;
|
||||
// 0 = converged; 1 = maximum iterations reached;
|
||||
// 2 = NNLS stalled (no change in residual for many iterations)
|
||||
int exit_flag = 1;
|
||||
|
||||
res_glob = rhs_avg_glob;
|
||||
Vector qt_rhs_glob = rhs_avg_glob;
|
||||
Vector qqt_rhs_glob = qt_rhs_glob;
|
||||
Vector sub_qt = rhs_avg_glob;
|
||||
|
||||
// Compute threshold tolerance for the Lagrange multiplier mu
|
||||
double mu_tol = 0.0;
|
||||
|
||||
{
|
||||
Vector rhs_scaled(rhs_halfgap_glob);
|
||||
Vector tmp(n);
|
||||
rhs_scaled *= row_scaling_;
|
||||
mat->MultTranspose(rhs_scaled, tmp);
|
||||
|
||||
mu_tol = 1.0e-15 * tmp.Max();
|
||||
}
|
||||
|
||||
double rmax = 0.0;
|
||||
double mumax = 0.0;
|
||||
|
||||
for (int oiter = 0; oiter < n_outer_; ++oiter)
|
||||
{
|
||||
stalledFlag = 0;
|
||||
|
||||
rmax = fabs(res_glob(0)) - rhs_halfgap_glob(0);
|
||||
for (int i=1; i<m; ++i)
|
||||
{
|
||||
rmax = std::max(rmax, fabs(res_glob(i)) - rhs_halfgap_glob(i));
|
||||
}
|
||||
|
||||
l2_res_hist.push_back(res_glob.Norml2());
|
||||
|
||||
if (verbosity_ > 1)
|
||||
{
|
||||
mfem::out << "NNLS " << oiter << " " << n_total_inner_iter << " " << m
|
||||
<< " " << n << " " << n_glob << " " << rmax << " "
|
||||
<< l2_res_hist[oiter] << endl;
|
||||
}
|
||||
if (rmax <= const_tol_ && n_glob >= min_nnz_cap)
|
||||
{
|
||||
if (verbosity_ > 1)
|
||||
{
|
||||
mfem::out << "NNLS target tolerance met" << endl;
|
||||
}
|
||||
exit_flag = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (n_glob >= max_nnz_)
|
||||
{
|
||||
if (verbosity_ > 1)
|
||||
{
|
||||
mfem::out << "NNLS target nnz met" << endl;
|
||||
}
|
||||
exit_flag = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (n_glob >= m)
|
||||
{
|
||||
if (verbosity_ > 1)
|
||||
{
|
||||
mfem::out << "NNLS system is square... exiting" << endl;
|
||||
}
|
||||
exit_flag = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for stall after the first nStallCheck iterations
|
||||
if (oiter > nStallCheck_)
|
||||
{
|
||||
double mean0 = 0.0;
|
||||
double mean1 = 0.0;
|
||||
for (int i=0; i<nStallCheck_/2; ++i)
|
||||
{
|
||||
mean0 += l2_res_hist[oiter - i];
|
||||
mean1 += l2_res_hist[oiter - (nStallCheck_) - i];
|
||||
}
|
||||
|
||||
double mean_res_change = (mean1 / mean0) - 1.0;
|
||||
if (std::abs(mean_res_change) < res_change_termination_tol_)
|
||||
{
|
||||
if (verbosity_ > 1)
|
||||
{
|
||||
mfem::out << "NNLSSolver stall detected... exiting" << endl;
|
||||
}
|
||||
exit_flag = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the next index
|
||||
res_glob *= row_scaling_;
|
||||
mat->MultTranspose(res_glob, mu);
|
||||
|
||||
for (int i = 0; i < n_nz_ind; ++i)
|
||||
{
|
||||
mu(nz_ind[i]) = 0.0;
|
||||
}
|
||||
for (unsigned int i = 0; i < stalled_indices.size(); ++i)
|
||||
{
|
||||
mu(stalled_indices[i]) = 0.0;
|
||||
}
|
||||
|
||||
mumax = mu.Max();
|
||||
|
||||
if (mumax < mu_tol)
|
||||
{
|
||||
num_stalled = stalled_indices.size();
|
||||
if (num_stalled > 0)
|
||||
{
|
||||
if (verbosity_ > 0)
|
||||
{
|
||||
mfem::out << "NNLS Lagrange multiplier is below the minimum "
|
||||
<< "threshold: mumax = " << mumax << ", mutol = "
|
||||
<< mu_tol << "\n" << " Resetting stalled indices "
|
||||
<< "vector of size " << num_stalled << "\n";
|
||||
}
|
||||
stalled_indices.resize(0);
|
||||
|
||||
mat->MultTranspose(res_glob, mu);
|
||||
|
||||
for (int i = 0; i < n_nz_ind; ++i)
|
||||
{
|
||||
mu(nz_ind[i]) = 0.0;
|
||||
}
|
||||
|
||||
mumax = mu.Max();
|
||||
}
|
||||
}
|
||||
|
||||
int imax = 0;
|
||||
{
|
||||
double tmax = mu(0);
|
||||
for (int i=1; i<n; ++i)
|
||||
{
|
||||
if (mu(i) > tmax)
|
||||
{
|
||||
tmax = mu(i);
|
||||
imax = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Record the local value of the next index
|
||||
nz_ind[n_nz_ind] = imax;
|
||||
++n_nz_ind;
|
||||
|
||||
if (verbosity_ > 2)
|
||||
{
|
||||
mfem::out << "Found next index: " << imax << " " << mumax << endl;
|
||||
}
|
||||
|
||||
for (int i=0; i<m; ++i)
|
||||
{
|
||||
mat_0_data(i + (n_glob*m)) = (*mat)(i,imax) * row_scaling_[i];
|
||||
mat_qr_data(i + (n_glob*m)) = mat_0_data(i + (n_glob*m));
|
||||
}
|
||||
|
||||
i_qr_start = n_glob;
|
||||
++n_glob; // Increment the size of the global matrix
|
||||
|
||||
if (verbosity_ > 2)
|
||||
{
|
||||
mfem::out << "Updated matrix with new index" << endl;
|
||||
}
|
||||
|
||||
for (int iiter = 0; iiter < n_inner_; ++iiter)
|
||||
{
|
||||
++n_total_inner_iter;
|
||||
|
||||
// Initialize
|
||||
const bool incremental_update = true;
|
||||
n_update = n_glob - i_qr_start;
|
||||
m_update = m - i_qr_start;
|
||||
if (incremental_update)
|
||||
{
|
||||
// Apply Householder reflectors to compute Q^T new_cols
|
||||
lwork = -1;
|
||||
work.resize(10);
|
||||
|
||||
dormqr_(&lside, &trans, &m, &n_update, &i_qr_start,
|
||||
mat_qr_data.GetData(), &m, tau.GetData(),
|
||||
mat_qr_data.GetData() + (i_qr_start * m), &m,
|
||||
work.data(), &lwork, &info);
|
||||
MFEM_VERIFY(info == 0, ""); // Q^T A update work calculation failed
|
||||
lwork = static_cast<int>(work[0]);
|
||||
work.resize(lwork);
|
||||
dormqr_(&lside, &trans, &m, &n_update, &i_qr_start,
|
||||
mat_qr_data.GetData(), &m, tau.GetData(),
|
||||
mat_qr_data.GetData() + (i_qr_start * m), &m,
|
||||
work.data(), &lwork, &info);
|
||||
MFEM_VERIFY(info == 0, ""); // Q^T A update failed
|
||||
// Compute QR factorization of the submatrix
|
||||
lwork = -1;
|
||||
work.resize(10);
|
||||
|
||||
// Copy m_update-by-n_update submatrix of mat_qr_data,
|
||||
// starting at (i_qr_start, i_qr_start)
|
||||
for (int i=0; i<m_update; ++i)
|
||||
for (int j=0; j<n_update; ++j)
|
||||
{
|
||||
submat_data[i + (j * m_update)] =
|
||||
mat_qr_data[i + i_qr_start + ((j + i_qr_start) * m)];
|
||||
}
|
||||
|
||||
// Copy tau subvector of length n_update, starting at i_qr_start
|
||||
for (int j=0; j<n_update; ++j)
|
||||
{
|
||||
sub_tau[j] = tau[i_qr_start + j];
|
||||
}
|
||||
|
||||
dgeqrf_(&m_update, &n_update,
|
||||
submat_data.GetData(), &m_update, sub_tau.GetData(),
|
||||
work.data(), &lwork, &info);
|
||||
MFEM_VERIFY(info == 0, ""); // QR update factorization work calc
|
||||
lwork = static_cast<int>(work[0]);
|
||||
if (lwork == 0) { lwork = 1; }
|
||||
work.resize(lwork);
|
||||
dgeqrf_(&m_update, &n_update,
|
||||
submat_data.GetData(), &m_update, sub_tau.GetData(),
|
||||
work.data(), &lwork, &info);
|
||||
MFEM_VERIFY(info == 0, ""); // QR update factorization failed
|
||||
|
||||
// Copy result back
|
||||
for (int i=0; i<m_update; ++i)
|
||||
for (int j=0; j<n_update; ++j)
|
||||
{
|
||||
mat_qr_data[i + i_qr_start + ((j + i_qr_start)* m)] =
|
||||
submat_data[i + (j * m_update)];
|
||||
}
|
||||
|
||||
for (int j=0; j<n_update; ++j)
|
||||
{
|
||||
tau[i_qr_start + j] = sub_tau[j];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy everything to mat_qr then do full QR
|
||||
for (int i=0; i<m; ++i)
|
||||
for (int j=0; j<n_glob; ++j)
|
||||
{
|
||||
mat_qr_data(i + (j*m)) = mat_0_data(i + (j*m));
|
||||
}
|
||||
|
||||
// Compute qr factorization (first find the size of work and then
|
||||
// perform qr)
|
||||
lwork = -1;
|
||||
work.resize(10);
|
||||
dgeqrf_(&m, &n_glob,
|
||||
mat_qr_data.GetData(), &m, tau.GetData(),
|
||||
work.data(), &lwork, &info);
|
||||
MFEM_VERIFY(info == 0, ""); // QR factorization work calculation
|
||||
lwork = static_cast<int>(work[0]);
|
||||
work.resize(lwork);
|
||||
dgeqrf_(&m, &n_glob,
|
||||
mat_qr_data.GetData(), &m, tau.GetData(),
|
||||
work.data(), &lwork, &info);
|
||||
MFEM_VERIFY(info == 0, ""); // QR factorization failed
|
||||
}
|
||||
|
||||
if (verbosity_ > 2)
|
||||
{
|
||||
mfem::out << "Updated QR " << iiter << endl;
|
||||
}
|
||||
|
||||
// Apply Householder reflectors to compute Q^T b
|
||||
if (incremental_update && iiter == 0)
|
||||
{
|
||||
lwork = -1;
|
||||
work.resize(10);
|
||||
|
||||
// Copy submatrix of mat_qr_data starting at
|
||||
// (i_qr_start, i_qr_start), of size m_update-by-1
|
||||
// Copy submatrix of qt_rhs_glob starting at (i_qr_start, 0),
|
||||
// of size m_update-by-1
|
||||
|
||||
for (int i=0; i<m_update; ++i)
|
||||
{
|
||||
submat_data[i] = mat_qr_data[i + i_qr_start + (i_qr_start * m)];
|
||||
sub_qt[i] = qt_rhs_glob[i + i_qr_start];
|
||||
}
|
||||
|
||||
sub_tau[0] = tau[i_qr_start];
|
||||
|
||||
dormqr_(&lside, &trans, &m_update, &ione, &ione,
|
||||
submat_data.GetData(), &m_update, sub_tau.GetData(),
|
||||
sub_qt.GetData(), &m_update,
|
||||
work.data(), &lwork, &info);
|
||||
MFEM_VERIFY(info == 0, ""); // H_last y work calculation failed
|
||||
lwork = static_cast<int>(work[0]);
|
||||
work.resize(lwork);
|
||||
dormqr_(&lside, &trans, &m_update, &ione, &ione,
|
||||
submat_data.GetData(), &m_update, sub_tau.GetData(),
|
||||
sub_qt.GetData(), &m_update,
|
||||
work.data(), &lwork, &info);
|
||||
MFEM_VERIFY(info == 0, ""); // H_last y failed
|
||||
// Copy result back
|
||||
for (int i=0; i<m_update; ++i)
|
||||
{
|
||||
qt_rhs_glob[i + i_qr_start] = sub_qt[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compute Q^T b from scratch
|
||||
qt_rhs_glob = rhs_avg_glob;
|
||||
lwork = -1;
|
||||
work.resize(10);
|
||||
dormqr_(&lside, &trans, &m, &ione, &n_glob,
|
||||
mat_qr_data.GetData(), &m, tau.GetData(),
|
||||
qt_rhs_glob.GetData(), &m,
|
||||
work.data(), &lwork, &info);
|
||||
MFEM_VERIFY(info == 0, ""); // Q^T b work calculation failed
|
||||
lwork = static_cast<int>(work[0]);
|
||||
work.resize(lwork);
|
||||
dormqr_(&lside, &trans, &m, &ione, &n_glob,
|
||||
mat_qr_data.GetData(), &m, tau.GetData(),
|
||||
qt_rhs_glob.GetData(), &m,
|
||||
work.data(), &lwork, &info);
|
||||
MFEM_VERIFY(info == 0, ""); // Q^T b failed
|
||||
}
|
||||
|
||||
if (verbosity_ > 2)
|
||||
{
|
||||
mfem::out << "Updated rhs " << iiter << endl;
|
||||
}
|
||||
|
||||
// Apply R^{-1}; first n_glob entries of vec1 are overwritten
|
||||
char upper = 'U';
|
||||
char nounit = 'N';
|
||||
vec1 = qt_rhs_glob;
|
||||
dtrsm_(&lside, &upper, ¬rans, &nounit,
|
||||
&n_glob, &ione, &fone,
|
||||
mat_qr_data.GetData(), &m,
|
||||
vec1.GetData(), &n_glob);
|
||||
|
||||
if (verbosity_ > 2)
|
||||
{
|
||||
mfem::out << "Solved triangular system " << iiter << endl;
|
||||
}
|
||||
|
||||
// Check if all entries are positive
|
||||
int pos_ibool = 0;
|
||||
double smin = n_glob > 0 ? vec1(0) : 0.0;
|
||||
for (int i=0; i<n_glob; ++i)
|
||||
{
|
||||
soln_nz_glob_up(i) = vec1(i);
|
||||
smin = std::min(smin, soln_nz_glob_up(i));
|
||||
}
|
||||
|
||||
if (smin > zero_tol_)
|
||||
{
|
||||
pos_ibool = 1;
|
||||
for (int i=0; i<n_glob; ++i)
|
||||
{
|
||||
soln_nz_glob(i) = soln_nz_glob_up(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (pos_ibool == 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (verbosity_ > 2)
|
||||
{
|
||||
mfem::out << "Start pruning " << iiter << endl;
|
||||
for (int i = 0; i < n_glob; ++i)
|
||||
{
|
||||
if (soln_nz_glob_up(i) <= zero_tol_)
|
||||
{
|
||||
mfem::out << i << " " << n_glob << " " << soln_nz_glob_up(i) << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (soln_nz_glob_up(n_glob - 1) <= zero_tol_)
|
||||
{
|
||||
stalledFlag = 1;
|
||||
if (verbosity_ > 2)
|
||||
{
|
||||
if (qr_residual_mode_ == QRresidualMode::hybrid)
|
||||
{
|
||||
mfem::out << "Detected stall due to adding and removing same "
|
||||
<< "column. Switching to QR residual calculation "
|
||||
<< "method." << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem::out << "Detected stall due to adding and removing same"
|
||||
<< " column. Exiting now." << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stalledFlag == 1 && qr_residual_mode_ == QRresidualMode::hybrid)
|
||||
{
|
||||
NNLS_qrres_on_ = true;
|
||||
break;
|
||||
}
|
||||
|
||||
double alpha = 1.0e300;
|
||||
// Find maximum permissible step
|
||||
for (int i = 0; i < n_glob; ++i)
|
||||
{
|
||||
if (soln_nz_glob_up(i) <= zero_tol_)
|
||||
{
|
||||
alpha = std::min(alpha, soln_nz_glob(i)/(soln_nz_glob(i) - soln_nz_glob_up(i)));
|
||||
}
|
||||
}
|
||||
// Update solution
|
||||
smin = 0.0;
|
||||
for (int i = 0; i < n_glob; ++i)
|
||||
{
|
||||
soln_nz_glob(i) += alpha*(soln_nz_glob_up(i) - soln_nz_glob(i));
|
||||
if (i == 0 || soln_nz_glob(i) < smin)
|
||||
{
|
||||
smin = soln_nz_glob(i);
|
||||
}
|
||||
}
|
||||
|
||||
while (smin > zero_tol_)
|
||||
{
|
||||
// This means there was a rounding error, as we should have
|
||||
// a zero element by definition. Recalculate alpha based on
|
||||
// the index that corresponds to the element that should be
|
||||
// zero.
|
||||
|
||||
int index_min = 0;
|
||||
smin = soln_nz_glob(0);
|
||||
for (int i = 1; i < n_glob; ++i)
|
||||
{
|
||||
if (soln_nz_glob(i) < smin)
|
||||
{
|
||||
smin = soln_nz_glob(i);
|
||||
index_min = i;
|
||||
}
|
||||
}
|
||||
|
||||
alpha = soln_nz_glob(index_min)/(soln_nz_glob(index_min)
|
||||
- soln_nz_glob_up(index_min));
|
||||
|
||||
// Reupdate solution
|
||||
for (int i = 0; i < n_glob; ++i)
|
||||
{
|
||||
soln_nz_glob(i) += alpha*(soln_nz_glob_up(i) - soln_nz_glob(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up zeroed entry
|
||||
i_qr_start = n_glob+1;
|
||||
while (true)
|
||||
{
|
||||
// Check if there is a zero entry
|
||||
int zero_ibool;
|
||||
|
||||
smin = n_glob > 0 ? soln_nz_glob(0) : 0.0;
|
||||
for (int i=1; i<n_glob; ++i)
|
||||
{
|
||||
smin = std::min(smin, soln_nz_glob(i));
|
||||
}
|
||||
|
||||
if (smin < zero_tol_)
|
||||
{
|
||||
zero_ibool = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
zero_ibool = 0;
|
||||
}
|
||||
|
||||
if (zero_ibool == 0) // Break if there is no more zero entry
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int ind_zero = -1; // Index where the first zero is encountered
|
||||
nz_ind_zero = 0;
|
||||
|
||||
// Identify global index of the zeroed element
|
||||
for (int i = 0; i < n_glob; ++i)
|
||||
{
|
||||
if (soln_nz_glob(i) < zero_tol_)
|
||||
{
|
||||
ind_zero = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
MFEM_VERIFY(ind_zero != -1, "");
|
||||
// Identify the local index for nz_ind to which the zeroed entry
|
||||
// belongs
|
||||
for (int i = 0; i < ind_zero; ++i)
|
||||
{
|
||||
++nz_ind_zero;
|
||||
}
|
||||
|
||||
{
|
||||
// Copy mat_0.cols[ind_zero+1,n_glob) to mat_qr.cols[ind_zero,n_glob-1)
|
||||
for (int i=0; i<m; ++i)
|
||||
for (int j=ind_zero; j<n_glob-1; ++j)
|
||||
{
|
||||
mat_qr_data(i + (j*m)) = mat_0_data(i + ((j+1)*m));
|
||||
}
|
||||
|
||||
// Copy mat_qr.cols[ind_zero,n_glob-1) to
|
||||
// mat_0.cols[ind_zero,n_glob-1)
|
||||
for (int i=0; i<m; ++i)
|
||||
for (int j=ind_zero; j<n_glob-1; ++j)
|
||||
{
|
||||
mat_0_data(i + (j*m)) = mat_qr_data(i + (j*m));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the zeroed entry from the local matrix index
|
||||
for (int i = nz_ind_zero; i < n_nz_ind-1; ++i)
|
||||
{
|
||||
nz_ind[i] = nz_ind[i+1];
|
||||
}
|
||||
--n_nz_ind;
|
||||
|
||||
// Shift soln_nz_glob and proc_index
|
||||
for (int i = ind_zero; i < n_glob-1; ++i)
|
||||
{
|
||||
soln_nz_glob(i) = soln_nz_glob(i+1);
|
||||
}
|
||||
|
||||
i_qr_start = std::min(i_qr_start, ind_zero);
|
||||
--n_glob;
|
||||
} // End of pruning loop
|
||||
|
||||
if (verbosity_ > 2)
|
||||
{
|
||||
mfem::out << "Finished pruning " << iiter << endl;
|
||||
}
|
||||
} // End of inner loop
|
||||
|
||||
// Check if we have stalled
|
||||
if (stalledFlag == 1)
|
||||
{
|
||||
--n_glob;
|
||||
--n_nz_ind;
|
||||
num_stalled = stalled_indices.size();
|
||||
stalled_indices.resize(num_stalled + 1);
|
||||
stalled_indices[num_stalled] = imax;
|
||||
if (verbosity_ > 2)
|
||||
{
|
||||
mfem::out << "Adding index " << imax << " to stalled index list "
|
||||
<< "of size " << num_stalled << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute residual
|
||||
if (!NNLS_qrres_on_)
|
||||
{
|
||||
res_glob = rhs_avg_glob;
|
||||
double fmone = -1.0;
|
||||
dgemv_(¬rans, &m, &n_glob, &fmone,
|
||||
mat_0_data.GetData(), &m,
|
||||
soln_nz_glob.GetData(), &ione, &fone,
|
||||
res_glob.GetData(), &ione);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compute residual using res = b - Q*Q^T*b, where Q is from an
|
||||
// economical QR decomposition
|
||||
lwork = -1;
|
||||
work.resize(10);
|
||||
qqt_rhs_glob = 0.0;
|
||||
for (int i=0; i<n_glob; ++i)
|
||||
{
|
||||
qqt_rhs_glob(i) = qt_rhs_glob(i);
|
||||
}
|
||||
|
||||
dormqr_(&lside, ¬rans, &m, &ione, &n_glob, mat_qr_data.GetData(), &m,
|
||||
tau.GetData(), qqt_rhs_glob.GetData(), &m,
|
||||
work.data(), &lwork, &info);
|
||||
|
||||
MFEM_VERIFY(info == 0, ""); // Q Q^T b work calculation failed.
|
||||
lwork = static_cast<int>(work[0]);
|
||||
work.resize(lwork);
|
||||
dormqr_(&lside, ¬rans, &m, &ione, &n_glob, mat_qr_data.GetData(), &m,
|
||||
tau.GetData(), qqt_rhs_glob.GetData(), &m,
|
||||
work.data(), &lwork, &info);
|
||||
MFEM_VERIFY(info == 0, ""); // Q Q^T b calculation failed.
|
||||
res_glob = rhs_avg_glob;
|
||||
res_glob -= qqt_rhs_glob;
|
||||
}
|
||||
|
||||
if (verbosity_ > 2)
|
||||
{
|
||||
mfem::out << "Computed residual" << endl;
|
||||
}
|
||||
|
||||
++n_outer_iter;
|
||||
} // End of outer loop
|
||||
|
||||
// Insert the solutions
|
||||
MFEM_VERIFY(n_glob == n_nz_ind, "");
|
||||
soln = 0.0;
|
||||
for (int i = 0; i < n_glob; ++i)
|
||||
{
|
||||
soln(nz_ind[i]) = soln_nz_glob(i);
|
||||
}
|
||||
|
||||
if (verbosity_ > 0)
|
||||
{
|
||||
mfem::out << "NNLS solver: m = " << m << ", n = " << n
|
||||
<< ", outer_iter = " << n_outer_iter << ", inner_iter = "
|
||||
<< n_total_inner_iter;
|
||||
|
||||
if (exit_flag == 0)
|
||||
{
|
||||
mfem::out << ": converged" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem::out << endl << "Warning, NNLS convergence stalled: "
|
||||
<< (exit_flag == 2) << endl;
|
||||
mfem::out << "resErr = " << rmax << " vs tol = " << const_tol_
|
||||
<< "; mumax = " << mumax << " vs tol = " << mu_tol << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // MFEM_USE_LAPACK
|
||||
|
||||
}
|
||||
|
||||
+121
-1
@@ -432,7 +432,7 @@ public:
|
||||
|
||||
~OperatorChebyshevSmoother() {}
|
||||
|
||||
void Mult(const Vector&x, Vector &y) const;
|
||||
void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
void MultTranspose(const Vector &x, Vector &y) const { Mult(x, y); }
|
||||
|
||||
@@ -1245,6 +1245,126 @@ public:
|
||||
};
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
/** Non-negative least squares (NNLS) solver class, for computing a vector
|
||||
with non-negative entries approximately satisfying an under-determined
|
||||
linear system. */
|
||||
class NNLSSolver : public Solver
|
||||
{
|
||||
public:
|
||||
NNLSSolver();
|
||||
|
||||
~NNLSSolver() { }
|
||||
|
||||
/// The operator must be a DenseMatrix.
|
||||
void SetOperator(const Operator &op) override;
|
||||
|
||||
void Mult(const Vector &w, Vector &sol) const override;
|
||||
|
||||
/**
|
||||
* Set verbosity. If set to 0: print nothing; if 1: just print results;
|
||||
* if 2: print short update on every iteration; if 3: print longer update
|
||||
* each iteration.
|
||||
*/
|
||||
void SetVerbosity(int v) { verbosity_ = v; }
|
||||
|
||||
void SetTolerance(double tol) { const_tol_ = tol; }
|
||||
|
||||
/// Set the minimum number of nonzeros required for the solution.
|
||||
void SetMinNNZ(int min_nnz) { min_nnz_ = min_nnz; }
|
||||
|
||||
/// Set the maximum number of nonzeros required for the solution, as an early
|
||||
/// termination condition.
|
||||
void SetMaxNNZ(int max_nnz) { max_nnz_ = max_nnz; }
|
||||
|
||||
/// Set threshold on relative change in residual over nStallCheck_ iterations.
|
||||
void SetResidualChangeTolerance(double tol)
|
||||
{ res_change_termination_tol_ = tol; }
|
||||
|
||||
void SetZeroTolerance(double tol) { zero_tol_ = tol; }
|
||||
|
||||
/// Set RHS vector constant shift, defining rhs_lb and rhs_ub in Solve().
|
||||
void SetRHSDelta(double d) { rhs_delta_ = d; }
|
||||
|
||||
/// Set the maximum number of outer iterations in Solve().
|
||||
void SetOuterIterations(int n) { n_outer_ = n; }
|
||||
|
||||
/// Set the maximum number of inner iterations in Solve().
|
||||
void SetInnerIterations(int n) { n_inner_ = n; }
|
||||
|
||||
/// Set the number of iterations to use for stall checking.
|
||||
void SetStallCheck(int n) { nStallCheck_ = n; }
|
||||
|
||||
/// Set a flag to determine whether to call NormalizeConstraints().
|
||||
void SetNormalize(bool n) { normalize_ = n; }
|
||||
|
||||
/**
|
||||
* Enumerated types of QRresidual mode. Options are 'off': the residual is
|
||||
* calculated normally, 'on': the residual is calculated using the QR
|
||||
* method, 'hybrid': the residual is calculated normally until we experience
|
||||
* rounding errors, then the QR method is used. The default is 'hybrid',
|
||||
* which should see the best performance. Recommend using 'hybrid' or 'off'
|
||||
* only, since 'on' is computationally expensive.
|
||||
*/
|
||||
enum class QRresidualMode {off, on, hybrid};
|
||||
|
||||
/**
|
||||
* Set the residual calculation mode for the NNLS solver. See QRresidualMode
|
||||
* enum above for details.
|
||||
*/
|
||||
void SetQRResidualMode(const QRresidualMode qr_residual_mode);
|
||||
|
||||
/**
|
||||
* @brief Solve the NNLS problem. Specifically, we find a vector @a soln,
|
||||
* such that rhs_lb < mat*soln < rhs_ub is satisfied, where mat is the
|
||||
* DenseMatrix input to SetOperator().
|
||||
*
|
||||
* The method by which we find the solution is the active-set method
|
||||
* developed by Lawson and Hanson (1974) using lapack. To decrease rounding
|
||||
* errors in the case of very tight tolerances, we have the option to compute
|
||||
* the residual using the QR factorization of A, by res = b - Q*Q^T*b. This
|
||||
* residual calculation results in less rounding error, but is more
|
||||
* computationally expensive. To select whether to use the QR residual method
|
||||
* or not, see set_qrresidual_mode above.
|
||||
*/
|
||||
void Solve(const Vector& rhs_lb, const Vector& rhs_ub, Vector& soln) const;
|
||||
|
||||
/**
|
||||
* Normalize the constraints such that the tolerances for each constraint
|
||||
* (i.e. (UB - LB)/2) are equal. This seems to help the performance in most
|
||||
* cases.
|
||||
*/
|
||||
void NormalizeConstraints(Vector& rhs_lb, Vector& rhs_ub) const;
|
||||
|
||||
private:
|
||||
const DenseMatrix *mat;
|
||||
|
||||
double const_tol_;
|
||||
int min_nnz_; // minimum number of nonzero entries
|
||||
mutable int max_nnz_; // maximum number of nonzero entries
|
||||
int verbosity_;
|
||||
|
||||
/**
|
||||
* @brief Threshold on relative change in residual over nStallCheck_
|
||||
* iterations, for stall sensing.
|
||||
*/
|
||||
double res_change_termination_tol_;
|
||||
|
||||
double zero_tol_;
|
||||
double rhs_delta_;
|
||||
int n_outer_;
|
||||
int n_inner_;
|
||||
int nStallCheck_;
|
||||
|
||||
bool normalize_;
|
||||
|
||||
mutable bool NNLS_qrres_on_;
|
||||
QRresidualMode qr_residual_mode_;
|
||||
|
||||
mutable Vector row_scaling_;
|
||||
};
|
||||
#endif // MFEM_USE_LAPACK
|
||||
|
||||
}
|
||||
|
||||
#endif // MFEM_SOLVERS
|
||||
|
||||
@@ -1345,7 +1345,7 @@ void SparseMatrix::Finalize(int skip_zeros, bool fix_empty_rows)
|
||||
if ((i-1) != aux->Column) { continue; }
|
||||
|
||||
bool found = false;
|
||||
double found_val;
|
||||
double found_val = 0.0; // init to suppress gcc warning
|
||||
for (RowNode *other = Rows[aux->Column]; other != NULL; other = other->Prev)
|
||||
{
|
||||
if (other->Column == (i-1))
|
||||
@@ -1381,7 +1381,7 @@ void SparseMatrix::Finalize(int skip_zeros, bool fix_empty_rows)
|
||||
if (i != aux->Column) { continue; }
|
||||
|
||||
bool found = false;
|
||||
double found_val;
|
||||
double found_val = 0.0; // init to suppress gcc warning
|
||||
for (RowNode *other = Rows[aux->Column]; other != NULL; other = other->Prev)
|
||||
{
|
||||
if (other->Column == i)
|
||||
|
||||
@@ -125,7 +125,7 @@ EXAMPLE_TEST_DIRS := examples
|
||||
|
||||
MINIAPP_SUBDIRS = common electromagnetics meshing navier performance tools \
|
||||
toys nurbs gslib adjoint solvers shifted mtop parelag autodiff hooke \
|
||||
multidomain dpg hdiv-linear-solver spde contact
|
||||
multidomain dpg hdiv-linear-solver spde
|
||||
MINIAPP_DIRS := $(addprefix miniapps/,$(MINIAPP_SUBDIRS))
|
||||
MINIAPP_TEST_DIRS := $(filter-out %/common,$(MINIAPP_DIRS))
|
||||
MINIAPP_USE_COMMON := $(addprefix miniapps/,electromagnetics meshing tools \
|
||||
|
||||
+248
-12
@@ -1444,7 +1444,7 @@ Element::Type Mesh::GetFaceElementType(int Face) const
|
||||
|
||||
Array<int> Mesh::GetFaceToBdrElMap() const
|
||||
{
|
||||
Array<int> face_to_be(NumOfFaces);
|
||||
Array<int> face_to_be(GetNumFaces());
|
||||
face_to_be = -1;
|
||||
for (int i = 0; i < NumOfBdrElements; i++)
|
||||
{
|
||||
@@ -3752,8 +3752,8 @@ Mesh& Mesh::operator=(Mesh &&mesh)
|
||||
return *this;
|
||||
}
|
||||
|
||||
Mesh Mesh::LoadFromFile(const char *filename, int generate_edges, int refine,
|
||||
bool fix_orientation)
|
||||
Mesh Mesh::LoadFromFile(const std::string &filename, int generate_edges,
|
||||
int refine, bool fix_orientation)
|
||||
{
|
||||
Mesh mesh;
|
||||
named_ifgzstream imesh(filename);
|
||||
@@ -3807,7 +3807,7 @@ Mesh Mesh::MakeRefined(Mesh &orig_mesh, const Array<int> &ref_factors,
|
||||
return mesh;
|
||||
}
|
||||
|
||||
Mesh::Mesh(const char *filename, int generate_edges, int refine,
|
||||
Mesh::Mesh(const std::string &filename, int generate_edges, int refine,
|
||||
bool fix_orientation)
|
||||
{
|
||||
// Initialization as in the default constructor
|
||||
@@ -5107,6 +5107,43 @@ std::vector<int> Mesh::CreatePeriodicVertexMapping(
|
||||
return v2v;
|
||||
}
|
||||
|
||||
void Mesh::RefineNURBSFromFile(std::string ref_file)
|
||||
{
|
||||
MFEM_VERIFY(NURBSext,"Mesh::RefineNURBSFromFile: Not a NURBS mesh!");
|
||||
mfem::out<<"Refining NURBS from refinement file: "<<ref_file<<endl;
|
||||
|
||||
int nkv;
|
||||
ifstream input(ref_file);
|
||||
input >> nkv;
|
||||
|
||||
// Check if the number of knot vectors in the refinement file and mesh match
|
||||
if ( nkv != NURBSext->GetNKV())
|
||||
{
|
||||
mfem::out<<endl;
|
||||
mfem::out<<"Knot vectors in ref_file: "<<nkv<<endl;
|
||||
mfem::out<<"Knot vectors in NURBSExt: "<<NURBSext->GetNKV()<<endl;
|
||||
MFEM_ABORT("Refine file does not have the correct number of knot vectors");
|
||||
}
|
||||
|
||||
// Read knot vectors from file
|
||||
Array<Vector *> knotVec(nkv);
|
||||
for (int kv = 0; kv < nkv; kv++)
|
||||
{
|
||||
knotVec[kv] = new Vector();
|
||||
knotVec[kv]-> Load(input);
|
||||
}
|
||||
input.close();
|
||||
|
||||
// Insert knots
|
||||
KnotInsert(knotVec);
|
||||
|
||||
// Delete knots
|
||||
for (int kv = 0; kv < nkv; kv++)
|
||||
{
|
||||
delete knotVec[kv];
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::KnotInsert(Array<KnotVector *> &kv)
|
||||
{
|
||||
if (NURBSext == NULL)
|
||||
@@ -5279,17 +5316,24 @@ void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_knot)
|
||||
|
||||
input >> ident; // 'edges'
|
||||
input >> NumOfEdges;
|
||||
edge_vertex = new Table(NumOfEdges, 2);
|
||||
edge_to_knot.SetSize(NumOfEdges);
|
||||
for (int j = 0; j < NumOfEdges; j++)
|
||||
if (NumOfEdges > 0)
|
||||
{
|
||||
int *v = edge_vertex->GetRow(j);
|
||||
input >> edge_to_knot[j] >> v[0] >> v[1];
|
||||
if (v[0] > v[1])
|
||||
edge_vertex = new Table(NumOfEdges, 2);
|
||||
edge_to_knot.SetSize(NumOfEdges);
|
||||
for (int j = 0; j < NumOfEdges; j++)
|
||||
{
|
||||
edge_to_knot[j] = -1 - edge_to_knot[j];
|
||||
int *v = edge_vertex->GetRow(j);
|
||||
input >> edge_to_knot[j] >> v[0] >> v[1];
|
||||
if (v[0] > v[1])
|
||||
{
|
||||
edge_to_knot[j] = -1 - edge_to_knot[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
edge_to_knot.SetSize(0);
|
||||
}
|
||||
|
||||
skip_comment_lines(input, '#');
|
||||
|
||||
@@ -5299,6 +5343,198 @@ void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_knot)
|
||||
|
||||
FinalizeTopology();
|
||||
CheckBdrElementOrientation(); // check and fix boundary element orientation
|
||||
|
||||
/* Generate knot 2 edge mapping -- if edges are not specified in the mesh file
|
||||
See data/two-squares-nurbs-autoedge.mesh for an example */
|
||||
if (edge_to_knot.Size() == 0)
|
||||
{
|
||||
edge_vertex = new Table(NumOfEdges, 2);
|
||||
edge_to_knot.SetSize(NumOfEdges);
|
||||
constexpr int notset = -9999999;
|
||||
edge_to_knot = notset;
|
||||
Array<int> edges;
|
||||
Array<int> oedge;
|
||||
int knot = 0;
|
||||
|
||||
Array<int> edge0, edge1;
|
||||
int flip = 1;
|
||||
if (Dimension() == 2 )
|
||||
{
|
||||
edge0.SetSize(2);
|
||||
edge1.SetSize(2);
|
||||
|
||||
edge0[0] = 0; edge1[0] = 2;
|
||||
edge0[1] = 1; edge1[1] = 3;
|
||||
flip = 1;
|
||||
}
|
||||
else if (Dimension() == 3 )
|
||||
{
|
||||
edge0.SetSize(9);
|
||||
edge1.SetSize(9);
|
||||
|
||||
edge0[0] = 0; edge1[0] = 2;
|
||||
edge0[1] = 0; edge1[1] = 4;
|
||||
edge0[2] = 0; edge1[2] = 6;
|
||||
|
||||
edge0[3] = 1; edge1[3] = 3;
|
||||
edge0[4] = 1; edge1[4] = 5;
|
||||
edge0[5] = 1; edge1[5] = 7;
|
||||
|
||||
edge0[6] = 8; edge1[6] = 9;
|
||||
edge0[7] = 8; edge1[7] = 10;
|
||||
edge0[8] = 8; edge1[8] = 11;
|
||||
flip = -1;
|
||||
}
|
||||
|
||||
/* Initial assignment of knots to edges. This is an algorithm that loops over the
|
||||
patches and assigns knot vectors to edges. It starts with assigning knot vector 0
|
||||
and 1 to the edges of the first patch. Then it uses: 1) patches can share edges
|
||||
2) knot vectors on opposing edges in a patch are equal, to create edge_to_knot */
|
||||
int e0, e1, v0, v1, df;
|
||||
int p,j,k;
|
||||
for (p = 0; p < GetNE(); p++)
|
||||
{
|
||||
GetElementEdges(p, edges, oedge);
|
||||
|
||||
const int *v = elements[p]->GetVertices();
|
||||
for (j = 0; j < edges.Size(); j++)
|
||||
{
|
||||
int *vv = edge_vertex->GetRow(edges[j]);
|
||||
const int *e = elements[p]->GetEdgeVertices(j);
|
||||
if (oedge[j] == 1)
|
||||
{
|
||||
vv[0] = v[e[0]];
|
||||
vv[1] = v[e[1]];
|
||||
}
|
||||
else
|
||||
{
|
||||
vv[0] = v[e[1]];
|
||||
vv[1] = v[e[0]];
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < edge1.Size(); j++)
|
||||
{
|
||||
e0 = edges[edge0[j]];
|
||||
e1 = edges[edge1[j]];
|
||||
v0 = edge_to_knot[e0];
|
||||
v1 = edge_to_knot[e1];
|
||||
df = flip*oedge[edge0[j]]*oedge[edge1[j]];
|
||||
|
||||
// Case 1: knot vector is not set
|
||||
if ((v0 == notset) && (v1 == notset))
|
||||
{
|
||||
edge_to_knot[e0] = knot;
|
||||
edge_to_knot[e1] = knot;
|
||||
knot++;
|
||||
}
|
||||
// Case 2 & 3: knot vector on one of the two edges
|
||||
// is set earlier (in another patch). We just have
|
||||
// to copy it for the opposing edge.
|
||||
else if ((v0 != notset) && (v1 == notset))
|
||||
{
|
||||
edge_to_knot[e1] = (df >= 0 ? -v0-1 : v0);
|
||||
}
|
||||
else if ((v0 == notset) && (v1 != notset))
|
||||
{
|
||||
edge_to_knot[e0] = (df >= 0 ? -v1-1 : v1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify correct assignment, make sure that corresponding edges
|
||||
within patch point to same knot vector. If not assign the lowest number.
|
||||
|
||||
We bound the while by GetNE() + 1 as this is probably the most unlucky
|
||||
case. +1 to finish without corrections. Note that this is a check and
|
||||
in general the initial assignment is correct. Then the while is performed
|
||||
only once. Only on very tricky meshes it might need corrections.*/
|
||||
int corrections;
|
||||
int passes = 0;
|
||||
do
|
||||
{
|
||||
corrections = 0;
|
||||
for (p = 0; p < GetNE(); p++)
|
||||
{
|
||||
GetElementEdges(p, edges, oedge);
|
||||
for (j = 0; j < edge1.Size(); j++)
|
||||
{
|
||||
e0 = edges[edge0[j]];
|
||||
e1 = edges[edge1[j]];
|
||||
v0 = edge_to_knot[e0];
|
||||
v1 = edge_to_knot[e1];
|
||||
v0 = ( v0 >= 0 ? v0 : -v0-1);
|
||||
v1 = ( v1 >= 0 ? v1 : -v1-1);
|
||||
if (v0 != v1)
|
||||
{
|
||||
corrections++;
|
||||
if (v0 < v1)
|
||||
{
|
||||
edge_to_knot[e1] = (oedge[edge1[j]] >= 0 ? v0 : -v0-1);
|
||||
}
|
||||
else if (v1 < v0)
|
||||
{
|
||||
edge_to_knot[e0] = (oedge[edge0[j]] >= 0 ? v1 : -v1-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
passes++;
|
||||
}
|
||||
while (corrections > 0 && passes < GetNE() + 1);
|
||||
|
||||
// Check the validity of corrections applied
|
||||
if (corrections > 0 )
|
||||
{
|
||||
mfem::err<<"Edge_to_knot mapping potentially incorrect"<<endl;
|
||||
mfem::err<<" passes = "<<passes<<endl;
|
||||
mfem::err<<" corrections = "<<corrections<<endl;
|
||||
}
|
||||
|
||||
/* Renumber knotvectors, such that:
|
||||
-- numbering is consecutive
|
||||
-- starts at zero */
|
||||
Array<int> cnt(NumOfEdges);
|
||||
cnt = 0;
|
||||
for (j = 0; j < NumOfEdges; j++)
|
||||
{
|
||||
k = edge_to_knot[j];
|
||||
cnt[(k >= 0 ? k : -k-1)]++;
|
||||
}
|
||||
|
||||
k = 0;
|
||||
for (j = 0; j < cnt.Size(); j++)
|
||||
{
|
||||
cnt[j] = (cnt[j] > 0 ? k++ : -1);
|
||||
}
|
||||
|
||||
for (j = 0; j < NumOfEdges; j++)
|
||||
{
|
||||
k = edge_to_knot[j];
|
||||
edge_to_knot[j] = (k >= 0 ? cnt[k]:-cnt[-k-1]-1);
|
||||
}
|
||||
|
||||
// Print knot to edge mapping
|
||||
mfem::out<<"Generated edge to knot mapping:"<<endl;
|
||||
for (j = 0; j < NumOfEdges; j++)
|
||||
{
|
||||
int *v = edge_vertex->GetRow(j);
|
||||
k = edge_to_knot[j];
|
||||
|
||||
v0 = v[0];
|
||||
v1 = v[1];
|
||||
if (k < 0)
|
||||
{
|
||||
v[0] = v1;
|
||||
v[1] = v0;
|
||||
}
|
||||
mfem::out<<(k >= 0 ? k:-k-1)<<" "<< v[0] <<" "<<v[1]<<endl;
|
||||
}
|
||||
|
||||
// Terminate here upon failure after printing to have an idea of edge_to_knot.
|
||||
if (corrections > 0 ) {mfem_error("Mesh::LoadPatchTopo");}
|
||||
}
|
||||
}
|
||||
|
||||
void XYZ_VectorFunction(const Vector &p, Vector &v)
|
||||
@@ -10466,7 +10702,7 @@ void Mesh::PrintTopo(std::ostream &os,const Array<int> &e_to_k) const
|
||||
os << "\nvertices\n" << NumOfVertices << '\n';
|
||||
}
|
||||
|
||||
void Mesh::Save(const char *fname, int precision) const
|
||||
void Mesh::Save(const std::string &fname, int precision) const
|
||||
{
|
||||
ofstream ofs(fname);
|
||||
ofs.precision(precision);
|
||||
|
||||
+13
-4
@@ -643,8 +643,8 @@ public:
|
||||
/** Creates mesh by reading a file in MFEM, Netgen, or VTK format. If
|
||||
generate_edges = 0 (default) edges are not generated, if 1 edges are
|
||||
generated. See also @a Mesh::LoadFromFile. */
|
||||
explicit Mesh(const char *filename, int generate_edges = 0, int refine = 1,
|
||||
bool fix_orientation = true);
|
||||
explicit Mesh(const std::string &filename, int generate_edges = 0,
|
||||
int refine = 1, bool fix_orientation = true);
|
||||
|
||||
/** Creates mesh by reading data stream in MFEM, Netgen, or VTK format. If
|
||||
generate_edges = 0 (default) edges are not generated, if 1 edges are
|
||||
@@ -697,7 +697,7 @@ public:
|
||||
@note @a filename is not cached by the Mesh object and can be
|
||||
safely deleted following this function call.
|
||||
*/
|
||||
static Mesh LoadFromFile(const char *filename,
|
||||
static Mesh LoadFromFile(const std::string &filename,
|
||||
int generate_edges = 0, int refine = 1,
|
||||
bool fix_orientation = true);
|
||||
|
||||
@@ -1984,6 +1984,15 @@ public:
|
||||
/// @}
|
||||
|
||||
///@{ @name NURBS mesh refinement methods
|
||||
/** Refine a NURBS mesh with the knots specified in the file named @a ref_file.
|
||||
The file has the number of knot vectors on the first line. It is the same
|
||||
number of knot vectors specified in the NURBS mesh in the section edges. Then
|
||||
for each knot vector specified in the section edges (with the same ordering),
|
||||
a line describes (in this order): 1) an integer giving the number of knots
|
||||
inserted, 2) the knots inserted as a double. The advantage of this method
|
||||
is that it is possible to specifically refine a coarse NURBS mesh without
|
||||
changing the mesh file itself. Examples in miniapps/nurbs/meshes. */
|
||||
void RefineNURBSFromFile(std::string ref_file);
|
||||
void KnotInsert(Array<KnotVector *> &kv);
|
||||
void KnotInsert(Array<Vector *> &kv);
|
||||
/* For each knot vector:
|
||||
@@ -2003,7 +2012,7 @@ public:
|
||||
|
||||
/// Save the mesh to a file using Mesh::Print. The given @a precision will be
|
||||
/// used for ASCII output.
|
||||
virtual void Save(const char *fname, int precision=16) const;
|
||||
virtual void Save(const std::string &fname, int precision=16) const;
|
||||
|
||||
/// Print the mesh to the given stream using the adios2 bp format
|
||||
#ifdef MFEM_USE_ADIOS2
|
||||
|
||||
+23
-23
@@ -2176,9 +2176,9 @@ void NCMesh::UpdateVertices()
|
||||
// - ghost (non-local) vertices (code -3)
|
||||
// - vertices beyond the ghost layer (code -4)
|
||||
|
||||
for (auto node = nodes.begin(); node != nodes.end(); ++node)
|
||||
for (auto & node : nodes)
|
||||
{
|
||||
node->vert_index = -4; // assume beyond ghost layer
|
||||
node.vert_index = -4; // assume beyond ghost layer
|
||||
}
|
||||
|
||||
for (int i = 0; i < leaf_elements.Size(); i++)
|
||||
@@ -2208,11 +2208,11 @@ void NCMesh::UpdateVertices()
|
||||
// STEP 2: assign indices of top-level local vertices, in original order
|
||||
|
||||
NVertices = 0;
|
||||
for (auto node = nodes.begin(); node != nodes.end(); ++node)
|
||||
for (auto &node : nodes)
|
||||
{
|
||||
if (node->vert_index == -1)
|
||||
if (node.vert_index == -1)
|
||||
{
|
||||
node->vert_index = NVertices++;
|
||||
node.vert_index = NVertices++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2308,20 +2308,20 @@ void NCMesh::UpdateVertices()
|
||||
}
|
||||
|
||||
vertex_nodeId.SetSize(NVertices);
|
||||
for (auto node = nodes.begin(); node != nodes.end(); ++node)
|
||||
for (auto &node : nodes)
|
||||
{
|
||||
if (node->HasVertex() && node->vert_index >= 0)
|
||||
if (node.HasVertex() && node.vert_index >= 0)
|
||||
{
|
||||
vertex_nodeId[node->vert_index] = node.index();
|
||||
vertex_nodeId[node.vert_index] = node.index();
|
||||
}
|
||||
}
|
||||
|
||||
NGhostVertices = 0;
|
||||
for (auto node = nodes.begin(); node != nodes.end(); ++node)
|
||||
for (auto &node : nodes)
|
||||
{
|
||||
if (node->HasVertex() && node->vert_index < 0)
|
||||
if (node.HasVertex() && node.vert_index < 0)
|
||||
{
|
||||
node->vert_index = NVertices + (NGhostVertices++);
|
||||
node.vert_index = NVertices + (NGhostVertices++);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2545,13 +2545,13 @@ void NCMesh::OnMeshUpdated(Mesh *mesh)
|
||||
NFaces = mesh->GetNumFaces();
|
||||
if (Dim < 2) { NFaces = 0; }
|
||||
// clear Node::edge_index and Face::index
|
||||
for (auto node = nodes.begin(); node != nodes.end(); ++node)
|
||||
for (auto &node : nodes)
|
||||
{
|
||||
if (node->HasEdge()) { node->edge_index = -1; }
|
||||
if (node.HasEdge()) { node.edge_index = -1; }
|
||||
}
|
||||
for (auto face = faces.begin(); face != faces.end(); ++face)
|
||||
for (auto &face : faces)
|
||||
{
|
||||
face->index = -1;
|
||||
face.index = -1;
|
||||
}
|
||||
|
||||
// get edge enumeration from the Mesh
|
||||
@@ -2615,19 +2615,19 @@ void NCMesh::OnMeshUpdated(Mesh *mesh)
|
||||
|
||||
// count ghost edges and assign their indices
|
||||
NGhostEdges = 0;
|
||||
for (auto node = nodes.begin(); node != nodes.end(); ++node)
|
||||
for (auto &node : nodes)
|
||||
{
|
||||
if (node->HasEdge() && node->edge_index < 0)
|
||||
if (node.HasEdge() && node.edge_index < 0)
|
||||
{
|
||||
node->edge_index = NEdges + (NGhostEdges++);
|
||||
node.edge_index = NEdges + (NGhostEdges++);
|
||||
}
|
||||
}
|
||||
|
||||
// count ghost faces
|
||||
NGhostFaces = 0;
|
||||
for (auto face = faces.begin(); face != faces.end(); ++face)
|
||||
for (auto &face : faces)
|
||||
{
|
||||
if (face->index < 0) { NGhostFaces++; }
|
||||
if (face.index < 0) { NGhostFaces++; }
|
||||
}
|
||||
|
||||
if (Dim == 2)
|
||||
@@ -2671,9 +2671,9 @@ void NCMesh::OnMeshUpdated(Mesh *mesh)
|
||||
}
|
||||
|
||||
// assign valid indices also to faces beyond the ghost layer
|
||||
for (auto face = faces.begin(); face != faces.end(); ++face)
|
||||
for (auto &face : faces)
|
||||
{
|
||||
if (face->index < 0) { face->index = NFaces + (nghosts++); }
|
||||
if (face.index < 0) { face.index = NFaces + (nghosts++); }
|
||||
}
|
||||
MFEM_ASSERT(nghosts == NGhostFaces, "");
|
||||
}
|
||||
@@ -3452,7 +3452,7 @@ const NCMesh::MeshId& NCMesh::NCList::LookUp(int index, int *type) const
|
||||
|
||||
if (!type)
|
||||
{
|
||||
MFEM_VERIFY(key >= 0, "entity not found.");
|
||||
MFEM_VERIFY(key >= 0, "index " << index << " not found.");
|
||||
}
|
||||
else // return entity type if requested, don't abort when not found
|
||||
{
|
||||
|
||||
+408
-36
@@ -1522,6 +1522,7 @@ NURBSExtension::NURBSExtension(const NURBSExtension &orig)
|
||||
own_topo(true),
|
||||
edge_to_knot(orig.edge_to_knot),
|
||||
knotVectors(orig.knotVectors.Size()), // knotVectors are copied in the body
|
||||
knotVectorsCompr(orig.knotVectorsCompr.Size()),
|
||||
weights(orig.weights),
|
||||
d_to_d(orig.d_to_d),
|
||||
master(orig.master),
|
||||
@@ -1547,6 +1548,7 @@ NURBSExtension::NURBSExtension(const NURBSExtension &orig)
|
||||
{
|
||||
knotVectors[i] = new KnotVector(*orig.knotVectors[i]);
|
||||
}
|
||||
CreateComprehensiveKV();
|
||||
|
||||
// Copy the patches:
|
||||
for (int p = 0; p < patches.Size(); p++)
|
||||
@@ -1649,6 +1651,8 @@ NURBSExtension::NURBSExtension(std::istream &input)
|
||||
MFEM_ABORT("invalid section: " << ident);
|
||||
}
|
||||
|
||||
CreateComprehensiveKV();
|
||||
|
||||
SetOrdersFromKnotVectors();
|
||||
|
||||
GenerateOffsets();
|
||||
@@ -1728,6 +1732,7 @@ NURBSExtension::NURBSExtension(NURBSExtension *parent, int newOrder)
|
||||
|
||||
NumOfKnotVectors = parent->GetNKV();
|
||||
knotVectors.SetSize(NumOfKnotVectors);
|
||||
knotVectorsCompr.SetSize(parent->GetNP()*parent->Dimension());
|
||||
const Array<int> &pOrders = parent->GetOrders();
|
||||
for (int i = 0; i < NumOfKnotVectors; i++)
|
||||
{
|
||||
@@ -1741,6 +1746,7 @@ NURBSExtension::NURBSExtension(NURBSExtension *parent, int newOrder)
|
||||
knotVectors[i] = new KnotVector(*parent->GetKnotVector(i));
|
||||
}
|
||||
}
|
||||
CreateComprehensiveKV();
|
||||
|
||||
// copy some data from parent
|
||||
NumOfElements = parent->NumOfElements;
|
||||
@@ -1798,6 +1804,7 @@ NURBSExtension::NURBSExtension(NURBSExtension *parent,
|
||||
knotVectors[i] = new KnotVector(*parent->GetKnotVector(i));
|
||||
}
|
||||
}
|
||||
CreateComprehensiveKV();
|
||||
|
||||
// copy some data from parent
|
||||
NumOfElements = parent->NumOfElements;
|
||||
@@ -1848,6 +1855,7 @@ NURBSExtension::NURBSExtension(Mesh *mesh_array[], int num_pieces)
|
||||
{
|
||||
knotVectors[i] = new KnotVector(*parent->GetKnotVector(i));
|
||||
}
|
||||
CreateComprehensiveKV();
|
||||
|
||||
GenerateOffsets();
|
||||
CountElements();
|
||||
@@ -1881,6 +1889,11 @@ NURBSExtension::~NURBSExtension()
|
||||
delete knotVectors[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < knotVectorsCompr.Size(); i++)
|
||||
{
|
||||
delete knotVectorsCompr[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < patches.Size(); i++)
|
||||
{
|
||||
delete patches[i];
|
||||
@@ -2360,17 +2373,6 @@ void NURBSExtension::CheckPatches()
|
||||
<< ")\n Inconsistent edge-to-knot mapping!\n";
|
||||
mfem_error();
|
||||
}
|
||||
|
||||
if ((Dimension() == 2 &&
|
||||
(edges[0] < 0 || edges[1] < 0)) ||
|
||||
|
||||
(Dimension() == 3 &&
|
||||
(edges[0] < 0 || edges[3] < 0 || edges[8] < 0)))
|
||||
{
|
||||
mfem::err << "NURBSExtension::CheckPatch (patch = " << p
|
||||
<< ") : Bad orientation!\n";
|
||||
mfem_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2402,6 +2404,269 @@ void NURBSExtension::CheckBdrPatches()
|
||||
}
|
||||
}
|
||||
|
||||
void NURBSExtension::CheckKVDirection(int p, Array <int> &kvdir)
|
||||
{
|
||||
// patchTopo->GetElementEdges is not yet implemented for 1D
|
||||
MFEM_VERIFY(Dimension()>1, "1D not yet implemented.");
|
||||
|
||||
kvdir.SetSize(Dimension());
|
||||
kvdir = 0;
|
||||
|
||||
Array<int> patchvert, edges, orient, edgevert;
|
||||
|
||||
patchTopo->GetElementVertices(p, patchvert);
|
||||
|
||||
patchTopo->GetElementEdges(p, edges, orient);
|
||||
|
||||
// Compare the vertices of the patches with the vertices of the knotvectors of knot2dge
|
||||
// Based on the match the orientation will be a 1 or a -1
|
||||
// -1: direction is flipped
|
||||
// 1: direction is not flipped
|
||||
|
||||
|
||||
for (int i = 0; i < edges.Size(); i++)
|
||||
{
|
||||
// First side
|
||||
patchTopo->GetEdgeVertices(edges[i], edgevert);
|
||||
if (edgevert[0] == patchvert[0] && edgevert[1] == patchvert[1])
|
||||
{
|
||||
kvdir[0] = 1;
|
||||
}
|
||||
|
||||
if (edgevert[0] == patchvert[1] && edgevert[1] == patchvert[0])
|
||||
{
|
||||
kvdir[0] = -1;
|
||||
}
|
||||
|
||||
// Second side
|
||||
if (edgevert[0] == patchvert[1] && edgevert[1] == patchvert[2])
|
||||
{
|
||||
kvdir[1] = 1;
|
||||
}
|
||||
|
||||
if (edgevert[0] == patchvert[2] && edgevert[1] == patchvert[1])
|
||||
{
|
||||
kvdir[1] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (Dimension() == 3)
|
||||
{
|
||||
// Third side
|
||||
for (int i = 0; i < edges.Size(); i++)
|
||||
{
|
||||
patchTopo->GetEdgeVertices(edges[i], edgevert);
|
||||
|
||||
if (edgevert[0] == patchvert[0] && edgevert[1] == patchvert[4])
|
||||
{
|
||||
kvdir[2] = 1;
|
||||
}
|
||||
|
||||
if (edgevert[0] == patchvert[4] && edgevert[1] == patchvert[0])
|
||||
{
|
||||
kvdir[2] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(kvdir.Find(0) == -1, "Could not find direction of knotvector.");
|
||||
}
|
||||
|
||||
void NURBSExtension::CreateComprehensiveKV()
|
||||
{
|
||||
Array<int> edges, orient, kvdir;
|
||||
Array<int> e(Dimension());
|
||||
|
||||
// 1D: comprehensive and unique KV are the same
|
||||
if (Dimension() == 1)
|
||||
{
|
||||
knotVectorsCompr.SetSize(GetNKV());
|
||||
for (int i = 0; i < GetNKV(); i++)
|
||||
{
|
||||
knotVectorsCompr[i] = new KnotVector(*(KnotVec(i)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (Dimension() == 2)
|
||||
{
|
||||
knotVectorsCompr.SetSize(GetNP()*Dimension());
|
||||
e[0] = 0;
|
||||
e[1] = 1;
|
||||
}
|
||||
else if (Dimension() == 3)
|
||||
{
|
||||
knotVectorsCompr.SetSize(GetNP()*Dimension());
|
||||
e[0] = 0;
|
||||
e[1] = 3;
|
||||
e[2] = 8;
|
||||
}
|
||||
|
||||
for (int p = 0; p < GetNP(); p++)
|
||||
{
|
||||
CheckKVDirection(p, kvdir);
|
||||
|
||||
patchTopo->GetElementEdges(p, edges, orient);
|
||||
|
||||
for (int d = 0; d < Dimension(); d++)
|
||||
{
|
||||
// Indices in unique and comprehensive sets of the KnotVector
|
||||
int iun = edges[e[d]];
|
||||
int icomp = Dimension()*p+d;
|
||||
|
||||
knotVectorsCompr[icomp] = new KnotVector(*(KnotVec(iun)));
|
||||
|
||||
if (kvdir[d] == -1) {knotVectorsCompr[icomp]->Flip();}
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(ConsistentKVSets(), "Mismatch in KnotVectors");
|
||||
}
|
||||
|
||||
void NURBSExtension::UpdateUniqueKV()
|
||||
{
|
||||
Array<int> e(Dimension());
|
||||
|
||||
// 1D: comprehensive and unique KV are the same
|
||||
if (Dimension() == 1)
|
||||
{
|
||||
for (int i = 0; i < GetNKV(); i++)
|
||||
{
|
||||
*(KnotVec(i)) = *(knotVectorsCompr[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (Dimension() == 2)
|
||||
{
|
||||
e[0] = 0;
|
||||
e[1] = 1;
|
||||
}
|
||||
else if (Dimension() == 3)
|
||||
{
|
||||
e[0] = 0;
|
||||
e[1] = 3;
|
||||
e[2] = 8;
|
||||
}
|
||||
|
||||
for (int p = 0; p < GetNP(); p++)
|
||||
{
|
||||
Array<int> edges, orient, kvdir;
|
||||
|
||||
patchTopo->GetElementEdges(p, edges, orient);
|
||||
CheckKVDirection(p, kvdir);
|
||||
|
||||
for ( int d = 0; d < Dimension(); d++)
|
||||
{
|
||||
bool flip = false;
|
||||
if (kvdir[d] == -1) {flip = true;}
|
||||
|
||||
// Indices in unique and comprehensive sets of the KnotVector
|
||||
int iun = edges[e[d]];
|
||||
int icomp = Dimension()*p+d;
|
||||
|
||||
// Check if difference in order
|
||||
int o1 = KnotVec(iun)->GetOrder();
|
||||
int o2 = knotVectorsCompr[icomp]->GetOrder();
|
||||
int diffo = abs(o1 - o2);
|
||||
|
||||
if (diffo)
|
||||
{
|
||||
// Update reduced set of knotvectors
|
||||
*(KnotVec(iun)) = *(knotVectorsCompr[icomp]);
|
||||
|
||||
// Give correct direction to unique knotvector.
|
||||
if (flip) { KnotVec(iun)->Flip(); }
|
||||
}
|
||||
|
||||
// Check if difference between knots
|
||||
Vector diffknot;
|
||||
|
||||
if (flip) { knotVectorsCompr[icomp]->Flip(); }
|
||||
|
||||
KnotVec(iun)->Difference(*(knotVectorsCompr[icomp]), diffknot);
|
||||
|
||||
if (flip) { knotVectorsCompr[icomp]->Flip(); }
|
||||
|
||||
if (diffknot.Size() > 0)
|
||||
{
|
||||
// Update reduced set of knotvectors
|
||||
*(KnotVec(iun)) = *(knotVectorsCompr[icomp]);
|
||||
|
||||
// Give correct direction to unique knotvector.
|
||||
if (flip) {KnotVec(iun)->Flip();}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(ConsistentKVSets(), "Mismatch in KnotVectors");
|
||||
}
|
||||
|
||||
bool NURBSExtension::ConsistentKVSets()
|
||||
{
|
||||
// patchTopo->GetElementEdges is not yet implemented for 1D
|
||||
MFEM_VERIFY(Dimension()>1, "1D not yet implemented.");
|
||||
|
||||
Array<int> edges, orient, kvdir;
|
||||
Vector diff;
|
||||
|
||||
Array<int>e(Dimension());
|
||||
|
||||
e[0] = 0;
|
||||
|
||||
if (Dimension() == 2)
|
||||
{
|
||||
e[1] = 1;
|
||||
}
|
||||
else if (Dimension() == 3)
|
||||
{
|
||||
e[1] = 3;
|
||||
e[2] = 8;
|
||||
}
|
||||
|
||||
for (int p = 0; p < GetNP(); p++)
|
||||
{
|
||||
patchTopo->GetElementEdges(p, edges, orient);
|
||||
|
||||
CheckKVDirection(p, kvdir);
|
||||
|
||||
for (int d = 0; d < Dimension(); d++)
|
||||
{
|
||||
bool flip = false;
|
||||
if (kvdir[d] == -1) {flip = true;}
|
||||
|
||||
// Indices in unique and comprehensive sets of the KnotVector
|
||||
int iun = edges[e[d]];
|
||||
int icomp = Dimension()*p+d;
|
||||
|
||||
// Check if KnotVectors are of equal order
|
||||
int o1 = KnotVec(iun)->GetOrder();
|
||||
int o2 = knotVectorsCompr[icomp]->GetOrder();
|
||||
int diffo = abs(o1 - o2);
|
||||
|
||||
if (diffo)
|
||||
{
|
||||
mfem::out << "\norder of knotVectorsCompr " << d << " of patch " << p;
|
||||
mfem::out << " does not agree with knotVectors " << KnotInd(iun) << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if Knotvectors have the same knots
|
||||
if (flip) {knotVectorsCompr[icomp]->Flip();}
|
||||
|
||||
KnotVec(iun)->Difference(*(knotVectorsCompr[icomp]), diff);
|
||||
|
||||
if (flip) {knotVectorsCompr[icomp]->Flip();}
|
||||
|
||||
if (diff.Size() > 0)
|
||||
{
|
||||
mfem::out << "\nknotVectorsCompr " << d << " of patch " << p;
|
||||
mfem::out << " does not agree with knotVectors " << KnotInd(iun) << "\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NURBSExtension::GetPatchKnotVectors(int p, Array<KnotVector *> &kv)
|
||||
{
|
||||
Array<int> edges, orient;
|
||||
@@ -2410,20 +2675,18 @@ void NURBSExtension::GetPatchKnotVectors(int p, Array<KnotVector *> &kv)
|
||||
|
||||
if (Dimension() == 1)
|
||||
{
|
||||
kv[0] = KnotVec(p);
|
||||
kv[0] = knotVectorsCompr[Dimension()*p];
|
||||
}
|
||||
else if (Dimension() == 2)
|
||||
{
|
||||
patchTopo->GetElementEdges(p, edges, orient);
|
||||
kv[0] = KnotVec(edges[0]);
|
||||
kv[1] = KnotVec(edges[1]);
|
||||
kv[0] = knotVectorsCompr[Dimension()*p];
|
||||
kv[1] = knotVectorsCompr[Dimension()*p + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
patchTopo->GetElementEdges(p, edges, orient);
|
||||
kv[0] = KnotVec(edges[0]);
|
||||
kv[1] = KnotVec(edges[3]);
|
||||
kv[2] = KnotVec(edges[8]);
|
||||
kv[0] = knotVectorsCompr[Dimension()*p];
|
||||
kv[1] = knotVectorsCompr[Dimension()*p + 1];
|
||||
kv[2] = knotVectorsCompr[Dimension()*p + 2];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2433,22 +2696,21 @@ const
|
||||
Array<int> edges, orient;
|
||||
|
||||
kv.SetSize(Dimension());
|
||||
|
||||
if (Dimension() == 1)
|
||||
{
|
||||
kv[0] = KnotVec(p);
|
||||
kv[0] = knotVectorsCompr[Dimension()*p];
|
||||
}
|
||||
else if (Dimension() == 2)
|
||||
{
|
||||
patchTopo->GetElementEdges(p, edges, orient);
|
||||
kv[0] = KnotVec(edges[0]);
|
||||
kv[1] = KnotVec(edges[1]);
|
||||
kv[0] = knotVectorsCompr[Dimension()*p];
|
||||
kv[1] = knotVectorsCompr[Dimension()*p + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
patchTopo->GetElementEdges(p, edges, orient);
|
||||
kv[0] = KnotVec(edges[0]);
|
||||
kv[1] = KnotVec(edges[3]);
|
||||
kv[2] = KnotVec(edges[8]);
|
||||
kv[0] = knotVectorsCompr[Dimension()*p];
|
||||
kv[1] = knotVectorsCompr[Dimension()*p + 1];
|
||||
kv[2] = knotVectorsCompr[Dimension()*p + 2];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3089,6 +3351,55 @@ void NURBSExtension::Generate3DElementDofTable()
|
||||
el_dof = new Table(NumOfActiveElems, el_dof_list);
|
||||
}
|
||||
|
||||
void NURBSExtension::GetPatchDofs(const int patch, Array<int> &dofs)
|
||||
{
|
||||
const KnotVector *kv[3];
|
||||
NURBSPatchMap p2g(this);
|
||||
|
||||
p2g.SetPatchDofMap(patch, kv);
|
||||
|
||||
if (Dimension() == 1)
|
||||
{
|
||||
const int nx = kv[0]->GetNCP();
|
||||
dofs.SetSize(nx);
|
||||
|
||||
for (int i=0; i<nx; ++i)
|
||||
{
|
||||
dofs[i] = DofMap(p2g(i));
|
||||
}
|
||||
}
|
||||
else if (Dimension() == 2)
|
||||
{
|
||||
const int nx = kv[0]->GetNCP();
|
||||
const int ny = kv[1]->GetNCP();
|
||||
dofs.SetSize(nx * ny);
|
||||
|
||||
for (int j=0; j<ny; ++j)
|
||||
for (int i=0; i<nx; ++i)
|
||||
{
|
||||
dofs[i + (nx * j)] = DofMap(p2g(i, j));
|
||||
}
|
||||
}
|
||||
else if (Dimension() == 3)
|
||||
{
|
||||
const int nx = kv[0]->GetNCP();
|
||||
const int ny = kv[1]->GetNCP();
|
||||
const int nz = kv[2]->GetNCP();
|
||||
dofs.SetSize(nx * ny * nz);
|
||||
|
||||
for (int k=0; k<nz; ++k)
|
||||
for (int j=0; j<ny; ++j)
|
||||
for (int i=0; i<nx; ++i)
|
||||
{
|
||||
dofs[i + (nx * (j + (k * ny)))] = DofMap(p2g(i, j, k));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Only 1D/2D/3D supported currently in NURBSExtension::GetPatchDofs");
|
||||
}
|
||||
}
|
||||
|
||||
void NURBSExtension::GenerateBdrElementDofTable()
|
||||
{
|
||||
if (Dimension() == 1)
|
||||
@@ -3348,6 +3659,7 @@ void NURBSExtension::SetKnotsFromPatches()
|
||||
}
|
||||
}
|
||||
|
||||
UpdateUniqueKV();
|
||||
SetOrdersFromKnotVectors();
|
||||
|
||||
GenerateOffsets();
|
||||
@@ -3471,6 +3783,7 @@ void NURBSExtension::KnotInsert(Array<KnotVector *> &kv)
|
||||
{
|
||||
Array<int> edges;
|
||||
Array<int> orient;
|
||||
Array<int> kvdir;
|
||||
|
||||
Array<KnotVector *> pkv(Dimension());
|
||||
|
||||
@@ -3494,7 +3807,26 @@ void NURBSExtension::KnotInsert(Array<KnotVector *> &kv)
|
||||
pkv[2] = kv[KnotInd(edges[8])];
|
||||
}
|
||||
|
||||
patches[p]->KnotInsert(pkv);
|
||||
|
||||
// Check whether inserted knots should be flipped before inserting.
|
||||
// Knotvectors are stored in a different array pkvc such that the original
|
||||
// knots which are inserted are not changed.
|
||||
// We need those knots for multiple patches so they have to remain original
|
||||
CheckKVDirection(p, kvdir);
|
||||
|
||||
Array<KnotVector *> pkvc(Dimension());
|
||||
for (int d = 0; d < Dimension(); d++)
|
||||
{
|
||||
pkvc[d] = new KnotVector(*(pkv[d]));
|
||||
|
||||
if (kvdir[d] == -1)
|
||||
{
|
||||
pkvc[d]->Flip();
|
||||
}
|
||||
}
|
||||
|
||||
patches[p]->KnotInsert(pkvc);
|
||||
for (int d = 0; d < Dimension(); d++) { delete pkvc[d]; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3502,6 +3834,7 @@ void NURBSExtension::KnotInsert(Array<Vector *> &kv)
|
||||
{
|
||||
Array<int> edges;
|
||||
Array<int> orient;
|
||||
Array<int> kvdir;
|
||||
|
||||
Array<Vector *> pkv(Dimension());
|
||||
|
||||
@@ -3525,11 +3858,41 @@ void NURBSExtension::KnotInsert(Array<Vector *> &kv)
|
||||
pkv[2] = kv[KnotInd(edges[8])];
|
||||
}
|
||||
|
||||
patches[p]->KnotInsert(pkv);
|
||||
|
||||
// Check whether inserted knots should be flipped before inserting.
|
||||
// Knotvectors are stored in a different array pkvc such that the original
|
||||
// knots which are inserted are not changed.
|
||||
CheckKVDirection(p, kvdir);
|
||||
|
||||
Array<Vector *> pkvc(Dimension());
|
||||
for (int d = 0; d < Dimension(); d++)
|
||||
{
|
||||
pkvc[d] = new Vector(*(pkv[d]));
|
||||
|
||||
if (kvdir[d] == -1)
|
||||
{
|
||||
// Find flip point, for knotvectors that do not have the domain [0:1]
|
||||
KnotVector *kva = knotVectorsCompr[Dimension()*p+d];
|
||||
double apb = (*kva)[0] + (*kva)[kva->Size()-1];
|
||||
|
||||
// Flip vector
|
||||
int size = pkvc[d]->Size();
|
||||
int ns = ceil(size/2.0);
|
||||
for (int j = 0; j < ns; j++)
|
||||
{
|
||||
double tmp = apb - pkvc[d]->Elem(j);
|
||||
pkvc[d]->Elem(j) = apb - pkvc[d]->Elem(size-1-j);
|
||||
pkvc[d]->Elem(size-1-j) = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
patches[p]->KnotInsert(pkvc);
|
||||
|
||||
for (int i = 0; i < Dimension(); i++) { delete pkvc[i]; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NURBSExtension::GetPatchNets(const Vector &coords, int vdim)
|
||||
{
|
||||
if (Dimension() == 1)
|
||||
@@ -3730,6 +4093,12 @@ void NURBSExtension::Set3DSolutionVector(Vector &coords, int vdim)
|
||||
}
|
||||
}
|
||||
|
||||
void NURBSExtension::GetElementIJK(int elem, Array<int> & ijk)
|
||||
{
|
||||
MFEM_VERIFY(ijk.Size() == el_to_IJK.NumCols(), "");
|
||||
el_to_IJK.GetRow(elem, ijk);
|
||||
}
|
||||
|
||||
void NURBSExtension::SetPatchToElements()
|
||||
{
|
||||
const int np = GetNP();
|
||||
@@ -3813,6 +4182,7 @@ ParNURBSExtension::ParNURBSExtension(MPI_Comm comm, NURBSExtension *parent,
|
||||
{
|
||||
knotVectors[i] = new KnotVector(*parent->GetKnotVector(i));
|
||||
}
|
||||
CreateComprehensiveKV();
|
||||
|
||||
GenerateOffsets();
|
||||
CountElements();
|
||||
@@ -3868,6 +4238,7 @@ ParNURBSExtension::ParNURBSExtension(NURBSExtension *parent,
|
||||
|
||||
NumOfKnotVectors = parent->NumOfKnotVectors;
|
||||
Swap(knotVectors, parent->knotVectors);
|
||||
Swap(knotVectorsCompr, parent->knotVectorsCompr);
|
||||
|
||||
NumOfVertices = parent->NumOfVertices;
|
||||
NumOfElements = parent->NumOfElements;
|
||||
@@ -4171,22 +4542,23 @@ void NURBSPatchMap::GetPatchKnotVectors(int p, const KnotVector *kv[])
|
||||
|
||||
if (Ext->Dimension() == 1)
|
||||
{
|
||||
kv[0] = Ext->KnotVec(p);
|
||||
kv[0] = Ext->knotVectorsCompr[Ext->Dimension()*p];
|
||||
}
|
||||
else if (Ext->Dimension() == 2)
|
||||
{
|
||||
Ext->patchTopo->GetElementEdges(p, edges, oedge);
|
||||
kv[0] = Ext->KnotVec(edges[0]);
|
||||
kv[1] = Ext->KnotVec(edges[1]);
|
||||
|
||||
kv[0] = Ext->knotVectorsCompr[Ext->Dimension()*p];
|
||||
kv[1] = Ext->knotVectorsCompr[Ext->Dimension()*p + 1];
|
||||
}
|
||||
else if (Ext->Dimension() == 3)
|
||||
{
|
||||
Ext->patchTopo->GetElementEdges(p, edges, oedge);
|
||||
Ext->patchTopo->GetElementFaces(p, faces, oface);
|
||||
|
||||
kv[0] = Ext->KnotVec(edges[0]);
|
||||
kv[1] = Ext->KnotVec(edges[3]);
|
||||
kv[2] = Ext->KnotVec(edges[8]);
|
||||
kv[0] = Ext->knotVectorsCompr[Ext->Dimension()*p];
|
||||
kv[1] = Ext->knotVectorsCompr[Ext->Dimension()*p + 1];
|
||||
kv[2] = Ext->knotVectorsCompr[Ext->Dimension()*p + 2];
|
||||
}
|
||||
opatch = 0;
|
||||
}
|
||||
|
||||
+34
-2
@@ -21,6 +21,7 @@
|
||||
#include "../general/communication.hpp"
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -218,7 +219,11 @@ protected:
|
||||
Mesh *patchTopo;
|
||||
int own_topo;
|
||||
Array<int> edge_to_knot;
|
||||
/** Set of knotvectors containing unique KnotVectors only */
|
||||
Array<KnotVector *> knotVectors;
|
||||
/** Comprehensive set of knotvectors. This set contains a KnotVector for
|
||||
every edge.*/
|
||||
Array<KnotVector *> knotVectorsCompr;
|
||||
Vector weights;
|
||||
|
||||
// periodic BC info:
|
||||
@@ -261,10 +266,22 @@ protected:
|
||||
void CheckPatches();
|
||||
void CheckBdrPatches();
|
||||
|
||||
/** Checks the direction of the knotvectors in the patch based on
|
||||
the patch orientation for patch @a p returns the direction of
|
||||
the Knotvectors in @a kvdir.*/
|
||||
void CheckKVDirection(int p, Array <int> &kvdir);
|
||||
/** Creates the comprehensive set of KnotVectors. They are the same for 1D. */
|
||||
void CreateComprehensiveKV();
|
||||
/** Updates the unique set of KnotVectors */
|
||||
void UpdateUniqueKV();
|
||||
|
||||
/** Checks if the comprehensive array of KnotVectors agrees with
|
||||
the reduced set of KnotVectors. Returns false if it finds
|
||||
a difference. */
|
||||
bool ConsistentKVSets();
|
||||
|
||||
void GetPatchKnotVectors (int p, Array<KnotVector *> &kv);
|
||||
void GetPatchKnotVectors (int p, Array<const KnotVector *> &kv) const;
|
||||
void GetBdrPatchKnotVectors(int p, Array<KnotVector *> &kv);
|
||||
void GetBdrPatchKnotVectors(int p, Array<const KnotVector *> &kv) const;
|
||||
|
||||
void SetOrderFromOrders();
|
||||
void SetOrdersFromKnotVectors();
|
||||
@@ -407,6 +424,11 @@ public:
|
||||
int GetNTotalDof() const { return NumOfDofs; }
|
||||
int GetNDof() const { return NumOfActiveDofs; }
|
||||
|
||||
/// Returns knotvectors in each dimension for patch @a p.
|
||||
void GetPatchKnotVectors(int p, Array<const KnotVector *> &kv) const;
|
||||
|
||||
void GetBdrPatchKnotVectors(int p, Array<const KnotVector *> &kv) const;
|
||||
|
||||
// Knotvector read-only access function
|
||||
const KnotVector *GetKnotVector(int i) const { return knotVectors[i]; }
|
||||
|
||||
@@ -466,6 +488,16 @@ public:
|
||||
void KnotInsert(Array<KnotVector *> &kv);
|
||||
void KnotInsert(Array<Vector *> &kv);
|
||||
|
||||
/// Returns the index of the patch containing element @a elem.
|
||||
int GetElementPatch(int elem) const { return el_to_patch[elem]; }
|
||||
|
||||
/** Returns the Cartesian indices (i,j) in 2D or (i,j,k) in 3D of element
|
||||
@a elem, in the knot-span tensor product ordering for its patch. */
|
||||
void GetElementIJK(int elem, Array<int> & ijk);
|
||||
|
||||
// Returns the degrees of freedom on the patch, in Cartesian order.
|
||||
void GetPatchDofs(const int patch, Array<int> &dofs);
|
||||
|
||||
const Array<int>& GetPatchElements(int patch);
|
||||
const Array<int>& GetPatchBdrElements(int patch);
|
||||
};
|
||||
|
||||
+3
-3
@@ -4952,7 +4952,7 @@ void ParMesh::Print(std::ostream &os) const
|
||||
}
|
||||
}
|
||||
|
||||
void ParMesh::Save(const char *fname, int precision) const
|
||||
void ParMesh::Save(const std::string &fname, int precision) const
|
||||
{
|
||||
ostringstream fname_with_suffix;
|
||||
fname_with_suffix << fname << "." << setfill('0') << setw(6) << MyRank;
|
||||
@@ -5615,7 +5615,7 @@ Mesh ParMesh::GetSerialMesh(int save_rank) const
|
||||
return serialmesh;
|
||||
}
|
||||
|
||||
void ParMesh::SaveAsOne(const char *fname, int precision) const
|
||||
void ParMesh::SaveAsOne(const std::string &fname, int precision) const
|
||||
{
|
||||
ofstream ofs;
|
||||
if (MyRank == 0)
|
||||
@@ -6505,7 +6505,7 @@ static void PrintVertex(const Vertex &v, int space_dim, ostream &os)
|
||||
}
|
||||
}
|
||||
|
||||
void ParMesh::PrintSharedEntities(const char *fname_prefix) const
|
||||
void ParMesh::PrintSharedEntities(const std::string &fname_prefix) const
|
||||
{
|
||||
stringstream out_name;
|
||||
out_name << fname_prefix << '_' << setw(5) << setfill('0') << MyRank
|
||||
|
||||
+3
-3
@@ -596,7 +596,7 @@ public:
|
||||
/// given suffixes according to the MPI rank. The mesh will be written to the
|
||||
/// files using ParMesh::Print. The given @a precision will be used for ASCII
|
||||
/// output.
|
||||
void Save(const char *fname, int precision=16) const override;
|
||||
void Save(const std::string &fname, int precision=16) const override;
|
||||
|
||||
#ifdef MFEM_USE_ADIOS2
|
||||
/** Print the part of the mesh in the calling processor using adios2 bp
|
||||
@@ -625,7 +625,7 @@ public:
|
||||
|
||||
/// Save the mesh as a single file (using ParMesh::PrintAsOne). The given
|
||||
/// @a precision is used for ASCII output.
|
||||
void SaveAsOne(const char *fname, int precision=16) const;
|
||||
void SaveAsOne(const std::string &fname, int precision=16) const;
|
||||
|
||||
/// Old mesh format (Netgen/Truegrid) version of 'PrintAsOne'
|
||||
void PrintAsOneXG(std::ostream &out = mfem::out);
|
||||
@@ -662,7 +662,7 @@ public:
|
||||
InverseElementTransformation *inv_trans = NULL) override;
|
||||
|
||||
/// Debugging method
|
||||
void PrintSharedEntities(const char *fname_prefix) const;
|
||||
void PrintSharedEntities(const std::string &fname_prefix) const;
|
||||
|
||||
virtual ~ParMesh();
|
||||
|
||||
|
||||
+17
-23
@@ -435,25 +435,21 @@ void ParNCMesh::CreateGroups(int nentities, Array<Connection> &index_rank,
|
||||
entity_group = 0;
|
||||
|
||||
CommGroup group;
|
||||
group.reserve(128);
|
||||
|
||||
int begin = 0, end = 0;
|
||||
while (begin < index_rank.Size())
|
||||
for (auto begin = index_rank.begin(); begin != index_rank.end(); /* nothing */)
|
||||
{
|
||||
int index = index_rank[begin].from;
|
||||
if (index >= nentities)
|
||||
{
|
||||
break; // probably creating entity_conf_group (no ghosts)
|
||||
}
|
||||
while (end < index_rank.Size() && index_rank[end].from == index)
|
||||
{
|
||||
end++;
|
||||
}
|
||||
group.resize(end - begin);
|
||||
for (int i = begin; i < end; i++)
|
||||
{
|
||||
group[i - begin] = index_rank[i].to;
|
||||
}
|
||||
const auto &index = begin->from;
|
||||
if (index >= nentities) { break; }
|
||||
|
||||
// Locate the next connection that is not from this index
|
||||
const auto end = std::find_if(begin, index_rank.end(),
|
||||
[&index](const mfem::Connection &c) { return c.from != index;});
|
||||
|
||||
// For each connection from this index, collect the ranks connected.
|
||||
group.resize(std::distance(begin, end));
|
||||
std::transform(begin, end, group.begin(), [](const mfem::Connection &c) { return c.to; });
|
||||
|
||||
// assign this entity's group and advance the search start
|
||||
entity_group[index] = GetGroupId(group);
|
||||
begin = end;
|
||||
}
|
||||
@@ -461,9 +457,9 @@ void ParNCMesh::CreateGroups(int nentities, Array<Connection> &index_rank,
|
||||
|
||||
void ParNCMesh::AddConnections(int entity, int index, const Array<int> &ranks)
|
||||
{
|
||||
for (int i = 0; i < ranks.Size(); i++)
|
||||
for (auto rank : ranks)
|
||||
{
|
||||
entity_index_rank[entity].Append(Connection(index, ranks[i]));
|
||||
entity_index_rank[entity].Append(Connection(index, rank));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,9 +476,8 @@ void ParNCMesh::CalculatePMatrixGroups()
|
||||
ranks.Reserve(256);
|
||||
|
||||
// connect slave edges to master edges and their vertices
|
||||
for (int i = 0; i < shared_edges.masters.Size(); i++)
|
||||
for (const auto &master_edge : shared_edges.masters)
|
||||
{
|
||||
const Master &master_edge = shared_edges.masters[i];
|
||||
ranks.SetSize(0);
|
||||
for (int j = master_edge.slaves_begin; j < master_edge.slaves_end; j++)
|
||||
{
|
||||
@@ -502,9 +497,8 @@ void ParNCMesh::CalculatePMatrixGroups()
|
||||
}
|
||||
|
||||
// connect slave faces to master faces and their edges and vertices
|
||||
for (int i = 0; i < shared_faces.masters.Size(); i++)
|
||||
for (const auto &master_face : shared_faces.masters)
|
||||
{
|
||||
const Master &master_face = shared_faces.masters[i];
|
||||
ranks.SetSize(0);
|
||||
for (int j = master_face.slaves_begin; j < master_face.slaves_end; j++)
|
||||
{
|
||||
|
||||
+25
-25
@@ -82,23 +82,23 @@ public:
|
||||
/** An override of NCMesh::Refine, which is called eventually, after making
|
||||
sure that refinements that occur on the processor boundary are sent to
|
||||
the neighbor processors so they can keep their ghost layers up to date.*/
|
||||
virtual void Refine(const Array<Refinement> &refinements);
|
||||
void Refine(const Array<Refinement> &refinements) override;
|
||||
|
||||
/// Parallel version of NCMesh::LimitNCLevel.
|
||||
virtual void LimitNCLevel(int max_nc_level);
|
||||
void LimitNCLevel(int max_nc_level) override;
|
||||
|
||||
/** Parallel version of NCMesh::CheckDerefinementNCLevel. */
|
||||
virtual void CheckDerefinementNCLevel(const Table &deref_table,
|
||||
Array<int> &level_ok, int max_nc_level);
|
||||
void CheckDerefinementNCLevel(const Table &deref_table,
|
||||
Array<int> &level_ok, int max_nc_level) override;
|
||||
|
||||
/** Parallel reimplementation of NCMesh::Derefine, keeps ghost layers
|
||||
in sync. The interface is identical. */
|
||||
virtual void Derefine(const Array<int> &derefs);
|
||||
void Derefine(const Array<int> &derefs) override;
|
||||
|
||||
/** Gets partitioning for the coarse mesh if the current fine mesh were to
|
||||
be derefined. */
|
||||
virtual void GetFineToCoarsePartitioning(const Array<int> &derefs,
|
||||
Array<int> &new_ranks) const;
|
||||
void GetFineToCoarsePartitioning(const Array<int> &derefs,
|
||||
Array<int> &new_ranks) const;
|
||||
|
||||
/** Migrate leaf elements of the global refinement hierarchy (including ghost
|
||||
elements) so that each processor owns the same number of leaves (+-1).
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
int GetNGhostVertices() const { return NGhostVertices; }
|
||||
int GetNGhostEdges() const { return NGhostEdges; }
|
||||
int GetNGhostFaces() const { return NGhostFaces; }
|
||||
int GetNGhostElements() const { return NGhostElements; }
|
||||
int GetNGhostElements() const override { return NGhostElements; }
|
||||
|
||||
// Return a list of vertices/edges/faces shared by this processor and at
|
||||
// least one other processor. These are subsets of NCMesh::<entity>_list. */
|
||||
@@ -232,12 +232,12 @@ public:
|
||||
|
||||
/** Extension of NCMesh::GetBoundaryClosure. Filters out ghost vertices and
|
||||
ghost edges from 'bdr_vertices' and 'bdr_edges'. */
|
||||
virtual void GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
|
||||
Array<int> &bdr_vertices,
|
||||
Array<int> &bdr_edges);
|
||||
void GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
|
||||
Array<int> &bdr_vertices,
|
||||
Array<int> &bdr_edges) override;
|
||||
|
||||
/// Save memory by releasing all non-essential and cached data.
|
||||
virtual void Trim();
|
||||
void Trim() override;
|
||||
|
||||
/// Return total number of bytes allocated.
|
||||
std::size_t MemoryUsage(bool with_base = true) const;
|
||||
@@ -267,8 +267,8 @@ protected: // implementation
|
||||
MPI_Comm MyComm;
|
||||
int NRanks;
|
||||
|
||||
typedef std::vector<CommGroup> GroupList;
|
||||
typedef std::map<CommGroup, GroupId> GroupMap;
|
||||
using GroupList = std::vector<CommGroup>;
|
||||
using GroupMap = std::map<CommGroup, GroupId>;
|
||||
|
||||
GroupList groups; // comm group list; NOTE: groups[0] = { MyRank }
|
||||
GroupMap group_id; // search index over groups
|
||||
@@ -299,7 +299,7 @@ protected: // implementation
|
||||
Array<int> ghost_layer; ///< list of elements whose 'element_type' == 2.
|
||||
Array<int> boundary_layer; ///< list of type 3 elements
|
||||
|
||||
virtual void Update();
|
||||
void Update() override;
|
||||
|
||||
/// Return the processor number for a global element number.
|
||||
int Partition(long index, long total_elements) const
|
||||
@@ -313,13 +313,13 @@ protected: // implementation
|
||||
long PartitionFirstIndex(int rank, long total_elements) const
|
||||
{ return (rank * total_elements + NRanks-1) / NRanks; }
|
||||
|
||||
virtual void BuildFaceList();
|
||||
virtual void BuildEdgeList();
|
||||
virtual void BuildVertexList();
|
||||
void BuildFaceList() override;
|
||||
void BuildEdgeList() override;
|
||||
void BuildVertexList() override;
|
||||
|
||||
virtual void ElementSharesFace(int elem, int local, int face);
|
||||
virtual void ElementSharesEdge(int elem, int local, int enode);
|
||||
virtual void ElementSharesVertex(int elem, int local, int vnode);
|
||||
void ElementSharesFace(int elem, int local, int face) override;
|
||||
void ElementSharesEdge(int elem, int local, int enode) override;
|
||||
void ElementSharesVertex(int elem, int local, int vnode) override;
|
||||
|
||||
GroupId GetGroupId(const CommGroup &group);
|
||||
GroupId GetSingletonGroup(int rank);
|
||||
@@ -451,8 +451,8 @@ protected: // implementation
|
||||
protected:
|
||||
ParNCMesh* pncmesh;
|
||||
|
||||
virtual void Encode(int);
|
||||
virtual void Decode(int);
|
||||
void Encode(int) override;
|
||||
void Decode(int) override;
|
||||
};
|
||||
|
||||
/** Used by ParNCMesh::Refine() to inform neighbors about refinements at
|
||||
@@ -513,8 +513,8 @@ protected: // implementation
|
||||
protected:
|
||||
ElementSet eset;
|
||||
|
||||
virtual void Encode(int);
|
||||
virtual void Decode(int);
|
||||
void Encode(int) override;
|
||||
void Decode(int) override;
|
||||
};
|
||||
|
||||
/** Assign new Element::rank to leaf elements and send them to their new
|
||||
|
||||
@@ -162,6 +162,8 @@ void ParTransferMap::Transfer(const ParGridFunction &src,
|
||||
if (category_ == TransferCategory::ParentToSubMesh)
|
||||
{
|
||||
// dst = S1^T src
|
||||
src.HostRead();
|
||||
dst.HostWrite(); // dst is fully overwritten
|
||||
for (int i = 0; i < sub1_to_parent_map_.Size(); i++)
|
||||
{
|
||||
double s = 1.0;
|
||||
@@ -178,6 +180,8 @@ void ParTransferMap::Transfer(const ParGridFunction &src,
|
||||
//
|
||||
// G is identity if the partitioning matches
|
||||
|
||||
src.HostRead();
|
||||
dst.HostReadWrite(); // dst is only partially overwritten
|
||||
for (int i = 0; i < sub1_to_parent_map_.Size(); i++)
|
||||
{
|
||||
double s = 1.0;
|
||||
@@ -196,6 +200,9 @@ void ParTransferMap::Transfer(const ParGridFunction &src,
|
||||
//
|
||||
// G is identity if the partitioning matches
|
||||
|
||||
src.HostRead();
|
||||
dst.HostReadWrite();
|
||||
|
||||
z_ = 0.0;
|
||||
|
||||
for (int i = 0; i < sub2_to_parent_map_.Size(); i++)
|
||||
|
||||
@@ -152,6 +152,8 @@ void TransferMap::Transfer(const GridFunction &src,
|
||||
if (category_ == TransferCategory::ParentToSubMesh)
|
||||
{
|
||||
// dst = S1^T src
|
||||
src.HostRead();
|
||||
dst.HostWrite(); // dst is fully overwritten
|
||||
for (int i = 0; i < sub1_to_parent_map_.Size(); i++)
|
||||
{
|
||||
double s = 1.0;
|
||||
@@ -168,6 +170,8 @@ void TransferMap::Transfer(const GridFunction &src,
|
||||
//
|
||||
// G is identity if the partitioning matches
|
||||
|
||||
src.HostRead();
|
||||
dst.HostReadWrite(); // dst is only partially overwritten
|
||||
for (int i = 0; i < sub1_to_parent_map_.Size(); i++)
|
||||
{
|
||||
double s = 1.0;
|
||||
@@ -184,6 +188,9 @@ void TransferMap::Transfer(const GridFunction &src,
|
||||
//
|
||||
// G is identity if the partitioning matches
|
||||
|
||||
src.HostRead();
|
||||
dst.HostReadWrite();
|
||||
|
||||
z_ = 0.0;
|
||||
|
||||
for (int i = 0; i < sub2_to_parent_map_.Size(); i++)
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
// Contact example
|
||||
//
|
||||
// Compile with: make contact
|
||||
//
|
||||
// Sample runs: ./contact -m1 block1.mesh -m2 block2.mesh -at "5 6 7 8"
|
||||
// Sample runs: ./contact -m1 block1_d.mesh -m2 block2_d.mesh -at "5 6 7 8"
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "ipsolver/IPsolver.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file1 = "meshes/block1.mesh";
|
||||
const char *mesh_file2 = "meshes/rotatedblock2.mesh";
|
||||
int order = 1;
|
||||
int ref = 0;
|
||||
Array<int> attr;
|
||||
Array<int> m_attr;
|
||||
int linSolver = 2;
|
||||
bool paraview = false;
|
||||
bool visualization = true;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file1, "-m1", "--mesh1",
|
||||
"First mesh file to use.");
|
||||
args.AddOption(&mesh_file2, "-m2", "--mesh2",
|
||||
"Second mesh file to use.");
|
||||
args.AddOption(&attr, "-at", "--attributes-surf",
|
||||
"Attributes of boundary faces on contact surface for mesh 2.");
|
||||
args.AddOption(&ref, "-r", "--refinements",
|
||||
"Number of uniform refinements.");
|
||||
args.AddOption(¶view, "-paraview", "--paraview", "-no-paraview",
|
||||
"--no-paraview",
|
||||
"Enable or disable ParaView visualization.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
ElasticityProblem prob1(mesh_file1,ref,order);
|
||||
ElasticityProblem prob2(mesh_file2,ref,order);
|
||||
|
||||
ContactProblem contact(&prob1, &prob2);
|
||||
QPOptContactProblem qpopt(&contact);
|
||||
int numconstr = contact.GetNumConstraints();
|
||||
|
||||
InteriorPointSolver optimizer(&qpopt);
|
||||
optimizer.SetTol(1e-6);
|
||||
optimizer.SetMaxIter(50);
|
||||
optimizer.SetLinearSolver(linSolver);
|
||||
optimizer.SetLinearSolveTol(1e-10);
|
||||
|
||||
GridFunction x1 = prob1.GetDisplacementGridFunction();
|
||||
GridFunction x2 = prob2.GetDisplacementGridFunction();
|
||||
|
||||
int ndofs1 = prob1.GetNumDofs();
|
||||
int ndofs2 = prob2.GetNumDofs();
|
||||
int ndofs = ndofs1 + ndofs2;
|
||||
|
||||
Vector x0(ndofs); x0 = 0.0;
|
||||
x0.SetVector(x1,0);
|
||||
x0.SetVector(x2,x1.Size());
|
||||
|
||||
Vector xf(ndofs); xf = 0.0;
|
||||
optimizer.Mult(x0, xf);
|
||||
Array<int> & CGiterations = optimizer.GetCGIterNumbers();
|
||||
|
||||
double Einitial = contact.E(x0);
|
||||
double Efinal = contact.E(xf);
|
||||
|
||||
mfem::out << endl;
|
||||
mfem::out << " Initial Energy objective = " << Einitial << endl;
|
||||
mfem::out << " Final Energy objective = " << Efinal << endl;
|
||||
mfem::out << " Global number of dofs = " << ndofs1 + ndofs2 << endl;
|
||||
mfem::out << " Global number of constraints = " << numconstr << endl;
|
||||
mfem::out << " CG iteration numbers = " ;
|
||||
CGiterations.Print(mfem::out, CGiterations.Size());
|
||||
|
||||
MFEM_VERIFY(optimizer.GetConverged(),
|
||||
"Interior point solver did not converge.");
|
||||
|
||||
if (visualization || paraview)
|
||||
{
|
||||
FiniteElementSpace * fes1 = prob1.GetFESpace();
|
||||
FiniteElementSpace * fes2 = prob2.GetFESpace();
|
||||
|
||||
Mesh * mesh1 = fes1->GetMesh();
|
||||
Mesh * mesh2 = fes2->GetMesh();
|
||||
|
||||
GridFunction x1_gf(fes1,xf.GetData());
|
||||
GridFunction x2_gf(fes2,&xf.GetData()[fes1->GetTrueVSize()]);
|
||||
|
||||
mesh1->MoveNodes(x1_gf);
|
||||
mesh2->MoveNodes(x2_gf);
|
||||
|
||||
if (paraview)
|
||||
{
|
||||
ParaViewDataCollection paraview_dc1("QPContactBody1", mesh1);
|
||||
paraview_dc1.SetPrefixPath("ParaView");
|
||||
paraview_dc1.SetLevelsOfDetail(1);
|
||||
paraview_dc1.SetDataFormat(VTKFormat::BINARY);
|
||||
paraview_dc1.SetHighOrderOutput(true);
|
||||
paraview_dc1.SetCycle(0);
|
||||
paraview_dc1.SetTime(0.0);
|
||||
paraview_dc1.RegisterField("Body1", &x1_gf);
|
||||
paraview_dc1.Save();
|
||||
|
||||
ParaViewDataCollection paraview_dc2("QPContactBody2", mesh2);
|
||||
paraview_dc2.SetPrefixPath("ParaView");
|
||||
paraview_dc2.SetLevelsOfDetail(1);
|
||||
paraview_dc2.SetDataFormat(VTKFormat::BINARY);
|
||||
paraview_dc2.SetHighOrderOutput(true);
|
||||
paraview_dc2.SetCycle(0);
|
||||
paraview_dc2.SetTime(0.0);
|
||||
paraview_dc2.RegisterField("Body2", &x2_gf);
|
||||
paraview_dc2.Save();
|
||||
}
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
{
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "parallel " << 2 << " " << 0 << "\n"
|
||||
<< "solution\n" << *mesh1 << x1_gf << flush;
|
||||
}
|
||||
{
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "parallel " << 2 << " " << 1 << "\n"
|
||||
<< "solution\n" << *mesh2 << x2_gf << flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,818 +0,0 @@
|
||||
#include "mfem.hpp"
|
||||
#include "IPsolver.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
|
||||
InteriorPointSolver::InteriorPointSolver(QPOptContactProblem * Problem)
|
||||
: optProblem(Problem), block_offsetsumlz(5), block_offsetsuml(4), block_offsetsx(3),
|
||||
saveLogBarrierIterates(false)
|
||||
{
|
||||
rel_tol = 1.e-2;
|
||||
max_iter = 20;
|
||||
mu_k = 1.0;
|
||||
|
||||
sMax = 1.e2;
|
||||
kSig = 1.e10; // control deviation from primal Hessian
|
||||
tauMin = 0.8; // control rate at which iterates can approach the boundary
|
||||
eta = 1.e-4; // backtracking constant
|
||||
thetaMin = 1.e-4; // allowed violation of the equality constraints
|
||||
|
||||
// constants in line-step A-5.4
|
||||
delta = 1.0;
|
||||
sTheta = 1.1;
|
||||
sPhi = 2.3;
|
||||
|
||||
// control the rate at which the penalty parameter is decreased
|
||||
kMu = 0.2;
|
||||
thetaMu = 1.5;
|
||||
|
||||
// TO DO -- include the filter
|
||||
|
||||
thetaMax = 1.e6; // maximum constraint violation
|
||||
// data for the second order correction
|
||||
kSoc = 0.99;
|
||||
|
||||
// equation (18)
|
||||
gTheta = 1.e-5;
|
||||
gPhi = 1.e-5;
|
||||
|
||||
kEps = 1.e1;
|
||||
|
||||
dimU = optProblem->GetDimU();
|
||||
dimM = optProblem->GetDimM();
|
||||
dimC = optProblem->GetDimC();
|
||||
ckSoc.SetSize(dimC);
|
||||
|
||||
block_offsetsumlz[0] = 0;
|
||||
block_offsetsumlz[1] = dimU; // u
|
||||
block_offsetsumlz[2] = dimM; // m
|
||||
block_offsetsumlz[3] = dimC; // lambda
|
||||
block_offsetsumlz[4] = dimM; // zl
|
||||
block_offsetsumlz.PartialSum();
|
||||
|
||||
for(int i = 0; i < block_offsetsuml.Size(); i++) { block_offsetsuml[i] = block_offsetsumlz[i]; }
|
||||
for(int i = 0; i < block_offsetsx.Size(); i++) { block_offsetsx[i] = block_offsetsuml[i] ; }
|
||||
|
||||
// lower-bound for the inequality constraint m >= ml
|
||||
ml = optProblem->Getml();
|
||||
|
||||
lk.SetSize(dimC); lk = 0.0;
|
||||
zlk.SetSize(dimM); zlk = 0.0;
|
||||
|
||||
linSolver = 0;
|
||||
MyRank = 0;
|
||||
iAmRoot = MyRank == 0 ? true : false;
|
||||
}
|
||||
|
||||
double InteriorPointSolver::MaxStepSize(Vector &x, Vector &xl, Vector &xhat, double tau)
|
||||
{
|
||||
double alphaMaxloc = 1.0;
|
||||
double alphaTmp;
|
||||
for(int i = 0; i < x.Size(); i++)
|
||||
{
|
||||
if( xhat(i) < 0. )
|
||||
{
|
||||
alphaTmp = -1. * tau * (x(i) - xl(i)) / xhat(i);
|
||||
alphaMaxloc = min(alphaMaxloc, alphaTmp);
|
||||
}
|
||||
}
|
||||
|
||||
// alphaMaxloc is the local maximum step size which is
|
||||
// distinct on each MPI process. Need to compute
|
||||
// the global maximum step size
|
||||
double alphaMaxglb;
|
||||
alphaMaxglb = alphaMaxloc;
|
||||
return alphaMaxglb;
|
||||
}
|
||||
|
||||
double InteriorPointSolver::MaxStepSize(Vector &x, Vector &xhat, double tau)
|
||||
{
|
||||
Vector zero(x.Size()); zero = 0.0;
|
||||
return MaxStepSize(x, zero, xhat, tau);
|
||||
}
|
||||
|
||||
|
||||
void InteriorPointSolver::Mult(const Vector &x0, Vector &xf)
|
||||
{
|
||||
BlockVector x0block(block_offsetsx); x0block = 0.0;
|
||||
x0block.GetBlock(0).Set(1.0, x0);
|
||||
// To do: give options for user specificiation of initialization m0
|
||||
x0block.GetBlock(1) = 1.0;
|
||||
x0block.GetBlock(1).Add(1.0, ml);
|
||||
BlockVector xfblock(block_offsetsx); xfblock = 0.0;
|
||||
Mult(x0block, xfblock);
|
||||
xf.Set(1.0, xfblock.GetBlock(0));
|
||||
}
|
||||
|
||||
void InteriorPointSolver::Mult(const BlockVector &x0, BlockVector &xf)
|
||||
{
|
||||
converged = false;
|
||||
|
||||
BlockVector xk(block_offsetsx), xhat(block_offsetsx); xk = 0; xhat = 0.0;
|
||||
BlockVector Xk(block_offsetsumlz), Xhat(block_offsetsumlz); Xk = 0.0; Xhat = 0.0;
|
||||
BlockVector Xhatuml(block_offsetsuml); Xhatuml = 0.0;
|
||||
Vector zlhat(dimM); zlhat = 0.0;
|
||||
|
||||
xk.GetBlock(0).Set(1.0, x0.GetBlock(0));
|
||||
xk.GetBlock(1).Set(1.0, x0.GetBlock(1));
|
||||
// running estimate of the final values of the Lagrange multipliers
|
||||
lk = 0.0;
|
||||
zlk = 0.0;
|
||||
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
zlk(i) = 1.e1 * mu_k / (xk(i+dimU) - ml(i));
|
||||
}
|
||||
|
||||
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
|
||||
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
|
||||
Xk.GetBlock(2).Set(1.0, lk);
|
||||
Xk.GetBlock(3).Set(1.0, zlk);
|
||||
|
||||
/* set theta0 = theta(x0)
|
||||
* thetaMin
|
||||
* thetaMax
|
||||
* when theta(xk) < thetaMin and the switching condition holds
|
||||
* then we ask for the Armijo sufficient decrease of the barrier
|
||||
* objective to be satisfied, in order to accept the trial step length alphakl
|
||||
*
|
||||
* thetaMax controls how the filter is initialized for each log-barrier subproblem
|
||||
* F0 = {(th, phi) s.t. th > thetaMax}
|
||||
* that is the filter does not allow for iterates where the constraint violation
|
||||
* is larger than that of thetaMax
|
||||
*/
|
||||
double theta0 = theta(xk);
|
||||
thetaMin = 1.e-4 * max(1.0, theta0);
|
||||
thetaMax = 1.e8 * thetaMin;
|
||||
|
||||
double Eeval, maxBarrierSolves, Eevalmu0;
|
||||
bool printOptimalityError; // control optimality error print to console for log-barrier subproblems
|
||||
|
||||
maxBarrierSolves = 10;
|
||||
|
||||
for(jOpt = 0; jOpt < max_iter; jOpt++)
|
||||
{
|
||||
mfem::out << "interior-point solve step " << jOpt << endl;
|
||||
// A-2. Check convergence of overall optimization problem
|
||||
printOptimalityError = false;
|
||||
Eevalmu0 = E(xk, lk, zlk, printOptimalityError);
|
||||
if(Eevalmu0 < rel_tol)
|
||||
{
|
||||
converged = true;
|
||||
mfem::out << "solved optimization problem :)\n";
|
||||
break;
|
||||
}
|
||||
|
||||
if(jOpt > 0) { maxBarrierSolves = 1; }
|
||||
|
||||
for(int i = 0; i < maxBarrierSolves; i++)
|
||||
{
|
||||
// A-3. Check convergence of the barrier subproblem
|
||||
printOptimalityError = true;
|
||||
Eeval = E(xk, lk, zlk, mu_k, printOptimalityError);
|
||||
if(Eeval < kEps * mu_k)
|
||||
{
|
||||
mfem::out << "solved barrier subproblem, for mu = " << mu_k << endl;
|
||||
// A-3.1. Recompute the barrier parameter
|
||||
mu_k = max(rel_tol / 10., min(kMu * mu_k, pow(mu_k, thetaMu)));
|
||||
// A-3.2. Re-initialize the filter
|
||||
F1.DeleteAll();
|
||||
F2.DeleteAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// A-4. Compute the search direction
|
||||
// solve for (uhat, mhat, lhat)
|
||||
mfem::out << "\n** A-4. IP-Newton solve **\n";
|
||||
zlhat = 0.0; Xhatuml = 0.0;
|
||||
// why do we have Xhatuml ....???
|
||||
// TO DO: remove Xhatuml in favor of passing Xhat
|
||||
IPNewtonSolve(xk, lk, zlk, zlhat, Xhatuml, mu_k, false);
|
||||
|
||||
// assign data stack, X = (u, m, l, zl)
|
||||
Xk = 0.0;
|
||||
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
|
||||
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
|
||||
Xk.GetBlock(2).Set(1.0, lk);
|
||||
Xk.GetBlock(3).Set(1.0, zlk);
|
||||
|
||||
// assign data stack, Xhat = (uhat, mhat, lhat, zlhat)
|
||||
Xhat = 0.0;
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
Xhat.GetBlock(i).Set(1.0, Xhatuml.GetBlock(i));
|
||||
}
|
||||
Xhat.GetBlock(3).Set(1.0, zlhat);
|
||||
|
||||
// A-5. Backtracking line search.
|
||||
mfem::out << "\n** A-5. Linesearch **\n";
|
||||
mfem::out << "mu = " << mu_k << endl;
|
||||
|
||||
lineSearch(Xk, Xhat, mu_k);
|
||||
if(lineSearchSuccess)
|
||||
{
|
||||
if(!switchCondition || !sufficientDecrease)
|
||||
{
|
||||
F1.Append( (1. - gTheta) * thx0);
|
||||
F2.Append( phx0 - gPhi * thx0);
|
||||
}
|
||||
// ----- A-6: Accept the trial point
|
||||
// print info regarding zl...
|
||||
xk.GetBlock(0).Add(alpha, Xhat.GetBlock(0));
|
||||
xk.GetBlock(1).Add(alpha, Xhat.GetBlock(1));
|
||||
lk.Add(alpha, Xhat.GetBlock(2));
|
||||
zlk.Add(alphaz, Xhat.GetBlock(3));
|
||||
projectZ(xk, zlk, mu_k);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem::out << "lineSearch not successful :(\n";
|
||||
mfem::out << "attempting feasibility restoration with theta = " << thx0 << endl;
|
||||
mfem::out << "no feasibility restoration implemented, exiting now \n";
|
||||
break;
|
||||
}
|
||||
//
|
||||
if(jOpt + 1 == max_iter)
|
||||
{
|
||||
mfem::out << "maximum optimization iterations :(\n";
|
||||
}
|
||||
}
|
||||
// done with optimization routine, just reassign data to xf reference so
|
||||
// that the application code has access to the optimal point
|
||||
xf = 0.0;
|
||||
xf.GetBlock(0).Set(1.0, xk.GetBlock(0));
|
||||
xf.GetBlock(1).Set(1.0, xk.GetBlock(1));
|
||||
}
|
||||
|
||||
void InteriorPointSolver::FormIPNewtonMat(BlockVector & x, Vector & l, Vector &zl, BlockOperator &Ak)
|
||||
{
|
||||
// WARNING: Huu, Hum, Hmu, Hmm should all be Hessian terms of the Lagrangian, currently we
|
||||
// them by Hessian terms of the objective function and neglect the Hessian of l^T c
|
||||
|
||||
Huu = optProblem->Duuf(x);
|
||||
Hum = optProblem->Dumf(x);
|
||||
Hmu = optProblem->Dmuf(x);
|
||||
Hmm = optProblem->Dmmf(x);
|
||||
|
||||
Vector DiagLogBar(dimM); DiagLogBar = 0.0;
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
DiagLogBar(ii) = zl(ii) / (x(ii+dimU) - ml(ii));
|
||||
}
|
||||
|
||||
if(saveLogBarrierIterates)
|
||||
{
|
||||
std::ofstream diagStream;
|
||||
char diagString[100];
|
||||
snprintf(diagString, 100, "logBarrierHessiandata/D%d.dat", jOpt);
|
||||
diagStream.open(diagString, ios::out | ios::trunc);
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
diagStream << setprecision(30) << DiagLogBar(ii) << endl;
|
||||
}
|
||||
diagStream.close();
|
||||
}
|
||||
|
||||
delete Wmm;
|
||||
if(Hmm != nullptr)
|
||||
{
|
||||
SparseMatrix * D = new SparseMatrix(DiagLogBar);
|
||||
Wmm = Add(*Hmm, *D);
|
||||
delete D;
|
||||
}
|
||||
else
|
||||
{
|
||||
Wmm = new SparseMatrix(DiagLogBar);
|
||||
}
|
||||
|
||||
delete JuT;
|
||||
delete JmT;
|
||||
Ju = optProblem->Duc(x); JuT = Transpose(*Ju);
|
||||
Jm = optProblem->Dmc(x); JmT = Transpose(*Jm);
|
||||
|
||||
Huucl = optProblem->lDuuc(x, l);
|
||||
if(Huucl != nullptr)
|
||||
{
|
||||
delete HLuucl;
|
||||
HLuucl = Add(*Huucl, *Huu);
|
||||
Ak.SetBlock(0, 0, HLuucl);
|
||||
}
|
||||
else
|
||||
{
|
||||
Ak.SetBlock(0, 0, Huu);
|
||||
}
|
||||
|
||||
// IP-Newton system matrix
|
||||
// Ak = [[H_(u,u) H_(u,m) J_u^T]
|
||||
// [H_(m,u) W_(m,m) J_m^T]
|
||||
// [ J_u J_m 0 ]]
|
||||
|
||||
Ak.SetBlock(0, 0, Huu);
|
||||
Ak.SetBlock(0, 2, JuT);
|
||||
Ak.SetBlock(1, 1, Wmm);
|
||||
Ak.SetBlock(1, 2, JmT);
|
||||
Ak.SetBlock(2, 0, Ju);
|
||||
Ak.SetBlock(2, 1, Jm);
|
||||
|
||||
if(Hum != nullptr) { Ak.SetBlock(0, 1, Hum); Ak.SetBlock(1, 0, Hmu); }
|
||||
}
|
||||
|
||||
// perturbed KKT system solve
|
||||
// determine the search direction
|
||||
void InteriorPointSolver::IPNewtonSolve(BlockVector &x, Vector &l, Vector &zl, Vector &zlhat, BlockVector &Xhat, double mu, bool socSolve)
|
||||
{
|
||||
// solve A x = b, where A is the IP-Newton matrix
|
||||
BlockOperator A(block_offsetsuml, block_offsetsuml); BlockVector b(block_offsetsuml); b = 0.0;
|
||||
FormIPNewtonMat(x, l, zl, A);
|
||||
|
||||
// [grad_u phi + Ju^T l]
|
||||
// b = - [grad_m phi + Jm^T l]
|
||||
// [ c ]
|
||||
BlockVector gradphi(block_offsetsx); gradphi = 0.0;
|
||||
BlockVector JTl(block_offsetsx); JTl = 0.0;
|
||||
Dxphi(x, mu, gradphi);
|
||||
|
||||
(A.GetBlock(0,2)).Mult(l, JTl.GetBlock(0));
|
||||
(A.GetBlock(1,2)).Mult(l, JTl.GetBlock(1));
|
||||
|
||||
|
||||
for(int ii = 0; ii < 2; ii++)
|
||||
{
|
||||
b.GetBlock(ii).Set(1.0, gradphi.GetBlock(ii));
|
||||
b.GetBlock(ii).Add(1.0, JTl.GetBlock(ii));
|
||||
}
|
||||
if(!socSolve)
|
||||
{
|
||||
optProblem->c(x, b.GetBlock(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
b.GetBlock(2).Set(1.0, ckSoc);
|
||||
}
|
||||
b *= -1.0;
|
||||
Xhat = 0.0;
|
||||
|
||||
|
||||
#ifdef MFEM_USE_SUITESPARSE
|
||||
// Direct solve for IP-Newton saddle-point system
|
||||
// A = [ [ Huu 0 Ju^T]
|
||||
// [ 0 D -I ]
|
||||
// [ Ju -I 0 ]]
|
||||
// if(linSolver == 0)
|
||||
// {
|
||||
// BlockMatrix ABlockMatrix(block_offsetsuml, block_offsetsuml);
|
||||
// for(int ii = 0; ii < 3; ii++)
|
||||
// {
|
||||
// for(int jj = 0; jj < 3; jj++)
|
||||
// {
|
||||
// if(!A.IsZeroBlock(ii, jj))
|
||||
// {
|
||||
// ABlockMatrix.SetBlock(ii, jj, dynamic_cast<SparseMatrix *>(&(A.GetBlock(ii, jj))));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// /* direct solve of the 3x3 IP-Newton linear system */
|
||||
// UMFPackSolver ASolver;
|
||||
// SparseMatrix *ASparse = ABlockMatrix.CreateMonolithic();
|
||||
// ASolver.SetOperator(*ASparse);
|
||||
// ASolver.Mult(b, Xhat);
|
||||
|
||||
// Vector residual(Xhat.Size());
|
||||
// ASparse->Mult(Xhat, residual);
|
||||
// residual.Add(-1.0, b);
|
||||
// delete ASparse;
|
||||
// }
|
||||
// else if(linSolver == 1)
|
||||
// {
|
||||
// // Direct solve for 0,0 Schur complement of IP-Newton system, Huu + Ju^T Wmm Ju,
|
||||
// // where Wmm = D for contact problems
|
||||
// SparseMatrix * Huuloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 0)));
|
||||
// SparseMatrix * Wmmloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(1, 1)));
|
||||
// SparseMatrix * Juloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(2, 0)));
|
||||
// SparseMatrix * JuTloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 2)));
|
||||
// Vector DVec(dimM); DVec = 0.0;
|
||||
// Vector one(dimM); one = 1.0;
|
||||
// D->Mult(one, DVec);
|
||||
// SparseMatrix *JuTDJu = Mult_AtDA(*Juloc, DVec); // Ju^T D Ju
|
||||
// SparseMatrix *Areduced = Add(*Huuloc, *JuTDJu); // Huu + Ju^T D Ju
|
||||
|
||||
// /* prepare the reduced rhs */
|
||||
// // breduced = bu + Ju^T (bm + Wmm bl)
|
||||
// Vector breduced(dimU); breduced = 0.0;
|
||||
// Vector tempVec(dimM); tempVec = 0.0;
|
||||
// Wmmloc->Mult(b.GetBlock(2), tempVec);
|
||||
// tempVec.Add(1.0, b.GetBlock(1));
|
||||
// JuTloc->Mult(tempVec, breduced);
|
||||
// breduced.Add(1.0, b.GetBlock(0));
|
||||
|
||||
// // solve the reduced linear system
|
||||
// UMFPackSolver AreducedSolver;
|
||||
// AreducedSolver.SetOperator(*Areduced);
|
||||
// AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
|
||||
|
||||
// // now propagate solved uhat to obtain mhat and lhat
|
||||
// // xm = Ju xu - bl
|
||||
// Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
|
||||
// Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
|
||||
|
||||
// // xl = Wmm xm - bm
|
||||
// Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
|
||||
// Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
|
||||
|
||||
// delete JuTDJu;
|
||||
// delete Areduced;
|
||||
// }
|
||||
#else
|
||||
MFEM_VERIFY(linSolver > 1, "linSolver = 0, 1 require MFEM_USE_SUITESPARSE=YES");
|
||||
#endif
|
||||
// if(linSolver ==2)
|
||||
{
|
||||
// Iterative solve for 0,0 Schur complement of IP-Newton system, Huu + Ju^T Wmm Ju,
|
||||
// where Wmm = D for contact problems
|
||||
// here the iterative solver is a Jacobi-preconditioned CG-solve
|
||||
SparseMatrix * Huuloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 0)));
|
||||
SparseMatrix * Wmmloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(1, 1)));
|
||||
SparseMatrix * Juloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(2, 0)));
|
||||
SparseMatrix * JuTloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 2)));
|
||||
|
||||
SparseMatrix *JuTDJu = RAP(*Juloc,*Wmmloc,*Juloc); // Ju^T D Ju
|
||||
SparseMatrix *Areduced = Add(*Huuloc, *JuTDJu); // Huu + Ju^T D Ju
|
||||
|
||||
/* prepare the reduced rhs */
|
||||
// breduced = bu + Ju^T (bm + Wmm bl)
|
||||
Vector breduced(dimU); breduced = 0.0;
|
||||
Vector tempVec(dimM); tempVec = 0.0;
|
||||
Wmmloc->SortColumnIndices();
|
||||
|
||||
Wmmloc->Mult(b.GetBlock(2), tempVec);
|
||||
tempVec.Add(1.0, b.GetBlock(1));
|
||||
JuTloc->Mult(tempVec, breduced);
|
||||
|
||||
|
||||
breduced.Add(1.0, b.GetBlock(0));
|
||||
int globalNumRows = dimU;
|
||||
HYPRE_BigInt rowStarts[2];
|
||||
rowStarts[0] = 0;
|
||||
rowStarts[1] = dimU;
|
||||
|
||||
HypreParMatrix Ahypre(MPI_COMM_WORLD, globalNumRows, rowStarts, Areduced);
|
||||
HypreBoomerAMG Aprec(Ahypre);
|
||||
Aprec.SetPrintLevel(0);
|
||||
Aprec.SetSystemsOptions(3,false);
|
||||
HyprePCG AreducedSolver(MPI_COMM_WORLD);
|
||||
AreducedSolver.SetOperator(Ahypre);
|
||||
// AreducedSolver.SetRelTol(linSolveTol);
|
||||
// AreducedSolver.SetRelTol(1e-6);
|
||||
AreducedSolver.SetTol(1e-6);
|
||||
AreducedSolver.SetMaxIter(1000);
|
||||
AreducedSolver.SetPreconditioner(Aprec);
|
||||
// AreducedSolver.SetResidualConvergenceOptions();
|
||||
AreducedSolver.SetPrintLevel(2);
|
||||
|
||||
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
|
||||
int num_iterations;
|
||||
AreducedSolver.GetNumIterations(num_iterations);
|
||||
cgnum_iterations.Append(num_iterations);
|
||||
|
||||
// now propagate solved uhat to obtain mhat and lhat
|
||||
// xm = Ju xu - bl
|
||||
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
|
||||
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
|
||||
// xl = Wmm xm - bm
|
||||
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
|
||||
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
|
||||
|
||||
delete JuTDJu;
|
||||
delete Areduced;
|
||||
}
|
||||
|
||||
/* backsolve to determine zlhat */
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
zlhat(ii) = -1.*(zl(ii) + (zl(ii) * Xhat(ii + dimU) - mu) / (x(ii + dimU) - ml(ii)) );
|
||||
}
|
||||
}
|
||||
|
||||
// here Xhat, X will be BlockVectors w.r.t. the 4 partitioning X = (u, m, l, zl)
|
||||
|
||||
void InteriorPointSolver::lineSearch(BlockVector& X0, BlockVector& Xhat, double mu)
|
||||
{
|
||||
double tau = max(tauMin, 1.0 - mu);
|
||||
Vector u0 = X0.GetBlock(0);
|
||||
Vector m0 = X0.GetBlock(1);
|
||||
Vector l0 = X0.GetBlock(2);
|
||||
Vector z0 = X0.GetBlock(3);
|
||||
Vector uhat = Xhat.GetBlock(0);
|
||||
Vector mhat = Xhat.GetBlock(1);
|
||||
Vector lhat = Xhat.GetBlock(2);
|
||||
Vector zhat = Xhat.GetBlock(3);
|
||||
double alphaMax = MaxStepSize(m0, ml, mhat, tau);
|
||||
double alphaMaxz = MaxStepSize(z0, zhat, tau);
|
||||
alphaz = alphaMaxz;
|
||||
|
||||
|
||||
BlockVector x0(block_offsetsx); x0 = 0.0;
|
||||
x0.GetBlock(0).Set(1.0, u0);
|
||||
x0.GetBlock(1).Set(1.0, m0);
|
||||
|
||||
BlockVector xhat(block_offsetsx); xhat = 0.0;
|
||||
xhat.GetBlock(0).Set(1.0, uhat);
|
||||
xhat.GetBlock(1).Set(1.0, mhat);
|
||||
|
||||
BlockVector xtrial(block_offsetsx); xtrial = 0.0;
|
||||
BlockVector Dxphi0(block_offsetsx); Dxphi0 = 0.0;
|
||||
int maxBacktrack = 20;
|
||||
alpha = alphaMax;
|
||||
|
||||
|
||||
Vector ck0(dimC); ck0 = 0.0;
|
||||
Vector zhatsoc(dimM); zhatsoc = 0.0;
|
||||
BlockVector Xhatumlsoc(block_offsetsuml); Xhatumlsoc = 0.0;
|
||||
BlockVector xhatsoc(block_offsetsx); xhatsoc = 0.0;
|
||||
Vector uhatsoc(dimU); uhatsoc = 0.0;
|
||||
Vector mhatsoc(dimM); mhatsoc = 0.0;
|
||||
|
||||
Dxphi(x0, mu, Dxphi0);
|
||||
Dxphi0_xhat = InnerProduct(Dxphi0, xhat);
|
||||
descentDirection = Dxphi0_xhat < 0. ? true : false;
|
||||
if(descentDirection)
|
||||
{
|
||||
mfem::out << "is a descent direction for the log-barrier objective\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem::out << "is not a descent direction for the log-barrier objective\n";
|
||||
}
|
||||
mfem::out << "Dxphi^T xhat / (|| Dxphi||_2 * || xhat ||_2) = " << Dxphi0_xhat / (xhat.Norml2() * Dxphi0.Norml2()) << endl;
|
||||
thx0 = theta(x0);
|
||||
phx0 = phi(x0, mu);
|
||||
|
||||
lineSearchSuccess = false;
|
||||
for(int i = 0; i < maxBacktrack; i++)
|
||||
{
|
||||
mfem::out << "\n--------- alpha = " << alpha << " ---------\n";
|
||||
|
||||
// ----- A-5.2. Compute trial point: xtrial = x0 + alpha_i xhat
|
||||
xtrial.Set(1.0, x0);
|
||||
xtrial.Add(alpha, xhat);
|
||||
|
||||
// ------ A-5.3. if not in filter region go to A.5.4 otherwise go to A-5.5.
|
||||
thxtrial = theta(xtrial);
|
||||
phxtrial = phi(xtrial, mu);
|
||||
|
||||
filterCheck(thxtrial, phxtrial);
|
||||
if(!inFilterRegion)
|
||||
{
|
||||
mfem::out << "not in filter region :)\n";
|
||||
// ------ A.5.4: Check sufficient decrease
|
||||
if(!descentDirection)
|
||||
{
|
||||
switchCondition = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
switchCondition = (alpha * pow(abs(Dxphi0_xhat), sPhi) > delta * pow(thx0, sTheta)) ? true : false;
|
||||
}
|
||||
mfem::out << "theta(x0) = " << thx0 << ", thetaMin = " << thetaMin << endl;
|
||||
mfem::out << "theta(xtrial) = " << thxtrial << ", (1-gTheta) *theta(x0) = " << (1. - gTheta) * thx0 << endl;
|
||||
mfem::out << "phi(xtrial) = " << phxtrial << ", phi(x0) - gPhi *theta(x0) = " << phx0 - gPhi * thx0 << endl;
|
||||
|
||||
// Case I
|
||||
if(thx0 <= thetaMin && switchCondition)
|
||||
{
|
||||
sufficientDecrease = phxtrial <= phx0 + eta * alpha * Dxphi0_xhat ? true : false;
|
||||
if(sufficientDecrease)
|
||||
{
|
||||
mfem::out << "Accepted step length -- sufficient decrease in log-barrier objective.\n";
|
||||
// accept the trial step
|
||||
lineSearchSuccess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(thxtrial <= (1. - gTheta) * thx0 || phxtrial <= phx0 - gPhi * thx0)
|
||||
{
|
||||
mfem::out << "Accepted step length -- decrease in either constraint violation or log-barrier objective.\n";
|
||||
// accept the trial step
|
||||
lineSearchSuccess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// A-5.5: Initialize the second-order correction
|
||||
if((!(thx0 < thxtrial)) && i == 0)
|
||||
{
|
||||
mfem::out << "second order correction\n";
|
||||
optProblem->c(xtrial, ckSoc);
|
||||
optProblem->c(x0, ck0);
|
||||
ckSoc.Add(alphaMax, ck0);
|
||||
// A-5.6 Compute the second-order correction.
|
||||
IPNewtonSolve(x0, l0, z0, zhatsoc, Xhatumlsoc, mu, true);
|
||||
mhatsoc.Set(1.0, Xhatumlsoc.GetBlock(1));
|
||||
// alphasoc = MaxStepSize(m0, ml, mhatsoc, tau);
|
||||
//WARNING: not complete but currently solver isn't entering this region
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem::out << "in filter region\n";
|
||||
}
|
||||
|
||||
// include more if needed
|
||||
alpha *= 0.5;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void InteriorPointSolver::projectZ(const Vector &x, Vector &z, double mu)
|
||||
{
|
||||
double zi;
|
||||
double mudivmml;
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
zi = z(i);
|
||||
mudivmml = mu / (x(i + dimU) - ml(i));
|
||||
z(i) = max(min(zi, kSig * mudivmml), mudivmml / kSig);
|
||||
}
|
||||
}
|
||||
|
||||
void InteriorPointSolver::filterCheck(double th, double ph)
|
||||
{
|
||||
inFilterRegion = false;
|
||||
if(th > thetaMax)
|
||||
{
|
||||
inFilterRegion = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < F1.Size(); i++)
|
||||
{
|
||||
if(th >= F1[i] && ph >= F2[i])
|
||||
{
|
||||
inFilterRegion = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double InteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, double mu, bool print)
|
||||
{
|
||||
double E1, E2, E3;
|
||||
double sc, sd;
|
||||
BlockVector gradL(block_offsetsx); gradL = 0.0; // stationarity grad L = grad f + J^T l - z
|
||||
Vector cx(dimC); cx = 0.0; // feasibility c = c(x)
|
||||
Vector comp(dimM); comp = 0.0; // complementarity M Z - mu 1
|
||||
|
||||
DxL(x, l, zl, gradL);
|
||||
E1 = gradL.Normlinf();
|
||||
|
||||
optProblem->c(x, cx);
|
||||
E2 = cx.Normlinf();
|
||||
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
comp(ii) = x(dimU + ii) * zl(ii) - mu;
|
||||
}
|
||||
E3 = comp.Normlinf();
|
||||
|
||||
double ll1, zl1;
|
||||
zl1 = zl.Norml1() / double(dimC + dimM);
|
||||
ll1 = l.Norml1();
|
||||
sc = max(sMax, zl1 / (double(dimM)) ) / sMax;
|
||||
sd = max(sMax, (ll1 + zl1) / (double(dimC + dimM))) / sMax;
|
||||
if(print)
|
||||
{
|
||||
mfem::out << "evaluating optimality error for mu = " << mu << endl;
|
||||
mfem::out << "stationarity measure = " << E1 / sd << endl;
|
||||
mfem::out << "feasibility measure = " << E2 << endl;
|
||||
mfem::out << "complimentarity measure = " << E3 / sc << endl;
|
||||
}
|
||||
return max(max(E1 / sd, E2), E3 / sc);
|
||||
}
|
||||
|
||||
double InteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, bool print)
|
||||
{
|
||||
return E(x, l, zl, 0.0, print);
|
||||
}
|
||||
|
||||
double InteriorPointSolver::theta(const BlockVector &x)
|
||||
{
|
||||
Vector cx(dimC); cx = 0.0;
|
||||
optProblem->c(x, cx);
|
||||
return cx.Norml2();
|
||||
}
|
||||
|
||||
// log-barrier objective
|
||||
double InteriorPointSolver::phi(const BlockVector &x, double mu)
|
||||
{
|
||||
double fx = optProblem->CalcObjective(x);
|
||||
double logBarrierLoc = 0.0;
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
logBarrierLoc += log(x(dimU+i)-ml(i));
|
||||
}
|
||||
double logBarrierGlb = 0.0;
|
||||
logBarrierGlb = logBarrierLoc;
|
||||
return fx - mu * logBarrierGlb;
|
||||
}
|
||||
|
||||
// gradient of log-barrier objective with respect to x = (u, m)
|
||||
void InteriorPointSolver::Dxphi(const BlockVector &x, double mu, BlockVector &y)
|
||||
{
|
||||
optProblem->CalcObjectiveGrad(x, y);
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
y(dimU + i) -= mu / (x(dimU + i));
|
||||
}
|
||||
}
|
||||
|
||||
// Lagrangian function evaluation
|
||||
// L(x, l, zl) = f(x) + l^T c(x) - zl^T m
|
||||
double InteriorPointSolver::L(const BlockVector &x, const Vector &l, const Vector &zl)
|
||||
{
|
||||
double fx = optProblem->CalcObjective(x);
|
||||
Vector cx(dimC); optProblem->c(x, cx);
|
||||
return (fx + InnerProduct(cx, l) - InnerProduct(x.GetBlock(1), zl));
|
||||
}
|
||||
|
||||
void InteriorPointSolver::DxL(const BlockVector &x, const Vector &l, const Vector &zl, BlockVector &y)
|
||||
{
|
||||
// evaluate the gradient of the objective with respect to the primal variables x = (u, m)
|
||||
BlockVector gradxf(block_offsetsx); gradxf = 0.0;
|
||||
optProblem->CalcObjectiveGrad(x, gradxf);
|
||||
|
||||
SparseMatrix *Jacu, *Jacm, *JacuT, *JacmT;
|
||||
Jacu = optProblem->Duc(x); Jacm = optProblem->Dmc(x);
|
||||
JacuT = Transpose(*Jacu);
|
||||
JacmT = Transpose(*Jacm);
|
||||
JacuT->Mult(l, y.GetBlock(0));
|
||||
JacmT->Mult(l, y.GetBlock(1));
|
||||
delete JacuT;
|
||||
delete JacmT;
|
||||
y.Add(1.0, gradxf);
|
||||
(y.GetBlock(1)).Add(-1.0, zl);
|
||||
}
|
||||
|
||||
|
||||
bool InteriorPointSolver::GetConverged() const
|
||||
{
|
||||
return converged;
|
||||
}
|
||||
|
||||
void InteriorPointSolver::SetTol(double Tol)
|
||||
{
|
||||
rel_tol = Tol;
|
||||
}
|
||||
|
||||
void InteriorPointSolver::SetMaxIter(int max_it)
|
||||
{
|
||||
max_iter = max_it;
|
||||
}
|
||||
|
||||
void InteriorPointSolver::SetBarrierParameter(double mu_0)
|
||||
{
|
||||
mu_k = mu_0;
|
||||
}
|
||||
|
||||
void InteriorPointSolver::SaveLogBarrierHessianIterates(bool save)
|
||||
{
|
||||
MFEM_ASSERT(MyRank == 0 || save == false, "currently can only save logbarrier hessian in serial codes");
|
||||
saveLogBarrierIterates = save;
|
||||
}
|
||||
|
||||
void InteriorPointSolver::SetLinearSolver(int LinSolver)
|
||||
{
|
||||
linSolver = LinSolver;
|
||||
}
|
||||
|
||||
void InteriorPointSolver::SetLinearSolveTol(double Tol)
|
||||
{
|
||||
linSolveTol = Tol;
|
||||
}
|
||||
|
||||
|
||||
InteriorPointSolver::~InteriorPointSolver()
|
||||
{
|
||||
delete HLuucl;
|
||||
delete JuT;
|
||||
delete JmT;
|
||||
delete Wmm;
|
||||
|
||||
F1.DeleteAll();
|
||||
F2.DeleteAll();
|
||||
block_offsetsx.DeleteAll();
|
||||
block_offsetsumlz.DeleteAll();
|
||||
block_offsetsuml.DeleteAll();
|
||||
ml.SetSize(0);
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
#include "mfem.hpp"
|
||||
#include "../problems/problems.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
#ifndef IPSOLVER
|
||||
#define IPSOLVER
|
||||
|
||||
class InteriorPointSolver
|
||||
{
|
||||
protected:
|
||||
QPOptContactProblem * optProblem;
|
||||
double rel_tol;
|
||||
int max_iter;
|
||||
double mu_k; // \mu_k
|
||||
Vector lk, zlk;
|
||||
|
||||
double sMax, kSig, tauMin, eta, thetaMin, delta, sTheta, sPhi, kMu, thetaMu;
|
||||
double thetaMax, kSoc, gTheta, gPhi, kEps;
|
||||
|
||||
// filter
|
||||
Array<double> F1, F2;
|
||||
|
||||
// quantities computed in lineSearch
|
||||
double alpha, alphaz;
|
||||
double thx0, thxtrial;
|
||||
double phx0, phxtrial;
|
||||
bool descentDirection, switchCondition, sufficientDecrease, lineSearchSuccess, inFilterRegion;
|
||||
double Dxphi0_xhat;
|
||||
|
||||
int dimU, dimM, dimC;
|
||||
Array<int> block_offsetsumlz, block_offsetsuml, block_offsetsx;
|
||||
Vector ml;
|
||||
|
||||
Vector ckSoc;
|
||||
SparseMatrix * Huu = nullptr;
|
||||
SparseMatrix * Hum = nullptr;
|
||||
SparseMatrix * Hmu = nullptr;
|
||||
SparseMatrix * Hmm = nullptr;
|
||||
SparseMatrix * Wmm = nullptr;
|
||||
SparseMatrix * Ju = nullptr;
|
||||
SparseMatrix * Jm = nullptr;
|
||||
SparseMatrix * JmT = nullptr;
|
||||
SparseMatrix * JuT = nullptr;
|
||||
SparseMatrix * Huucl = nullptr;
|
||||
SparseMatrix * HLuucl = nullptr;
|
||||
|
||||
int jOpt;
|
||||
bool converged;
|
||||
|
||||
int MyRank;
|
||||
bool iAmRoot;
|
||||
|
||||
bool saveLogBarrierIterates;
|
||||
|
||||
int linSolver;
|
||||
double linSolveTol;
|
||||
Array<int> cgnum_iterations;
|
||||
|
||||
public:
|
||||
InteriorPointSolver(QPOptContactProblem*);
|
||||
void Mult(const BlockVector& , BlockVector&); // used when the user wants to be aware of bound-constrained variable m >= ml
|
||||
void Mult(const Vector&, Vector &); // useful when the user doesn't need to know about bound-constrained variable m >= ml, e.g., when m is a slack variable
|
||||
double MaxStepSize(Vector& , Vector& , Vector& , double);
|
||||
double MaxStepSize(Vector& , Vector& , double);
|
||||
void FormIPNewtonMat(BlockVector& , Vector& , Vector& , BlockOperator &);
|
||||
void IPNewtonSolve(BlockVector& , Vector& , Vector& , Vector&, BlockVector& , double, bool);
|
||||
void lineSearch(BlockVector& , BlockVector& , double);
|
||||
void projectZ(const Vector & , Vector &, double);
|
||||
void filterCheck(double, double);
|
||||
double E(const BlockVector &, const Vector &, const Vector &, double, bool);
|
||||
double E(const BlockVector &, const Vector &, const Vector &, bool);
|
||||
Array<int> & GetCGIterNumbers() {return cgnum_iterations;}
|
||||
bool GetConverged() const;
|
||||
// TO DO: include Hessian of Lagrangian
|
||||
double theta(const BlockVector &);
|
||||
double phi(const BlockVector &, double);
|
||||
void Dxphi(const BlockVector &, double, BlockVector &);
|
||||
double L(const BlockVector &, const Vector &, const Vector &);
|
||||
void DxL(const BlockVector &, const Vector &, const Vector &, BlockVector &);
|
||||
void SetTol(double);
|
||||
void SetMaxIter(int);
|
||||
void SetBarrierParameter(double);
|
||||
void SaveLogBarrierHessianIterates(bool);
|
||||
void SetLinearSolver(int);
|
||||
void SetLinearSolveTol(double);
|
||||
virtual ~InteriorPointSolver();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,864 +0,0 @@
|
||||
#include "mfem.hpp"
|
||||
#include "ParIPsolver.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
ParInteriorPointSolver::ParInteriorPointSolver(QPOptParContactProblem * problem_)
|
||||
: problem(problem_)
|
||||
{
|
||||
OptTol = 1.e-2;
|
||||
max_iter = 20;
|
||||
mu_k = 1.0;
|
||||
|
||||
sMax = 1.e2;
|
||||
kSig = 1.e10; // control deviation from primal Hessian
|
||||
tauMin = 0.8; // control rate at which iterates can approach the boundary
|
||||
eta = 1.e-4; // backtracking constant
|
||||
thetaMin = 1.e-4; // allowed violation of the equality constraints
|
||||
|
||||
// constants in line-step A-5.4
|
||||
delta = 1.0;
|
||||
sTheta = 1.1;
|
||||
sPhi = 2.3;
|
||||
|
||||
// control the rate at which the penalty parameter is decreased
|
||||
kMu = 0.2;
|
||||
thetaMu = 1.5;
|
||||
|
||||
thetaMax = 1.e6; // maximum constraint violation
|
||||
// data for the second order correction
|
||||
kSoc = 0.99;
|
||||
|
||||
// equation (18)
|
||||
gTheta = 1.e-5;
|
||||
gPhi = 1.e-5;
|
||||
|
||||
kEps = 1.e1;
|
||||
|
||||
dimU = problem->GetDimU();
|
||||
dimM = problem->GetDimM();
|
||||
dimC = problem->GetDimC();
|
||||
|
||||
MPI_Allreduce(&dimU,&gdimU,1,MPI_INT,MPI_SUM,problem->GetComm());
|
||||
MPI_Allreduce(&dimM,&gdimM,1,MPI_INT,MPI_SUM,problem->GetComm());
|
||||
MPI_Allreduce(&dimC,&gdimC,1,MPI_INT,MPI_SUM,problem->GetComm());
|
||||
|
||||
ckSoc.SetSize(dimC);
|
||||
|
||||
block_offsetsumlz.SetSize(5);
|
||||
block_offsetsuml.SetSize(4);
|
||||
block_offsetsx.SetSize(3);
|
||||
|
||||
block_offsetsumlz[0] = 0;
|
||||
block_offsetsumlz[1] = dimU; // u
|
||||
block_offsetsumlz[2] = dimM; // m
|
||||
block_offsetsumlz[3] = dimC; // lambda
|
||||
block_offsetsumlz[4] = dimM; // zl
|
||||
block_offsetsumlz.PartialSum();
|
||||
|
||||
for(int i = 0; i < block_offsetsuml.Size(); i++)
|
||||
{
|
||||
block_offsetsuml[i] = block_offsetsumlz[i];
|
||||
}
|
||||
for(int i = 0; i < block_offsetsx.Size(); i++)
|
||||
{
|
||||
block_offsetsx[i] = block_offsetsuml[i] ;
|
||||
}
|
||||
|
||||
ml = problem->Getml();
|
||||
|
||||
lk.SetSize(dimC); lk = 0.0;
|
||||
zlk.SetSize(dimM); zlk = 0.0;
|
||||
|
||||
linSolver = 0;
|
||||
linSolveTol = 1.e-8;
|
||||
MyRank = Mpi::WorldRank();
|
||||
iAmRoot = MyRank == 0 ? true : false;
|
||||
}
|
||||
|
||||
double ParInteriorPointSolver::MaxStepSize(Vector &x, Vector &xl, Vector &xhat, double tau)
|
||||
{
|
||||
double alphaMaxloc = 1.0;
|
||||
double alphaTmp;
|
||||
for(int i = 0; i < x.Size(); i++)
|
||||
{
|
||||
if( xhat(i) < 0. )
|
||||
{
|
||||
alphaTmp = -1. * tau * (x(i) - xl(i)) / xhat(i);
|
||||
alphaMaxloc = min(alphaMaxloc, alphaTmp);
|
||||
}
|
||||
}
|
||||
|
||||
// alphaMaxloc is the local maximum step size which is
|
||||
// distinct on each MPI process. Need to compute
|
||||
// the global maximum step size
|
||||
double alphaMaxglb;
|
||||
MPI_Allreduce(&alphaMaxloc, &alphaMaxglb, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
|
||||
return alphaMaxglb;
|
||||
}
|
||||
|
||||
double ParInteriorPointSolver::MaxStepSize(Vector &x, Vector &xhat, double tau)
|
||||
{
|
||||
Vector zero(x.Size()); zero = 0.0;
|
||||
return MaxStepSize(x, zero, xhat, tau);
|
||||
}
|
||||
|
||||
|
||||
void ParInteriorPointSolver::Mult(const Vector &x0, Vector &xf)
|
||||
{
|
||||
BlockVector x0block(block_offsetsx); x0block = 0.0;
|
||||
x0block.GetBlock(0).Set(1.0, x0);
|
||||
x0block.GetBlock(1) = 1.0;
|
||||
x0block.GetBlock(1).Add(1.0, ml);
|
||||
BlockVector xfblock(block_offsetsx); xfblock = 0.0;
|
||||
|
||||
Mult(x0block, xfblock);
|
||||
xf.Set(1.0, xfblock.GetBlock(0));
|
||||
}
|
||||
|
||||
|
||||
void ParInteriorPointSolver::Mult(const BlockVector &x0, BlockVector &xf)
|
||||
{
|
||||
converged = false;
|
||||
|
||||
BlockVector xk(block_offsetsx), xhat(block_offsetsx); xk = 0; xhat = 0.0;
|
||||
BlockVector Xk(block_offsetsumlz), Xhat(block_offsetsumlz); Xk = 0.0; Xhat = 0.0;
|
||||
BlockVector Xhatuml(block_offsetsuml); Xhatuml = 0.0;
|
||||
Vector zlhat(dimM); zlhat = 0.0;
|
||||
|
||||
xk.GetBlock(0).Set(1.0, x0.GetBlock(0));
|
||||
xk.GetBlock(1).Set(1.0, x0.GetBlock(1));
|
||||
// running estimate of the final values of the Lagrange multipliers
|
||||
lk = 0.0;
|
||||
zlk = 0.0;
|
||||
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
zlk(i) = 1.e1 * mu_k / (xk(i+dimU) - ml(i));
|
||||
}
|
||||
|
||||
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
|
||||
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
|
||||
Xk.GetBlock(2).Set(1.0, lk);
|
||||
Xk.GetBlock(3).Set(1.0, zlk);
|
||||
|
||||
/* set theta0 = theta(x0)
|
||||
* thetaMin
|
||||
* thetaMax
|
||||
* when theta(xk) < thetaMin and the switching condition holds
|
||||
* then we ask for the Armijo sufficient decrease of the barrier
|
||||
* objective to be satisfied, in order to accept the trial step length alphakl
|
||||
*
|
||||
* thetaMax controls how the filter is initialized for each log-barrier subproblem
|
||||
* F0 = {(th, phi) s.t. th > thetaMax}
|
||||
* that is the filter does not allow for iterates where the constraint violation
|
||||
* is larger than that of thetaMax
|
||||
*/
|
||||
double theta0 = theta(xk);
|
||||
thetaMin = 1.e-4 * max(1.0, theta0);
|
||||
thetaMax = 1.e8 * thetaMin; // 1.e4 * max(1.0, theta0)
|
||||
|
||||
double Eeval, maxBarrierSolves, Eevalmu0;
|
||||
bool printOptimalityError; // control optimality error print to console for log-barrier subproblems
|
||||
|
||||
maxBarrierSolves = 10;
|
||||
|
||||
for(jOpt = 0; jOpt < max_iter; jOpt++)
|
||||
{
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "interior-point solve step " << jOpt << endl;
|
||||
}
|
||||
// A-2. Check convergence of overall optimization problem
|
||||
printOptimalityError = false;
|
||||
Eevalmu0 = E(xk, lk, zlk, printOptimalityError);
|
||||
if(Eevalmu0 < OptTol)
|
||||
{
|
||||
converged = true;
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "solved optimization problem :)\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(jOpt > 0) { maxBarrierSolves = 1; }
|
||||
|
||||
for(int i = 0; i < maxBarrierSolves; i++)
|
||||
{
|
||||
// A-3. Check convergence of the barrier subproblem
|
||||
printOptimalityError = true;
|
||||
Eeval = E(xk, lk, zlk, mu_k, printOptimalityError);
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "E = " << Eeval << endl;
|
||||
}
|
||||
if(Eeval < kEps * mu_k)
|
||||
{
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "solved barrier subproblem :), for mu = " << mu_k << endl;
|
||||
}
|
||||
// A-3.1. Recompute the barrier parameter
|
||||
mu_k = max(OptTol / 10., min(kMu * mu_k, pow(mu_k, thetaMu)));
|
||||
// A-3.2. Re-initialize the filter
|
||||
F1.DeleteAll();
|
||||
F2.DeleteAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// A-4. Compute the search direction
|
||||
// solve for (uhat, mhat, lhat)
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "\n** A-4. IP-Newton solve **\n";
|
||||
}
|
||||
zlhat = 0.0; Xhatuml = 0.0;
|
||||
// why do we have Xhatuml ....???
|
||||
// TO DO: remove Xhatuml in favor of passing Xhat
|
||||
IPNewtonSolve(xk, lk, zlk, zlhat, Xhatuml, mu_k, false);
|
||||
|
||||
// assign data stack, X = (u, m, l, zl)
|
||||
Xk = 0.0;
|
||||
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
|
||||
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
|
||||
Xk.GetBlock(2).Set(1.0, lk);
|
||||
Xk.GetBlock(3).Set(1.0, zlk);
|
||||
|
||||
// assign data stack, Xhat = (uhat, mhat, lhat, zlhat)
|
||||
Xhat = 0.0;
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
Xhat.GetBlock(i).Set(1.0, Xhatuml.GetBlock(i));
|
||||
}
|
||||
Xhat.GetBlock(3).Set(1.0, zlhat);
|
||||
|
||||
// A-5. Backtracking line search.
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "\n** A-5. Linesearch **\n";
|
||||
cout << "mu = " << mu_k << endl;
|
||||
}
|
||||
lineSearch(Xk, Xhat, mu_k);
|
||||
|
||||
if(lineSearchSuccess)
|
||||
{
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "lineSearch successful :)\n";
|
||||
}
|
||||
if(!switchCondition || !sufficientDecrease)
|
||||
{
|
||||
F1.Append( (1. - gTheta) * thx0);
|
||||
F2.Append( phx0 - gPhi * thx0);
|
||||
}
|
||||
// ----- A-6: Accept the trial point
|
||||
// print info regarding zl...
|
||||
xk.GetBlock(0).Add(alpha, Xhat.GetBlock(0));
|
||||
xk.GetBlock(1).Add(alpha, Xhat.GetBlock(1));
|
||||
lk.Add(alpha, Xhat.GetBlock(2));
|
||||
zlk.Add(alphaz, Xhat.GetBlock(3));
|
||||
projectZ(xk, zlk, mu_k);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "lineSearch not successful :(\n";
|
||||
cout << "attempting feasibility restoration with theta = " << thx0 << endl;
|
||||
cout << "no feasibility restoration implemented, exiting now \n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(jOpt + 1 == max_iter && iAmRoot)
|
||||
{
|
||||
cout << "maximum optimization iterations :(\n";
|
||||
}
|
||||
}
|
||||
// done with optimization routine, just reassign data to xf reference so
|
||||
// that the application code has access to the optimal point
|
||||
xf = 0.0;
|
||||
xf.GetBlock(0).Set(1.0, xk.GetBlock(0));
|
||||
xf.GetBlock(1).Set(1.0, xk.GetBlock(1));
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::FormIPNewtonMat(BlockVector & x, Vector & l, Vector &zl,
|
||||
BlockOperator &Ak)
|
||||
{
|
||||
// WARNING: Huu, Hum, Hmu, Hmm should all be Hessian terms of the Lagrangian, currently we
|
||||
// them by Hessian terms of the objective function and neglect the Hessian of l^T c
|
||||
|
||||
Huu = problem->Duuf(x);
|
||||
Hum = problem->Dumf(x);
|
||||
Hmu = problem->Dmuf(x);
|
||||
Hmm = problem->Dmmf(x);
|
||||
|
||||
Vector DiagLogBar(dimM); DiagLogBar = 0.0;
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
DiagLogBar(ii) = zl(ii) / (x(ii+dimU) - ml(ii));
|
||||
}
|
||||
if(saveLogBarrierIterates)
|
||||
{
|
||||
std::ofstream diagStream;
|
||||
char diagString[100];
|
||||
snprintf(diagString, 100, "logBarrierHessiandata/D%d.dat", jOpt);
|
||||
diagStream.open(diagString, ios::out | ios::trunc);
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
diagStream << setprecision(30) << DiagLogBar(ii) << endl;
|
||||
// mfem::out << DiagLogBar(ii) << endl;
|
||||
}
|
||||
diagStream.close();
|
||||
}
|
||||
|
||||
|
||||
int gsize = problem->GetGlobalNumConstraints();
|
||||
int * rows = problem->GetConstraintsStarts();
|
||||
|
||||
delete Wmm;
|
||||
if(Hmm != nullptr)
|
||||
{
|
||||
SparseMatrix * Ds = new SparseMatrix(DiagLogBar);
|
||||
HypreParMatrix * D = new HypreParMatrix(problem->GetComm(), gsize, rows, Ds);
|
||||
HypreStealOwnership(*D,*Ds);
|
||||
delete Ds;
|
||||
Wmm = ParAdd(Hmm,D);
|
||||
delete D;
|
||||
}
|
||||
else
|
||||
{
|
||||
SparseMatrix * Ds = new SparseMatrix(DiagLogBar);
|
||||
Wmm = new HypreParMatrix(problem->GetComm(), gsize, rows, Ds);
|
||||
HypreStealOwnership(*Wmm,*Ds);
|
||||
delete Ds;
|
||||
}
|
||||
|
||||
delete JuT;
|
||||
delete JmT;
|
||||
Ju = problem->Duc(x); JuT = Ju->Transpose();
|
||||
Jm = problem->Dmc(x); JmT = Jm->Transpose();
|
||||
|
||||
// IP-Newton system matrix
|
||||
// Ak = [[H_(u,u) H_(u,m) J_u^T]
|
||||
// [H_(m,u) W_(m,m) J_m^T]
|
||||
// [ J_u J_m 0 ]]
|
||||
|
||||
Ak.SetBlock(0, 0, Huu); Ak.SetBlock(0, 2, JuT);
|
||||
Ak.SetBlock(1, 1, Wmm); Ak.SetBlock(1, 2, JmT);
|
||||
Ak.SetBlock(2, 0, Ju); Ak.SetBlock(2, 1, Jm);
|
||||
if(Hum != nullptr) { Ak.SetBlock(0, 1, Hum); Ak.SetBlock(1, 0, Hmu); }
|
||||
}
|
||||
|
||||
// perturbed KKT system solve
|
||||
// determine the search direction
|
||||
void ParInteriorPointSolver::IPNewtonSolve(BlockVector &x, Vector &l, Vector &zl, Vector &zlhat, BlockVector &Xhat, double mu, bool socSolve)
|
||||
{
|
||||
// solve A x = b, where A is the IP-Newton matrix
|
||||
BlockOperator A(block_offsetsuml, block_offsetsuml);
|
||||
BlockVector b(block_offsetsuml); b = 0.0;
|
||||
FormIPNewtonMat(x, l, zl, A);
|
||||
|
||||
// [grad_u phi + Ju^T l]
|
||||
// b = - [grad_m phi + Jm^T l]
|
||||
// [ c ]
|
||||
BlockVector gradphi(block_offsetsx); gradphi = 0.0;
|
||||
BlockVector JTl(block_offsetsx); JTl = 0.0;
|
||||
Dxphi(x, mu, gradphi);
|
||||
|
||||
(A.GetBlock(0,2)).Mult(l, JTl.GetBlock(0));
|
||||
(A.GetBlock(1,2)).Mult(l, JTl.GetBlock(1));
|
||||
|
||||
for(int ii = 0; ii < 2; ii++)
|
||||
{
|
||||
b.GetBlock(ii).Set(1.0, gradphi.GetBlock(ii));
|
||||
b.GetBlock(ii).Add(1.0, JTl.GetBlock(ii));
|
||||
}
|
||||
if(!socSolve)
|
||||
{
|
||||
problem->c(x, b.GetBlock(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
b.GetBlock(2).Set(1.0, ckSoc);
|
||||
}
|
||||
b *= -1.0;
|
||||
Xhat = 0.0;
|
||||
|
||||
// Direct solver (default)
|
||||
if(linSolver == 0)
|
||||
{
|
||||
Array2D<HypreParMatrix *> ABlockMatrix(3,3);
|
||||
for(int ii = 0; ii < 3; ii++)
|
||||
{
|
||||
for(int jj = 0; jj < 3; jj++)
|
||||
{
|
||||
if(!A.IsZeroBlock(ii, jj))
|
||||
{
|
||||
ABlockMatrix(ii, jj) = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(ii, jj)));
|
||||
}
|
||||
else
|
||||
{
|
||||
ABlockMatrix(ii, jj) = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HypreParMatrix * Ah = HypreParMatrixFromBlocks(ABlockMatrix);
|
||||
|
||||
/* direct solve of the 3x3 IP-Newton linear system */
|
||||
#ifdef MFEM_USE_MUMPS
|
||||
MUMPSSolver ASolver(*Ah);;
|
||||
ASolver.SetPrintLevel(0);
|
||||
ASolver.SetMatrixSymType(MUMPSSolver::MatType::UNSYMMETRIC);
|
||||
ASolver.Mult(b, Xhat);
|
||||
#else
|
||||
#ifdef MFEM_USE_MKL_CPARDISO
|
||||
CPardisoSolver ASolver(MPI_COMM_WORLD);
|
||||
ASolver.SetOperator(*Ah);
|
||||
ASolver.Mult(b, Xhat);
|
||||
#else
|
||||
MFEM_VERIFY(false, "linSolver 0 will not work unless compiled with MUMPS or MKL");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
delete Ah;
|
||||
}
|
||||
else if(linSolver == 1 || linSolver == 2)
|
||||
{
|
||||
// form A = Huu + Ju^T D Ju, Wmm = D for contact
|
||||
HypreParMatrix * Wmmloc = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(1, 1)));
|
||||
HypreParMatrix * Huuloc = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(0, 0)));
|
||||
HypreParMatrix * Juloc = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(2, 0)));
|
||||
HypreParMatrix * JuTloc = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(0, 2)));
|
||||
HypreParMatrix *JuTDJu = RAP(Wmmloc, Juloc); // Ju^T D Ju
|
||||
HypreParMatrix *Areduced = ParAdd(Huuloc, JuTDJu); // Huu + Ju^T D Ju
|
||||
|
||||
Areduced->DropSmallEntries(1e-16);
|
||||
|
||||
/* prepare the reduced rhs */
|
||||
// breduced = bu + Ju^T (bm + Wmm bl)
|
||||
Vector breduced(dimU); breduced = 0.0;
|
||||
Vector tempVec(dimM); tempVec = 0.0;
|
||||
Wmmloc->Mult(b.GetBlock(2), tempVec);
|
||||
tempVec.Add(1.0, b.GetBlock(1));
|
||||
JuTloc->Mult(tempVec, breduced);
|
||||
breduced.Add(1.0, b.GetBlock(0));
|
||||
|
||||
if(linSolver == 1)
|
||||
{
|
||||
// setup the solver for the reduced linear system
|
||||
#ifdef MFEM_USE_MUMPS
|
||||
MUMPSSolver AreducedSolver(*Areduced);
|
||||
AreducedSolver.SetPrintLevel(0);
|
||||
AreducedSolver.SetMatrixSymType(MUMPSSolver::MatType::SYMMETRIC_INDEFINITE);
|
||||
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
|
||||
#else
|
||||
#ifdef MFEM_USE_MKL_CPARDISO
|
||||
CPardisoSolver AreducedSolver(MPI_COMM_WORLD);
|
||||
AreducedSolver.SetOperator(*Areduced);
|
||||
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
|
||||
#else
|
||||
MFEM_VERIFY(false, "linSolver 1 will not work unless compiled with MUMPS or MKL");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
HypreBoomerAMG amg(*Areduced);
|
||||
amg.SetPrintLevel(0);
|
||||
if (pfes)
|
||||
{
|
||||
amg.SetElasticityOptions(pfes);
|
||||
}
|
||||
else
|
||||
{
|
||||
amg.SetSystemsOptions(3,false);
|
||||
}
|
||||
amg.SetRelaxType(relax_type);
|
||||
int n;
|
||||
|
||||
|
||||
// CGSolver AreducedSolver(MPI_COMM_WORLD);
|
||||
// AreducedSolver.SetOperator(*Areduced);
|
||||
// AreducedSolver.SetRelTol(linSolveTol);
|
||||
// AreducedSolver.SetMaxIter(1000);
|
||||
// AreducedSolver.SetPreconditioner(amg);
|
||||
// AreducedSolver.SetPrintLevel(3);
|
||||
// AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
|
||||
// n = AreducedSolver.GetNumIterations();
|
||||
|
||||
HyprePCG AreducedSolver(*Areduced);
|
||||
AreducedSolver.SetTol(linSolveTol);
|
||||
AreducedSolver.SetMaxIter(1000);
|
||||
AreducedSolver.SetPreconditioner(amg);
|
||||
AreducedSolver.SetPrintLevel(2);
|
||||
// AreducedSolver.SetResidualConvergenceOptions();
|
||||
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
|
||||
AreducedSolver.GetNumIterations(n);
|
||||
|
||||
cgnum_iterations.Append(n);
|
||||
|
||||
|
||||
}
|
||||
|
||||
// now propagate solved uhat to obtain mhat and lhat
|
||||
// xm = Ju xu - bl
|
||||
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
|
||||
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
|
||||
|
||||
// xl = Wmm xm - bm
|
||||
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
|
||||
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
|
||||
|
||||
delete JuTDJu;
|
||||
delete Areduced;
|
||||
}
|
||||
|
||||
/* backsolve to determine zlhat */
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
zlhat(ii) = -1.*(zl(ii) + (zl(ii) * Xhat(ii + dimU) - mu) / (x(ii + dimU) - ml(ii)) );
|
||||
}
|
||||
}
|
||||
|
||||
// here Xhat, X will be BlockVectors w.r.t. the 4 partitioning X = (u, m, l, zl)
|
||||
|
||||
void ParInteriorPointSolver::lineSearch(BlockVector& X0, BlockVector& Xhat, double mu)
|
||||
{
|
||||
double tau = max(tauMin, 1.0 - mu);
|
||||
Vector u0 = X0.GetBlock(0);
|
||||
Vector m0 = X0.GetBlock(1);
|
||||
Vector l0 = X0.GetBlock(2);
|
||||
Vector z0 = X0.GetBlock(3);
|
||||
Vector uhat = Xhat.GetBlock(0);
|
||||
Vector mhat = Xhat.GetBlock(1);
|
||||
Vector lhat = Xhat.GetBlock(2);
|
||||
Vector zhat = Xhat.GetBlock(3);
|
||||
double alphaMax = MaxStepSize(m0, ml, mhat, tau);
|
||||
double alphaMaxz = MaxStepSize(z0, zhat, tau);
|
||||
alphaz = alphaMaxz;
|
||||
|
||||
BlockVector x0(block_offsetsx); x0 = 0.0;
|
||||
x0.GetBlock(0).Set(1.0, u0);
|
||||
x0.GetBlock(1).Set(1.0, m0);
|
||||
|
||||
BlockVector xhat(block_offsetsx); xhat = 0.0;
|
||||
xhat.GetBlock(0).Set(1.0, uhat);
|
||||
xhat.GetBlock(1).Set(1.0, mhat);
|
||||
|
||||
BlockVector xtrial(block_offsetsx); xtrial = 0.0;
|
||||
BlockVector Dxphi0(block_offsetsx); Dxphi0 = 0.0;
|
||||
int maxBacktrack = 20;
|
||||
alpha = alphaMax;
|
||||
|
||||
Vector ck0(dimC); ck0 = 0.0;
|
||||
Vector zhatsoc(dimM); zhatsoc = 0.0;
|
||||
BlockVector Xhatumlsoc(block_offsetsuml); Xhatumlsoc = 0.0;
|
||||
BlockVector xhatsoc(block_offsetsx); xhatsoc = 0.0;
|
||||
Vector uhatsoc(dimU); uhatsoc = 0.0;
|
||||
Vector mhatsoc(dimM); mhatsoc = 0.0;
|
||||
|
||||
Dxphi(x0, mu, Dxphi0);
|
||||
|
||||
Dxphi0_xhat = InnerProduct(MPI_COMM_WORLD, Dxphi0, xhat);
|
||||
descentDirection = Dxphi0_xhat < 0. ? true : false;
|
||||
|
||||
|
||||
if (iAmRoot)
|
||||
{
|
||||
if(descentDirection)
|
||||
{
|
||||
cout << "is a descent direction for the log-barrier objective\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "is not a descent direction for the log-barrier objective\n";
|
||||
}
|
||||
}
|
||||
|
||||
thx0 = theta(x0);
|
||||
phx0 = phi(x0, mu);
|
||||
|
||||
lineSearchSuccess = false;
|
||||
for(int i = 0; i < maxBacktrack; i++)
|
||||
{
|
||||
if (iAmRoot)
|
||||
{
|
||||
cout << "\n--------- alpha = " << alpha << " ---------\n";
|
||||
}
|
||||
// ----- A-5.2. Compute trial point: xtrial = x0 + alpha_i xhat
|
||||
xtrial.Set(1.0, x0);
|
||||
xtrial.Add(alpha, xhat);
|
||||
|
||||
// ------ A-5.3. if not in filter region go to A.5.4 otherwise go to A-5.5.
|
||||
thxtrial = theta(xtrial);
|
||||
phxtrial = phi(xtrial, mu);
|
||||
filterCheck(thxtrial, phxtrial);
|
||||
if(!inFilterRegion)
|
||||
{
|
||||
if (iAmRoot)
|
||||
{
|
||||
cout << "not in filter region :)\n";
|
||||
}
|
||||
// ------ A.5.4: Check sufficient decrease
|
||||
if(!descentDirection)
|
||||
{
|
||||
switchCondition = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
switchCondition = (alpha * pow(abs(Dxphi0_xhat), sPhi) > delta * pow(thx0, sTheta)) ? true : false;
|
||||
}
|
||||
if (iAmRoot)
|
||||
{
|
||||
cout << "theta(x0) = " << thx0 << ", thetaMin = " << thetaMin << endl;
|
||||
cout << "theta(xtrial) = " << thxtrial << ", (1-gTheta) *theta(x0) = " << (1. - gTheta) * thx0 << endl;
|
||||
cout << "phi(xtrial) = " << phxtrial << ", phi(x0) - gPhi *theta(x0) = " << phx0 - gPhi * thx0 << endl;
|
||||
}
|
||||
// Case I
|
||||
if(thx0 <= thetaMin && switchCondition)
|
||||
{
|
||||
sufficientDecrease = (phxtrial <= phx0 + eta * alpha * Dxphi0_xhat) ? true : false;
|
||||
if(sufficientDecrease)
|
||||
{
|
||||
if(iAmRoot) { cout << "Line search successful: sufficient decrease in log-barrier objective.\n"; }
|
||||
// accept the trial step
|
||||
lineSearchSuccess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(thxtrial <= (1. - gTheta) * thx0 || phxtrial <= phx0 - gPhi * thx0)
|
||||
{
|
||||
if(iAmRoot) { cout << "Line search successful: infeasibility or log-barrier objective decreased.\n"; }
|
||||
// accept the trial step
|
||||
lineSearchSuccess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// A-5.5: Initialize the second-order correction
|
||||
if((!(thx0 < thxtrial)) && i == 0)
|
||||
{
|
||||
if (iAmRoot)
|
||||
{
|
||||
cout << "second order correction\n";
|
||||
}
|
||||
problem->c(xtrial, ckSoc);
|
||||
problem->c(x0, ck0);
|
||||
ckSoc.Add(alphaMax, ck0);
|
||||
// A-5.6 Compute the second-order correction.
|
||||
IPNewtonSolve(x0, l0, z0, zhatsoc, Xhatumlsoc, mu, true);
|
||||
mhatsoc.Set(1.0, Xhatumlsoc.GetBlock(1));
|
||||
//WARNING: not complete but currently solver isn't entering this region
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iAmRoot)
|
||||
{
|
||||
cout << "in filter region :(\n";
|
||||
}
|
||||
}
|
||||
// include more if needed
|
||||
alpha *= 0.5;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParInteriorPointSolver::projectZ(const Vector &x, Vector &z, double mu)
|
||||
{
|
||||
double zi;
|
||||
double mudivmml;
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
zi = z(i);
|
||||
mudivmml = mu / (x(i + dimU) - ml(i));
|
||||
z(i) = max(min(zi, kSig * mudivmml), mudivmml / kSig);
|
||||
}
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::filterCheck(double th, double ph)
|
||||
{
|
||||
inFilterRegion = false;
|
||||
if(th > thetaMax)
|
||||
{
|
||||
inFilterRegion = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < F1.Size(); i++)
|
||||
{
|
||||
if(th >= F1[i] && ph >= F2[i])
|
||||
{
|
||||
inFilterRegion = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double ParInteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, double mu, bool printEeval)
|
||||
{
|
||||
double E1, E2, E3;
|
||||
double sc, sd;
|
||||
BlockVector gradL(block_offsetsx); gradL = 0.0; // stationarity grad L = grad f + J^T l - z
|
||||
Vector cx(dimC); cx = 0.0; // feasibility c = c(x)
|
||||
Vector comp(dimM); comp = 0.0; // complementarity M Z - mu 1
|
||||
|
||||
DxL(x, l, zl, gradL);
|
||||
E1 = GlobalLpNorm(infinity(), gradL.Normlinf(), MPI_COMM_WORLD);
|
||||
|
||||
problem->c(x, cx);
|
||||
E2 = GlobalLpNorm(infinity(), cx.Normlinf(), MPI_COMM_WORLD);
|
||||
|
||||
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
comp(ii) = x(dimU + ii) * zl(ii) - mu;
|
||||
}
|
||||
E3 = GlobalLpNorm(infinity(), comp.Normlinf(), MPI_COMM_WORLD);
|
||||
|
||||
double ll1, zl1;
|
||||
|
||||
zl1 = GlobalLpNorm(1, zl.Norml1(), MPI_COMM_WORLD)/ double(gdimC + gdimM);;
|
||||
ll1 = GlobalLpNorm(1, l.Norml1(), MPI_COMM_WORLD);
|
||||
sc = max(sMax, zl1 / (double(gdimM)) ) / sMax;
|
||||
sd = max(sMax, (ll1 + zl1) / (double(gdimC + gdimM))) / sMax;
|
||||
if(iAmRoot && printEeval)
|
||||
{
|
||||
cout << "evaluating optimality error for mu = " << mu << endl;
|
||||
cout << "stationarity measure = " << E1 / sd << endl;
|
||||
cout << "feasibility measure = " << E2 << endl;
|
||||
cout << "complimentarity measure = " << E3 / sc << endl;
|
||||
}
|
||||
return max(max(E1 / sd, E2), E3 / sc);
|
||||
}
|
||||
|
||||
double ParInteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, bool printEeval)
|
||||
{
|
||||
return E(x, l, zl, 0.0, printEeval);
|
||||
}
|
||||
|
||||
double ParInteriorPointSolver::theta(const BlockVector &x)
|
||||
{
|
||||
Vector cx(dimC);
|
||||
problem->c(x, cx);
|
||||
return sqrt(InnerProduct(MPI_COMM_WORLD,cx, cx));
|
||||
}
|
||||
|
||||
// log-barrier objective
|
||||
double ParInteriorPointSolver::phi(const BlockVector &x, double mu)
|
||||
{
|
||||
double fx = problem->CalcObjective(x);
|
||||
double logBarrierLoc = 0.0;
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
logBarrierLoc += log(x(dimU+i)-ml(i));
|
||||
}
|
||||
double logBarrierGlb;
|
||||
MPI_Allreduce(&logBarrierLoc, &logBarrierGlb, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
|
||||
return fx - mu * logBarrierGlb;
|
||||
}
|
||||
|
||||
// gradient of log-barrier objective with respect to x = (u, m)
|
||||
void ParInteriorPointSolver::Dxphi(const BlockVector &x, double mu, BlockVector &y)
|
||||
{
|
||||
problem->CalcObjectiveGrad(x, y);
|
||||
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
y(dimU + i) -= mu / (x(dimU + i));
|
||||
}
|
||||
}
|
||||
|
||||
// Lagrangian function evaluation
|
||||
// L(x, l, zl) = f(x) + l^T c(x) - zl^T m
|
||||
double ParInteriorPointSolver::L(const BlockVector &x, const Vector &l, const Vector &zl)
|
||||
{
|
||||
double fx = problem->CalcObjective(x);
|
||||
Vector cx(dimC); problem->c(x, cx);
|
||||
return (fx + InnerProduct(MPI_COMM_WORLD,cx, l) - InnerProduct(MPI_COMM_WORLD, x.GetBlock(1), zl));
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::DxL(const BlockVector &x, const Vector &l, const Vector &zl, BlockVector &y)
|
||||
{
|
||||
// evaluate the gradient of the objective with respect to the primal variables x = (u, m)
|
||||
BlockVector gradxf(block_offsetsx); gradxf = 0.0;
|
||||
problem->CalcObjectiveGrad(x, gradxf);
|
||||
|
||||
HypreParMatrix *Jacu, *Jacm, *JacuT, *JacmT;
|
||||
Jacu = problem->Duc(x);
|
||||
Jacm = problem->Dmc(x);
|
||||
JacuT = Jacu->Transpose();
|
||||
JacmT = Jacm->Transpose();
|
||||
|
||||
JacuT->Mult(l, y.GetBlock(0));
|
||||
JacmT->Mult(l, y.GetBlock(1));
|
||||
|
||||
delete JacuT;
|
||||
delete JacmT;
|
||||
|
||||
y.Add(1.0, gradxf);
|
||||
(y.GetBlock(1)).Add(-1.0, zl);
|
||||
}
|
||||
|
||||
bool ParInteriorPointSolver::GetConverged() const
|
||||
{
|
||||
return converged;
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::SetTol(double Tol)
|
||||
{
|
||||
OptTol = Tol;
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::SetMaxIter(int max_it)
|
||||
{
|
||||
max_iter = max_it;
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::SetBarrierParameter(double mu_0)
|
||||
{
|
||||
mu_k = mu_0;
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::SaveLogBarrierHessianIterates(bool save)
|
||||
{
|
||||
MFEM_ASSERT(MyRank == 0 || save == false, "currently can only save logbarrier hessian in serial codes");
|
||||
saveLogBarrierIterates = save;
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::SetLinearSolver(int LinSolver)
|
||||
{
|
||||
linSolver = LinSolver;
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::SetLinearSolveTol(double Tol)
|
||||
{
|
||||
linSolveTol = Tol;
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::SetLinearSolveRelaxType(int relax_type_)
|
||||
{
|
||||
relax_type = relax_type_;
|
||||
}
|
||||
|
||||
|
||||
ParInteriorPointSolver::~ParInteriorPointSolver()
|
||||
{
|
||||
delete JuT;
|
||||
delete JmT;
|
||||
delete Wmm;
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
#include "mfem.hpp"
|
||||
#include "../problems/parproblems.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
#ifndef PARIPSOLVER
|
||||
#define PARIPSOLVER
|
||||
|
||||
class ParInteriorPointSolver
|
||||
{
|
||||
protected:
|
||||
QPOptParContactProblem* problem;
|
||||
double OptTol;
|
||||
int max_iter;
|
||||
double mu_k; // \mu_k
|
||||
Vector lk, zlk;
|
||||
|
||||
double sMax, kSig, tauMin, eta, thetaMin, delta, sTheta, sPhi, kMu, thetaMu;
|
||||
double thetaMax, kSoc, gTheta, gPhi, kEps;
|
||||
|
||||
// filter
|
||||
Array<double> F1, F2;
|
||||
|
||||
// quantities computed in lineSearch
|
||||
double alpha, alphaz;
|
||||
double thx0, thxtrial;
|
||||
double phx0, phxtrial;
|
||||
bool descentDirection, switchCondition, sufficientDecrease, lineSearchSuccess, inFilterRegion;
|
||||
double Dxphi0_xhat;
|
||||
|
||||
int dimU, dimM, dimC;
|
||||
int gdimU, gdimM, gdimC;
|
||||
Array<int> block_offsetsumlz, block_offsetsuml, block_offsetsx;
|
||||
Vector ml;
|
||||
|
||||
Vector ckSoc;
|
||||
HypreParMatrix * Huu = nullptr;
|
||||
HypreParMatrix * Hum = nullptr;
|
||||
HypreParMatrix * Hmu = nullptr;
|
||||
HypreParMatrix * Hmm = nullptr;
|
||||
HypreParMatrix * Wmm = nullptr;
|
||||
HypreParMatrix * Ju = nullptr;
|
||||
HypreParMatrix * Jm = nullptr;
|
||||
HypreParMatrix * JuT = nullptr;
|
||||
HypreParMatrix * JmT = nullptr;
|
||||
|
||||
Array<int> cgnum_iterations;
|
||||
ParFiniteElementSpace *pfes = nullptr;
|
||||
|
||||
int jOpt;
|
||||
bool converged;
|
||||
|
||||
int MyRank;
|
||||
bool iAmRoot;
|
||||
|
||||
bool saveLogBarrierIterates = false;
|
||||
|
||||
int linSolver;
|
||||
double linSolveTol;
|
||||
int relax_type = 8;
|
||||
public:
|
||||
ParInteriorPointSolver(QPOptParContactProblem*);
|
||||
double MaxStepSize(Vector& , Vector& , Vector& , double);
|
||||
double MaxStepSize(Vector& , Vector& , double);
|
||||
void Mult(const BlockVector& , BlockVector&);
|
||||
void Mult(const Vector&, Vector &);
|
||||
void FormIPNewtonMat(BlockVector& , Vector& , Vector& , BlockOperator &);
|
||||
void IPNewtonSolve(BlockVector& , Vector& , Vector& , Vector&, BlockVector& , double, bool);
|
||||
void lineSearch(BlockVector& , BlockVector& , double);
|
||||
void projectZ(const Vector & , Vector &, double);
|
||||
void filterCheck(double, double);
|
||||
double E(const BlockVector &, const Vector &, const Vector &, double, bool);
|
||||
double E(const BlockVector &, const Vector &, const Vector &, bool);
|
||||
bool GetConverged() const;
|
||||
Array<int> & GetCGIterNumbers() {return cgnum_iterations;}
|
||||
// TO DO: include Hessian of Lagrangian
|
||||
double theta(const BlockVector &);
|
||||
double phi(const BlockVector &, double);
|
||||
void Dxphi(const BlockVector &, double, BlockVector &);
|
||||
double L(const BlockVector &, const Vector &, const Vector &);
|
||||
void DxL(const BlockVector &, const Vector &, const Vector &, BlockVector &);
|
||||
void SetTol(double);
|
||||
void SetMaxIter(int);
|
||||
void SetBarrierParameter(double);
|
||||
void SaveLogBarrierHessianIterates(bool);
|
||||
void SetLinearSolver(int);
|
||||
void SetLinearSolveTol(double);
|
||||
void SetLinearSolveRelaxType(int);
|
||||
void SetFiniteElementSpace(ParFiniteElementSpace * pfes_)
|
||||
{
|
||||
pfes = pfes_;
|
||||
}
|
||||
virtual ~ParInteriorPointSolver();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,110 +0,0 @@
|
||||
# Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/miniapps/contact/,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
|
||||
# Include defaults.mk to get XLINKER
|
||||
DEFAULTS_MK = $(MFEM_DIR)/config/defaults.mk
|
||||
include $(DEFAULTS_MK)
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
CONTACT_SEQ_SRC = problems/problems.cpp problems/problems_util.cpp util/util.cpp ipsolver/IPsolver.cpp
|
||||
CONTACT_SEC_OBJ = $(CONTACT_PAR_SRC:.cpp=.o)
|
||||
CONTACT_PAR_SRC = $(CONTACT_SEQ_SRC) ipsolver/ParIPsolver.cpp problems/parproblems.cpp problems/parproblems_util.cpp util/mpicomm.cpp
|
||||
CONTACT_PAR_OBJ = $(CONTACT_PAR_SRC:.cpp=.o)
|
||||
|
||||
CONTACT_SRC = contact_driver.cpp $(CONTACT_SEQ_SRC)
|
||||
CONTACT_OBJ = $(CONTACT_SRC:.cpp=.o)
|
||||
|
||||
PCONTACT_SRC = pcontact_driver.cpp $(CONTACT_PAR_SRC)
|
||||
PCONTACT_OBJ = $(PCONTACT_SRC:.cpp=.o)
|
||||
|
||||
SEQ_MINIAPPS = contact_driver
|
||||
PAR_MINIAPPS = pcontact_driver
|
||||
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
MINIAPPS = $(SEQ_MINIAPPS)
|
||||
else
|
||||
MINIAPPS = $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
|
||||
endif
|
||||
|
||||
COMMON_LIB = -L$(MFEM_BUILD_DIR)/miniapps/common -lmfem-common
|
||||
|
||||
# If MFEM_SHARED is set, add the ../common rpath
|
||||
COMMON_LIB += $(if $(MFEM_SHARED:YES=),,\
|
||||
$(if $(MFEM_USE_CUDA:YES=),$(CXX_XLINKER),$(CUDA_XLINKER))-rpath,$(abspath\
|
||||
$(MFEM_BUILD_DIR)/miniapps/common))
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all lib-common clean clean-build clean-exec
|
||||
|
||||
# Remove built-in rule
|
||||
%: %.cpp
|
||||
%.o: %.cpp
|
||||
|
||||
%.o: $(SRC)%.cpp $(wildcard $(SRC)%.hpp) $(MFEM_LIB_FILE)\
|
||||
$(CONFIG_MK) | lib-common
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@
|
||||
|
||||
util/%.o: $(SRC)util/%.cpp $(wildcard $(SRC)util/%.hpp) $(MFEM_LIB_FILE)\
|
||||
$(CONFIG_MK) | lib-common
|
||||
mkdir -p $(@D)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@
|
||||
|
||||
problems/%.o: $(SRC)problems/%.cpp $(wildcard $(SRC)problems/%.hpp) $(MFEM_LIB_FILE)\
|
||||
$(CONFIG_MK) | lib-common
|
||||
mkdir -p $(@D)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@
|
||||
|
||||
|
||||
all: $(MINIAPPS)
|
||||
|
||||
contact_driver: $(CONTACT_OBJ)
|
||||
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(CONTACT_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
|
||||
|
||||
pcontact_driver: $(PCONTACT_OBJ)
|
||||
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PCONTACT_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
|
||||
|
||||
|
||||
# Rule for building lib-common
|
||||
lib-common:
|
||||
$(MAKE) -C $(MFEM_BUILD_DIR)/miniapps/common
|
||||
|
||||
MFEM_TESTS = MINIAPPS
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Specific execution options
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
contact-test-seq: diffusion
|
||||
@$(call mfem-test,$<,, contact miniapp,)
|
||||
pcontact-test-par: pcontact
|
||||
@$(call mfem-test,$<, $(RUN_MPI), pcontact miniapp,)
|
||||
|
||||
# Generate an error message if the MFEM library is not built and exit
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
|
||||
rm -f $(CONTACT_OBJ) $(PCONTACT_OBJ)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@rm -rf ParaView
|
||||
@@ -1,103 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
elements
|
||||
9
|
||||
1 5 0 1 3 2 8 9 11 10
|
||||
1 5 2 3 5 4 10 11 13 12
|
||||
1 5 4 5 7 6 12 13 15 14
|
||||
1 5 8 9 11 10 16 17 19 18
|
||||
1 5 10 11 13 12 18 19 21 20
|
||||
1 5 12 13 15 14 20 21 23 22
|
||||
1 5 16 17 19 18 24 25 27 26
|
||||
1 5 18 19 21 20 26 27 29 28
|
||||
1 5 20 21 23 22 28 29 31 30
|
||||
|
||||
|
||||
|
||||
# 0 nothing
|
||||
# 1 dirichlet bc
|
||||
# 2 contact
|
||||
boundary
|
||||
30
|
||||
1 3 1 0 2 3
|
||||
1 3 3 2 4 5
|
||||
1 3 5 4 6 7
|
||||
1 3 24 25 27 26
|
||||
1 3 26 27 29 28
|
||||
1 3 28 29 31 30
|
||||
2 3 2 0 8 10
|
||||
2 3 4 2 10 12
|
||||
2 3 6 4 12 14
|
||||
2 3 10 8 16 18
|
||||
2 3 12 10 18 20
|
||||
2 3 14 12 20 22
|
||||
2 3 18 16 24 26
|
||||
2 3 20 18 26 28
|
||||
2 3 22 20 28 30
|
||||
3 3 1 3 11 9
|
||||
3 3 3 5 13 11
|
||||
3 3 5 7 15 13
|
||||
3 3 9 11 19 17
|
||||
3 3 11 13 21 19
|
||||
3 3 13 15 23 21
|
||||
3 3 17 19 27 25
|
||||
3 3 19 21 29 27
|
||||
3 3 21 23 31 29
|
||||
1 3 8 0 1 9
|
||||
1 3 16 8 9 17
|
||||
1 3 24 16 17 25
|
||||
1 3 6 14 15 7
|
||||
1 3 14 22 23 15
|
||||
1 3 22 30 31 23
|
||||
|
||||
|
||||
vertices
|
||||
32
|
||||
3
|
||||
-1.0000 0 0
|
||||
0 0 0
|
||||
-1.0000 0.3000 0
|
||||
0 0.3000 0
|
||||
-1.0000 0.6500 0
|
||||
0 0.6500 0
|
||||
-1.0000 1.0000 0
|
||||
0 1.0000 0
|
||||
-1.0000 0 0.3000
|
||||
0 0 0.3000
|
||||
-1.0000 0.3000 0.3500
|
||||
0 0.3000 0.3500
|
||||
-1.0000 0.6500 0.3000
|
||||
0 0.6500 0.3000
|
||||
-1.0000 1.0000 0.3000
|
||||
0 1.0000 0.3000
|
||||
-1.0000 0 0.6500
|
||||
0 0 0.6500
|
||||
-1.0000 0.3000 0.6500
|
||||
0 0.3000 0.6500
|
||||
-1.0000 0.6500 0.6500
|
||||
0 0.6500 0.6500
|
||||
-1.0000 1.0000 0.6500
|
||||
0 1.0000 0.6500
|
||||
-1.0000 0 1.0000
|
||||
0 0 1.0000
|
||||
-1.0000 0.3000 1.0000
|
||||
0 0.3000 1.0000
|
||||
-1.0000 0.6500 1.0000
|
||||
0 0.6500 1.0000
|
||||
-1.0000 1.0000 1.0000
|
||||
0 1.0000 1.0000
|
||||
@@ -1,68 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
# 1 nothing
|
||||
elements
|
||||
4
|
||||
1 5 0 1 3 2 6 7 9 8
|
||||
1 5 2 3 5 4 8 9 11 10
|
||||
1 5 6 7 9 8 12 13 15 14
|
||||
1 5 8 9 11 10 14 15 17 16
|
||||
|
||||
# 0 nothing
|
||||
# 1 dirichlet bc
|
||||
# 2 contact
|
||||
boundary
|
||||
16
|
||||
1 3 1 0 2 3
|
||||
1 3 3 2 4 5
|
||||
1 3 12 13 15 14
|
||||
1 3 14 15 17 16
|
||||
3 3 2 0 6 8
|
||||
3 3 4 2 8 10
|
||||
3 3 8 6 12 14
|
||||
3 3 10 8 14 16
|
||||
2 3 1 3 9 7
|
||||
2 3 3 5 11 9
|
||||
2 3 7 9 15 13
|
||||
2 3 9 11 17 15
|
||||
1 3 6 0 1 7
|
||||
1 3 12 6 7 13
|
||||
1 3 4 10 11 5
|
||||
1 3 10 16 17 11
|
||||
|
||||
vertices
|
||||
18
|
||||
3
|
||||
0 0.2464 0.2464
|
||||
0.5071 0.2464 0.2464
|
||||
0 0.5000 0.2464
|
||||
0.5071 0.5000 0.2464
|
||||
0 0.7536 0.2464
|
||||
0.5071 0.7536 0.2464
|
||||
0 0.2464 0.5000
|
||||
0.5071 0.2464 0.5000
|
||||
0 0.5000 0.5000
|
||||
0.5071 0.5000 0.5000
|
||||
0 0.7536 0.5000
|
||||
0.5071 0.7536 0.5000
|
||||
0 0.2464 0.7536
|
||||
0.5071 0.2464 0.7536
|
||||
0 0.5000 0.7536
|
||||
0.5071 0.5000 0.7536
|
||||
0 0.7536 0.7536
|
||||
0.5071 0.7536 0.7536
|
||||
@@ -1,70 +0,0 @@
|
||||
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
# 1 nothing
|
||||
elements
|
||||
4
|
||||
1 5 0 1 3 2 6 7 9 8
|
||||
1 5 2 3 5 4 8 9 11 10
|
||||
1 5 6 7 9 8 12 13 15 14
|
||||
1 5 8 9 11 10 14 15 17 16
|
||||
|
||||
# 0 nothing
|
||||
# 1 dirichlet bc
|
||||
# 2 contact
|
||||
boundary
|
||||
16
|
||||
1 3 1 0 2 3
|
||||
1 3 3 2 4 5
|
||||
1 3 12 13 15 14
|
||||
1 3 14 15 17 16
|
||||
3 3 2 0 6 8
|
||||
3 3 4 2 8 10
|
||||
3 3 8 6 12 14
|
||||
3 3 10 8 14 16
|
||||
2 3 1 3 9 7
|
||||
2 3 3 5 11 9
|
||||
2 3 7 9 15 13
|
||||
2 3 9 11 17 15
|
||||
1 3 6 0 1 7
|
||||
1 3 12 6 7 13
|
||||
1 3 4 10 11 5
|
||||
1 3 10 16 17 11
|
||||
|
||||
vertices
|
||||
18
|
||||
3
|
||||
|
||||
0.000000000000 0.145770950245 0.443895630208
|
||||
0.507100000000 0.145770950245 0.443895630208
|
||||
0.000000000000 0.350937660019 0.294833290227
|
||||
0.507100000000 0.350937660019 0.294833290227
|
||||
0.000000000000 0.556104369792 0.145770950245
|
||||
0.507100000000 0.556104369792 0.145770950245
|
||||
0.000000000000 0.294833290227 0.649062339981
|
||||
0.507100000000 0.294833290227 0.649062339981
|
||||
0.000000000000 0.500000000000 0.500000000000
|
||||
0.507100000000 0.500000000000 0.500000000000
|
||||
0.000000000000 0.705166709773 0.350937660019
|
||||
0.507100000000 0.705166709773 0.350937660019
|
||||
0.000000000000 0.443895630208 0.854229049755
|
||||
0.507100000000 0.443895630208 0.854229049755
|
||||
0.000000000000 0.649062339981 0.705166709773
|
||||
0.507100000000 0.649062339981 0.705166709773
|
||||
0.000000000000 0.854229049755 0.556104369792
|
||||
0.507100000000 0.854229049755 0.556104369792
|
||||
@@ -1,254 +0,0 @@
|
||||
// Parallel contact example
|
||||
//
|
||||
// Compile with: make pcontact_driver
|
||||
// sample run
|
||||
// mpirun -np 6 ./pcontact_driver -sr 2 -pr 2
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "ipsolver/ParIPsolver.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Mpi::Init();
|
||||
int myid = Mpi::WorldRank();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
Hypre::Init();
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "meshes/merged.mesh";
|
||||
int order = 1;
|
||||
int sref = 0;
|
||||
int pref = 0;
|
||||
Array<int> attr;
|
||||
Array<int> m_attr;
|
||||
bool visualization = true;
|
||||
bool paraview = false;
|
||||
double linsolvertol = 1e-6;
|
||||
int relax_type = 8;
|
||||
double optimizer_tol = 1e-6;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&attr, "-at", "--attributes-surf",
|
||||
"Attributes of boundary faces on contact surface for mesh 2.");
|
||||
args.AddOption(&sref, "-sr", "--serial-refinements",
|
||||
"Number of uniform refinements.");
|
||||
args.AddOption(&pref, "-pr", "--parallel-refinements",
|
||||
"Number of uniform refinements.");
|
||||
args.AddOption(&linsolvertol, "-stol", "--solver-tol",
|
||||
"Linear Solver Tolerance.");
|
||||
args.AddOption(&optimizer_tol, "-otol", "--optimizer-tol",
|
||||
"Interior Point Solver Tolerance.");
|
||||
args.AddOption(&relax_type, "-rt", "--relax-type",
|
||||
"Selection of Smoother for AMG");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(¶view, "-paraview", "--paraview", "-no-paraview",
|
||||
"--no-paraview",
|
||||
"Enable or disable ParaView visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
Mesh * merged_mesh = new Mesh(mesh_file,1);
|
||||
|
||||
|
||||
Array<int> attr1; attr1.Append(1);
|
||||
Array<int> attr2; attr2.Append(2);
|
||||
Mesh * mesh1 = new Mesh(SubMesh::CreateFromDomain(*merged_mesh,attr1));
|
||||
Mesh * mesh2 = new Mesh(SubMesh::CreateFromDomain(*merged_mesh,attr2));
|
||||
|
||||
for (int i = 0; i<sref; i++)
|
||||
{
|
||||
mesh1->UniformRefinement();
|
||||
mesh2->UniformRefinement();
|
||||
}
|
||||
for (int i = 0; i<mesh1->GetNE(); i++)
|
||||
{
|
||||
mesh1->SetAttribute(i,1);
|
||||
}
|
||||
mesh1->SetAttributes();
|
||||
for (int i = 0; i<mesh2->GetNE(); i++)
|
||||
{
|
||||
mesh2->SetAttribute(i,2);
|
||||
}
|
||||
mesh2->SetAttributes();
|
||||
|
||||
ParMesh * pmesh1 = new ParMesh(MPI_COMM_WORLD,*mesh1);
|
||||
ParMesh * pmesh2 = new ParMesh(MPI_COMM_WORLD,*mesh2);
|
||||
|
||||
for (int i = 0; i<pref; i++)
|
||||
{
|
||||
pmesh1->UniformRefinement();
|
||||
pmesh2->UniformRefinement();
|
||||
}
|
||||
|
||||
MFEM_VERIFY(pmesh1->GetNE(), "Empty partition mesh1");
|
||||
MFEM_VERIFY(pmesh2->GetNE(), "Empty partition mesh2");
|
||||
|
||||
ParElasticityProblem * prob1 = new ParElasticityProblem(pmesh1,order);
|
||||
ParElasticityProblem * prob2 = new ParElasticityProblem(pmesh2,order);
|
||||
|
||||
|
||||
Vector lambda1(prob1->GetMesh()->attributes.Max()); lambda1 = 57.6923076923;
|
||||
Vector mu1(prob1->GetMesh()->attributes.Max()); mu1 = 38.4615384615;
|
||||
Vector lambda2(prob2->GetMesh()->attributes.Max()); lambda2 = 57.6923076923;
|
||||
Vector mu2(prob2->GetMesh()->attributes.Max()); mu2 = 38.4615384615;
|
||||
|
||||
prob1->SetLambda(lambda1); prob1->SetMu(mu1);
|
||||
prob2->SetLambda(lambda2); prob2->SetMu(mu2);
|
||||
|
||||
ParContactProblem contact(prob1,prob2);
|
||||
QPOptParContactProblem qpopt(&contact);
|
||||
int numconstr = contact.GetGlobalNumConstraints();
|
||||
|
||||
ParInteriorPointSolver optimizer(&qpopt);
|
||||
|
||||
optimizer.SetTol(optimizer_tol);
|
||||
optimizer.SetMaxIter(50);
|
||||
|
||||
int linsolver = 2;
|
||||
optimizer.SetLinearSolver(linsolver);
|
||||
optimizer.SetLinearSolveTol(linsolvertol);
|
||||
optimizer.SetLinearSolveRelaxType(relax_type);
|
||||
|
||||
ParGridFunction x1 = prob1->GetDisplacementGridFunction();
|
||||
ParGridFunction x2 = prob2->GetDisplacementGridFunction();
|
||||
|
||||
int ndofs1 = prob1->GetNumTDofs();
|
||||
int ndofs2 = prob2->GetNumTDofs();
|
||||
int gndofs1 = prob1->GetGlobalNumDofs();
|
||||
int gndofs2 = prob2->GetGlobalNumDofs();
|
||||
int ndofs = ndofs1 + ndofs2;
|
||||
|
||||
Vector X1 = x1.GetTrueVector();
|
||||
Vector X2 = x2.GetTrueVector();
|
||||
|
||||
Vector x0(ndofs); x0 = 0.0;
|
||||
x0.SetVector(X1,0);
|
||||
x0.SetVector(X2,X1.Size());
|
||||
|
||||
Vector xf(ndofs); xf = 0.0;
|
||||
optimizer.Mult(x0, xf);
|
||||
|
||||
double Einitial = contact.E(x0);
|
||||
double Efinal = contact.E(xf);
|
||||
Array<int> & CGiterations = optimizer.GetCGIterNumbers();
|
||||
if (Mpi::Root())
|
||||
{
|
||||
mfem::out << endl;
|
||||
mfem::out << " Initial Energy objective = " << Einitial << endl;
|
||||
mfem::out << " Final Energy objective = " << Efinal << endl;
|
||||
mfem::out << " Global number of dofs = " << gndofs1 + gndofs2 << endl;
|
||||
mfem::out << " Global number of constraints = " << numconstr << endl;
|
||||
mfem::out << " CG iteration numbers = " ;
|
||||
CGiterations.Print(mfem::out, CGiterations.Size());
|
||||
}
|
||||
|
||||
MFEM_VERIFY(optimizer.GetConverged(),
|
||||
"Interior point solver did not converge.");
|
||||
|
||||
|
||||
if (visualization || paraview)
|
||||
{
|
||||
ParFiniteElementSpace * fes1 = prob1->GetFESpace();
|
||||
ParFiniteElementSpace * fes2 = prob2->GetFESpace();
|
||||
|
||||
ParMesh * pmesh_1 = fes1->GetParMesh();
|
||||
ParMesh * pmesh_2 = fes2->GetParMesh();
|
||||
|
||||
Vector X1_new(xf.GetData(),fes1->GetTrueVSize());
|
||||
Vector X2_new(&xf.GetData()[fes1->GetTrueVSize()],fes2->GetTrueVSize());
|
||||
|
||||
ParGridFunction x1_gf(fes1);
|
||||
ParGridFunction x2_gf(fes2);
|
||||
|
||||
x1_gf.SetFromTrueDofs(X1_new);
|
||||
x2_gf.SetFromTrueDofs(X2_new);
|
||||
|
||||
pmesh_1->MoveNodes(x1_gf);
|
||||
pmesh_2->MoveNodes(x2_gf);
|
||||
|
||||
if (paraview)
|
||||
{
|
||||
ParaViewDataCollection paraview_dc1("QPContactBody1", pmesh_1);
|
||||
paraview_dc1.SetPrefixPath("ParaView");
|
||||
paraview_dc1.SetLevelsOfDetail(1);
|
||||
paraview_dc1.SetDataFormat(VTKFormat::BINARY);
|
||||
paraview_dc1.SetHighOrderOutput(true);
|
||||
paraview_dc1.SetCycle(0);
|
||||
paraview_dc1.SetTime(0.0);
|
||||
paraview_dc1.RegisterField("Body1", &x1_gf);
|
||||
paraview_dc1.Save();
|
||||
|
||||
ParaViewDataCollection paraview_dc2("QPContactBody2", pmesh_2);
|
||||
paraview_dc2.SetPrefixPath("ParaView");
|
||||
paraview_dc2.SetLevelsOfDetail(1);
|
||||
paraview_dc2.SetDataFormat(VTKFormat::BINARY);
|
||||
paraview_dc2.SetHighOrderOutput(true);
|
||||
paraview_dc2.SetCycle(0);
|
||||
paraview_dc2.SetTime(0.0);
|
||||
paraview_dc2.RegisterField("Body2", &x2_gf);
|
||||
paraview_dc2.Save();
|
||||
}
|
||||
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
|
||||
{
|
||||
socketstream sol_sock1(vishost, visport);
|
||||
sol_sock1.precision(8);
|
||||
sol_sock1 << "parallel " << num_procs << " " << myid << "\n"
|
||||
<< "solution\n" << *pmesh_1 << x1_gf << flush;
|
||||
}
|
||||
{
|
||||
socketstream sol_sock2(vishost, visport);
|
||||
sol_sock2.precision(8);
|
||||
sol_sock2 << "parallel " << num_procs << " " << myid << "\n"
|
||||
<< "solution\n" << *pmesh_2 << x2_gf << flush;
|
||||
}
|
||||
|
||||
// {
|
||||
// socketstream sol_sock(vishost, visport);
|
||||
// sol_sock.precision(8);
|
||||
// sol_sock << "parallel " << 2*num_procs << " " << myid << "\n"
|
||||
// << "solution\n" << *pmesh_1 << x1_gf << flush;
|
||||
// }
|
||||
// {
|
||||
// socketstream sol_sock(vishost, visport);
|
||||
// sol_sock.precision(8);
|
||||
// sol_sock << "parallel " << 2*num_procs << " " << myid+num_procs << "\n"
|
||||
// << "solution\n" << *pmesh_2 << x2_gf << flush;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
delete prob2;
|
||||
delete prob1;
|
||||
delete pmesh2;
|
||||
delete pmesh1;
|
||||
// delete mesh1;
|
||||
// delete mesh2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,516 +0,0 @@
|
||||
#include "parproblems.hpp"
|
||||
|
||||
void ParElasticityProblem::Init()
|
||||
{
|
||||
int dim = pmesh->Dimension();
|
||||
fec = new H1_FECollection(order,dim);
|
||||
fes = new ParFiniteElementSpace(pmesh,fec,dim,Ordering::byVDIM);
|
||||
ndofs = fes->GetVSize();
|
||||
ntdofs = fes->GetTrueVSize();
|
||||
gndofs = fes->GlobalTrueVSize();
|
||||
pmesh->SetNodalFESpace(fes);
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
|
||||
}
|
||||
ess_bdr = 0; ess_bdr[1] = 1;
|
||||
fes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list);
|
||||
// Solution GridFunction
|
||||
x.SetSpace(fes); x = 0.0;
|
||||
// RHS
|
||||
b.Update(fes);
|
||||
|
||||
// Elasticity operator
|
||||
lambda.SetSize(pmesh->attributes.Max()); lambda = 57.6923076923;
|
||||
mu.SetSize(pmesh->attributes.Max()); mu = 38.4615384615;
|
||||
|
||||
lambda_cf.UpdateConstants(lambda);
|
||||
mu_cf.UpdateConstants(mu);
|
||||
|
||||
a = new ParBilinearForm(fes);
|
||||
a->AddDomainIntegrator(new ElasticityIntegrator(lambda_cf,mu_cf));
|
||||
}
|
||||
|
||||
void ParElasticityProblem::FormLinearSystem()
|
||||
{
|
||||
if (!formsystem)
|
||||
{
|
||||
formsystem = true;
|
||||
b.Assemble();
|
||||
a->Assemble();
|
||||
a->FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
}
|
||||
}
|
||||
|
||||
void ParElasticityProblem::UpdateLinearSystem()
|
||||
{
|
||||
if (formsystem)
|
||||
{
|
||||
b.Update();
|
||||
a->Update();
|
||||
formsystem = false;
|
||||
}
|
||||
FormLinearSystem();
|
||||
}
|
||||
|
||||
ParContactProblem::ParContactProblem(ParElasticityProblem * prob1_, ParElasticityProblem * prob2_)
|
||||
: prob1(prob1_), prob2(prob2_)
|
||||
{
|
||||
ParMesh* pmesh1 = prob1->GetMesh();
|
||||
comm = pmesh1->GetComm();
|
||||
MPI_Comm_rank(comm, &myid);
|
||||
MPI_Comm_size(comm, &numprocs);
|
||||
|
||||
dim = pmesh1->Dimension();
|
||||
nodes0.SetSpace(pmesh1->GetNodes()->FESpace());
|
||||
nodes0 = *pmesh1->GetNodes();
|
||||
nodes1 = pmesh1->GetNodes();
|
||||
Vector delta1(dim);
|
||||
delta1 = 0.0; delta1[0] = 0.1;
|
||||
prob1->SetDisplacementDirichletData(delta1);
|
||||
prob1->FormLinearSystem();
|
||||
|
||||
Vector delta2(dim);
|
||||
delta2 = 0.0;
|
||||
prob2->SetDisplacementDirichletData(delta2);
|
||||
prob2->FormLinearSystem();
|
||||
|
||||
int ndof1 = prob1->GetNumTDofs();
|
||||
int ndof2 = prob2->GetNumTDofs();
|
||||
|
||||
tdof_offsets.SetSize(3);
|
||||
tdof_offsets[0] = 0;
|
||||
tdof_offsets[1] = ndof1;
|
||||
tdof_offsets[2] = ndof2;
|
||||
tdof_offsets.PartialSum();
|
||||
|
||||
Array2D<HypreParMatrix*> A(2,2);
|
||||
A(0,0) = &prob1->GetOperator();
|
||||
A(1,1) = &prob2->GetOperator();
|
||||
A(1,0) = nullptr;
|
||||
A(0,1) = nullptr;
|
||||
K = HypreParMatrixFromBlocks(A);
|
||||
|
||||
B = new BlockVector(tdof_offsets);
|
||||
B->GetBlock(0).Set(1.0, prob1->GetRHS());
|
||||
B->GetBlock(1).Set(1.0, prob2->GetRHS());
|
||||
|
||||
ComputeContactVertices();
|
||||
}
|
||||
|
||||
void ParContactProblem::ComputeContactVertices()
|
||||
{
|
||||
if (gnpoints>0) return;
|
||||
|
||||
ParMesh * pmesh1 = prob1->GetMesh();
|
||||
ParMesh * pmesh2 = prob2->GetMesh();
|
||||
dim = pmesh1->Dimension();
|
||||
|
||||
vfes1 = new ParFiniteElementSpace(pmesh1, prob1->GetFECol());
|
||||
vfes2 = new ParFiniteElementSpace(pmesh2, prob2->GetFECol());
|
||||
|
||||
int gnv1 = vfes1->GlobalTrueVSize();
|
||||
int gnv2 = vfes2->GlobalTrueVSize();
|
||||
gnv = gnv1+gnv2;
|
||||
int nv1 = vfes1->GetTrueVSize();
|
||||
int nv2 = vfes2->GetTrueVSize();
|
||||
nv = nv1+nv2;
|
||||
|
||||
vertices1.SetSize(pmesh1->GetNV());
|
||||
vertices2.SetSize(pmesh2->GetNV());
|
||||
|
||||
for (int i = 0; i<pmesh1->GetNV(); i++)
|
||||
{
|
||||
vertices1[i] = i;
|
||||
}
|
||||
pmesh1->GetGlobalVertexIndices(vertices1);
|
||||
|
||||
for (int i = 0; i<pmesh2->GetNV(); i++)
|
||||
{
|
||||
vertices2[i] = i;
|
||||
}
|
||||
pmesh2->GetGlobalVertexIndices(vertices2);
|
||||
|
||||
int voffset2 = vfes2->GetMyTDofOffset();
|
||||
|
||||
std::vector<int> vertex2_offsets;
|
||||
ComputeTdofOffsets(comm,voffset2, vertex2_offsets);
|
||||
|
||||
Array<int> vert;
|
||||
for (int b=0; b<pmesh2->GetNBE(); b++)
|
||||
{
|
||||
if (pmesh2->GetBdrAttribute(b) == 3)
|
||||
{
|
||||
pmesh2->GetBdrElementVertices(b, vert);
|
||||
for (auto v : vert)
|
||||
{
|
||||
if (myid != get_rank(vertices2[v],vertex2_offsets)) { continue; }
|
||||
contact_vertices.insert(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
npoints = contact_vertices.size();
|
||||
|
||||
MPI_Allreduce(&npoints, &gnpoints,1,MPI_INT,MPI_SUM,pmesh1->GetComm());
|
||||
int constrains_offset;
|
||||
MPI_Scan(&npoints,&constrains_offset,1,MPI_INT,MPI_SUM,pmesh1->GetComm());
|
||||
|
||||
constrains_offset-=npoints;
|
||||
constraints_starts.SetSize(2);
|
||||
constraints_starts[0] = constrains_offset;
|
||||
constraints_starts[1] = constrains_offset+npoints;
|
||||
|
||||
ComputeTdofOffsets(comm,constrains_offset, constraints_offsets);
|
||||
}
|
||||
|
||||
void ParContactProblem::ComputeGapFunctionAndDerivatives(const Vector & displ1, const Vector &displ2)
|
||||
{
|
||||
ComputeContactVertices();
|
||||
ParMesh * pmesh1 = prob1->GetMesh();
|
||||
ParMesh * pmesh2 = prob2->GetMesh();
|
||||
|
||||
ParGridFunction displ1_gf(prob1->GetFESpace());
|
||||
ParGridFunction displ2_gf(prob2->GetFESpace());
|
||||
|
||||
displ1_gf.SetFromTrueDofs(displ1);
|
||||
displ2_gf.SetFromTrueDofs(displ2);
|
||||
|
||||
Array<int> conn2(npoints);
|
||||
Vector xyz(dim * npoints);
|
||||
|
||||
int cnt = 0;
|
||||
for (auto v : contact_vertices)
|
||||
{
|
||||
for (int d = 0; d<dim; d++)
|
||||
{
|
||||
xyz(cnt*dim + d) = pmesh2->GetVertex(v)[d]+displ2_gf[v*dim+d];
|
||||
}
|
||||
conn2[cnt] = vertices2[v];
|
||||
cnt++;
|
||||
}
|
||||
|
||||
MFEM_VERIFY(cnt == npoints, "");
|
||||
gapv.SetSize(npoints*dim); gapv = 0.0;
|
||||
// segment reference coordinates of the closest point
|
||||
Vector xi1(npoints*(dim-1));
|
||||
Array<int> conn1(npoints*4);
|
||||
DenseMatrix coordsm(npoints*4, dim);
|
||||
// add(nodes0, displ1_gf, *nodes1);
|
||||
FindPointsInMesh(*pmesh1, vertices1, conn2, displ1_gf, xyz, conn1, xi1, coordsm);
|
||||
if (M)
|
||||
{
|
||||
delete M;
|
||||
for (int i = 0; i<dM.Size(); i++)
|
||||
{
|
||||
delete dM[i];
|
||||
}
|
||||
dM.SetSize(0);
|
||||
}
|
||||
|
||||
int ndofs1 = prob1->GetFESpace()->GetTrueVSize();
|
||||
int ndofs2 = prob2->GetFESpace()->GetTrueVSize();
|
||||
int gndofs1 = prob1->GetFESpace()->GlobalTrueVSize();
|
||||
int gndofs2 = prob2->GetFESpace()->GlobalTrueVSize();
|
||||
|
||||
Array<int> npts(numprocs);
|
||||
MPI_Allgather(&npoints,1,MPI_INT,&npts[0],1,MPI_INT,comm);
|
||||
npts.PartialSum(); npts.Prepend(0);
|
||||
|
||||
SparseMatrix S1(gnpoints,gndofs1);
|
||||
SparseMatrix S2(gnpoints,gndofs2);
|
||||
Array<SparseMatrix *> dS11;
|
||||
Array<SparseMatrix *> dS12;
|
||||
Array<SparseMatrix *> dS21;
|
||||
Array<SparseMatrix *> dS22;
|
||||
|
||||
// local to global map for constraints
|
||||
Array<int> points_map(npoints);
|
||||
cnt = 0;
|
||||
for (int i = 0; i<gnpoints; i++)
|
||||
{
|
||||
if (i >= npts[myid] && i< npts[myid+1])
|
||||
{
|
||||
points_map[cnt++] = i;
|
||||
}
|
||||
}
|
||||
if (compute_hessians)
|
||||
{
|
||||
dS11.SetSize(gnpoints);
|
||||
dS12.SetSize(gnpoints);
|
||||
dS21.SetSize(gnpoints);
|
||||
dS22.SetSize(gnpoints);
|
||||
for (int i = 0; i<gnpoints; i++)
|
||||
{
|
||||
if (i >= npts[myid] && i< npts[myid+1])
|
||||
{
|
||||
dS11[i] = new SparseMatrix(gndofs1,gndofs1);
|
||||
dS12[i] = new SparseMatrix(gndofs1,gndofs2);
|
||||
dS21[i] = new SparseMatrix(gndofs2,gndofs1);
|
||||
dS22[i] = new SparseMatrix(gndofs2,gndofs2);
|
||||
}
|
||||
else
|
||||
{
|
||||
dS11[i] = nullptr;
|
||||
dS12[i] = nullptr;
|
||||
dS21[i] = nullptr;
|
||||
dS22[i] = nullptr;
|
||||
}
|
||||
}
|
||||
Assemble_Contact(xyz, xi1, coordsm, conn2, conn1, gapv, S1,S2,
|
||||
dS11,dS12,dS21,dS22);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assemble_Contact(xyz, xi1, coordsm, conn2, conn1, gapv, S1,S2, points_map);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Redistribute the M block matrix [M1 M2]
|
||||
// --------------------------------------------------------------------
|
||||
int offset = constraints_offsets[myid];
|
||||
MPICommunicator Mcomm1(comm,offset,gnpoints);
|
||||
SparseMatrix localS1(npoints,gndofs1);
|
||||
Mcomm1.Communicate(S1,localS1);
|
||||
MPICommunicator Mcomm2(comm,offset,gnpoints);
|
||||
SparseMatrix localS2(npoints,gndofs2);
|
||||
Mcomm2.Communicate(S2,localS2);
|
||||
|
||||
MFEM_VERIFY(HYPRE_AssumedPartitionCheck(), "Hypre_AssumedPartitionCheck is False");
|
||||
|
||||
// Construct M row and col starts to construct HypreParMatrix
|
||||
int M1rows[2], M2rows[2];
|
||||
int M1cols[2], M2cols[2];
|
||||
M1rows[0] = constraints_starts[0];
|
||||
M1rows[1] = constraints_starts[1];
|
||||
|
||||
M2rows[0] = constraints_starts[0];
|
||||
M2rows[1] = constraints_starts[1];
|
||||
|
||||
M1cols[0] = prob1->GetFESpace()->GetTrueDofOffsets()[0];
|
||||
M1cols[1] = prob1->GetFESpace()->GetTrueDofOffsets()[1];
|
||||
|
||||
M2cols[0] = prob2->GetFESpace()->GetTrueDofOffsets()[0];
|
||||
M2cols[1] = prob2->GetFESpace()->GetTrueDofOffsets()[1];
|
||||
|
||||
Array2D<HypreParMatrix*> blockM(1,2);
|
||||
blockM(0,0) = new HypreParMatrix(comm,npoints,gnpoints,gndofs1,
|
||||
localS1.GetI(), localS1.GetJ(),localS1.GetData(),
|
||||
M1rows,M1cols);
|
||||
|
||||
blockM(0,1) = new HypreParMatrix(comm,npoints,gnpoints,gndofs2,
|
||||
localS2.GetI(), localS2.GetJ(),localS2.GetData(),
|
||||
M2rows,M2cols);
|
||||
|
||||
M = HypreParMatrixFromBlocks(blockM);
|
||||
delete blockM(0,0);
|
||||
delete blockM(0,1);
|
||||
blockM.DeleteAll();
|
||||
|
||||
if (compute_hessians)
|
||||
{
|
||||
Array<SparseMatrix*> localdS11(gnpoints);
|
||||
Array<SparseMatrix*> localdS12(gnpoints);
|
||||
Array<SparseMatrix*> localdS21(gnpoints);
|
||||
Array<SparseMatrix*> localdS22(gnpoints);
|
||||
for (int k = 0; k<gnpoints; k++)
|
||||
{
|
||||
localdS11[k] = new SparseMatrix(ndofs1,gndofs1);
|
||||
localdS12[k] = new SparseMatrix(ndofs1,gndofs2);
|
||||
localdS21[k] = new SparseMatrix(ndofs2,gndofs1);
|
||||
localdS22[k] = new SparseMatrix(ndofs2,gndofs2);
|
||||
}
|
||||
|
||||
int offset1 = prob1->GetFESpace()->GetMyTDofOffset();
|
||||
int offset2 = prob2->GetFESpace()->GetMyTDofOffset();
|
||||
|
||||
MPICommunicator dmcomm11(comm, offset1, gndofs1);
|
||||
dmcomm11.Communicate(dS11,localdS11);
|
||||
for (int k = 0; k<gnpoints; k++) { delete dS11[k]; }
|
||||
|
||||
MPICommunicator dmcomm12(comm, offset1, gndofs1);
|
||||
dmcomm12.Communicate(dS12,localdS12);
|
||||
for (int k = 0; k<gnpoints; k++) { delete dS12[k]; }
|
||||
|
||||
MPICommunicator dmcomm21(comm, offset2, gndofs2);
|
||||
dmcomm21.Communicate(dS21,localdS21);
|
||||
for (int k = 0; k<gnpoints; k++) { delete dS21[k]; }
|
||||
|
||||
MPICommunicator dmcomm22(comm, offset2, gndofs2);
|
||||
dmcomm22.Communicate(dS22,localdS22);
|
||||
for (int k = 0; k<gnpoints; k++) { delete dS22[k]; }
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Redistribute the block dM matrices [dM11 dM12; dM21 dM22]
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// Construct dMi HypreParMatrix
|
||||
Array2D<HypreParMatrix *> dMs(2,2);
|
||||
dM.SetSize(gnpoints);
|
||||
int * offs1 = prob1->GetFESpace()->GetTrueDofOffsets();
|
||||
int * offs2 = prob2->GetFESpace()->GetTrueDofOffsets();
|
||||
for (int i = 0; i<gnpoints; i++)
|
||||
{
|
||||
dMs(0,0) = new HypreParMatrix(comm, ndofs1, gndofs1, gndofs1,
|
||||
localdS11[i]->GetI(), localdS11[i]->GetJ(),
|
||||
localdS11[i]->GetData(),
|
||||
offs1,offs1);
|
||||
delete localdS11[i];
|
||||
dMs(0,1) = new HypreParMatrix(comm, ndofs1, gndofs1, gndofs2,
|
||||
localdS12[i]->GetI(), localdS12[i]->GetJ(),
|
||||
localdS12[i]->GetData(),
|
||||
offs1,offs2);
|
||||
delete localdS12[i];
|
||||
dMs(1,0) = new HypreParMatrix(comm, ndofs2, gndofs2, gndofs1,
|
||||
localdS21[i]->GetI(), localdS21[i]->GetJ(),
|
||||
localdS21[i]->GetData(),
|
||||
offs2,offs1);
|
||||
delete localdS21[i];
|
||||
dMs(1,1) = new HypreParMatrix(comm, ndofs2, gndofs2, gndofs2,
|
||||
localdS22[i]->GetI(), localdS22[i]->GetJ(),
|
||||
localdS22[i]->GetData(),
|
||||
offs2,offs2);
|
||||
delete localdS22[i];
|
||||
|
||||
dM[i] = HypreParMatrixFromBlocks(dMs);
|
||||
delete dMs(0,0);
|
||||
delete dMs(0,1);
|
||||
delete dMs(1,0);
|
||||
delete dMs(1,1);
|
||||
}
|
||||
dMs.DeleteAll();
|
||||
}
|
||||
}
|
||||
|
||||
double ParContactProblem::E(const Vector & d)
|
||||
{
|
||||
Vector kd(K->Height());
|
||||
K->Mult(d,kd);
|
||||
return 0.5 * InnerProduct(comm,d, kd) - InnerProduct(comm,d, *B);
|
||||
}
|
||||
|
||||
void ParContactProblem::DdE(const Vector &d, Vector &gradE)
|
||||
{
|
||||
gradE.SetSize(K->Height());
|
||||
K->Mult(d, gradE);
|
||||
gradE.Add(-1.0, *B);
|
||||
}
|
||||
|
||||
HypreParMatrix* ParContactProblem::DddE(const Vector &d)
|
||||
{
|
||||
return K;
|
||||
}
|
||||
|
||||
void ParContactProblem::g(const Vector &d, Vector &gd, bool compute_hessians_)
|
||||
{
|
||||
compute_hessians = compute_hessians_;
|
||||
int ndof1 = prob1->GetNumTDofs();
|
||||
int ndof2 = prob2->GetNumTDofs();
|
||||
double * data = d.GetData();
|
||||
Vector displ1(data,ndof1);
|
||||
Vector displ2(&data[ndof1],ndof2);
|
||||
|
||||
if (recompute)
|
||||
{
|
||||
ComputeGapFunctionAndDerivatives(displ1, displ2);
|
||||
recompute = false;
|
||||
}
|
||||
|
||||
gd = GetGapFunction();
|
||||
}
|
||||
|
||||
HypreParMatrix* ParContactProblem::Ddg(const Vector &d)
|
||||
{
|
||||
return GetJacobian();
|
||||
}
|
||||
|
||||
HypreParMatrix* ParContactProblem::lDddg(const Vector &d, const Vector &l)
|
||||
{
|
||||
return nullptr; // for now
|
||||
}
|
||||
|
||||
|
||||
QPOptParContactProblem::QPOptParContactProblem(ParContactProblem * problem_)
|
||||
: problem(problem_)
|
||||
{
|
||||
dimU = problem->GetNumDofs();
|
||||
dimM = problem->GetNumContraints();
|
||||
dimC = problem->GetNumContraints();
|
||||
ml.SetSize(dimM); ml = 0.0;
|
||||
Vector negone(dimM); negone = -1.0;
|
||||
SparseMatrix diag(negone);
|
||||
|
||||
int gsize = problem->GetGlobalNumConstraints();
|
||||
int * rows = problem->GetConstraintsStarts().GetData();
|
||||
|
||||
NegId = new HypreParMatrix(problem->GetComm(),gsize, rows,&diag);
|
||||
HypreStealOwnership(*NegId, diag);
|
||||
}
|
||||
|
||||
int QPOptParContactProblem::GetDimU() { return dimU; }
|
||||
|
||||
int QPOptParContactProblem::GetDimM() { return dimM; }
|
||||
|
||||
int QPOptParContactProblem::GetDimC() { return dimC; }
|
||||
|
||||
Vector & QPOptParContactProblem::Getml() { return ml; }
|
||||
|
||||
HypreParMatrix * QPOptParContactProblem::Duuf(const BlockVector & x)
|
||||
{
|
||||
return problem->DddE(x.GetBlock(0));
|
||||
}
|
||||
|
||||
HypreParMatrix * QPOptParContactProblem::Dumf(const BlockVector & x)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HypreParMatrix * QPOptParContactProblem::Dmuf(const BlockVector & x)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HypreParMatrix * QPOptParContactProblem::Dmmf(const BlockVector & x)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HypreParMatrix * QPOptParContactProblem::Duc(const BlockVector & x)
|
||||
{
|
||||
return problem->Ddg(x.GetBlock(0));
|
||||
}
|
||||
|
||||
HypreParMatrix * QPOptParContactProblem::Dmc(const BlockVector & x)
|
||||
{
|
||||
return NegId;
|
||||
}
|
||||
|
||||
HypreParMatrix * QPOptParContactProblem::lDuuc(const BlockVector & x, const Vector & l)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void QPOptParContactProblem::c(const BlockVector &x, Vector & y)
|
||||
{
|
||||
Vector g0;
|
||||
problem->g(x.GetBlock(0),g0, false); // gap function
|
||||
g0.Add(-1.0, x.GetBlock(1));
|
||||
problem->GetJacobian()->Mult(x.GetBlock(0),y);
|
||||
y.Add(1.0, g0);
|
||||
}
|
||||
|
||||
double QPOptParContactProblem::CalcObjective(const BlockVector & x)
|
||||
{
|
||||
return problem->E(x.GetBlock(0));
|
||||
}
|
||||
|
||||
void QPOptParContactProblem::CalcObjectiveGrad(const BlockVector & x, BlockVector & y)
|
||||
{
|
||||
problem->DdE(x.GetBlock(0), y.GetBlock(0));
|
||||
y.GetBlock(1) = 0.0;
|
||||
}
|
||||
|
||||
QPOptParContactProblem::~QPOptParContactProblem()
|
||||
{
|
||||
delete NegId;
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
|
||||
#include "parproblems_util.hpp"
|
||||
|
||||
class ParElasticityProblem
|
||||
{
|
||||
private:
|
||||
MPI_Comm comm;
|
||||
bool formsystem = false;
|
||||
ParMesh * pmesh = nullptr;
|
||||
int order;
|
||||
int ndofs;
|
||||
int ntdofs;
|
||||
int gndofs;
|
||||
FiniteElementCollection * fec = nullptr;
|
||||
ParFiniteElementSpace * fes = nullptr;
|
||||
Vector lambda, mu;
|
||||
PWConstCoefficient lambda_cf, mu_cf;
|
||||
Array<int> ess_bdr, ess_tdof_list;
|
||||
ParBilinearForm *a=nullptr;
|
||||
ParLinearForm b;
|
||||
ParGridFunction x;
|
||||
HypreParMatrix A;
|
||||
Vector B,X;
|
||||
void Init();
|
||||
bool own_mesh;
|
||||
public:
|
||||
ParElasticityProblem(MPI_Comm comm_, const char *mesh_file , int sref, int pref, int order_ = 1) : comm(comm_), order(order_)
|
||||
{
|
||||
own_mesh = true;
|
||||
Mesh * mesh = new Mesh(mesh_file,1,1);
|
||||
for (int i = 0; i<sref; i++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
pmesh = new ParMesh(comm,*mesh);
|
||||
MFEM_VERIFY(pmesh->GetNE(), "ParElasticityProblem::Empty partition");
|
||||
delete mesh;
|
||||
for (int i = 0; i<pref; i++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
Init();
|
||||
}
|
||||
|
||||
ParElasticityProblem(ParMesh * pmesh_, int order_ = 1) : pmesh(pmesh_), order(order_)
|
||||
{
|
||||
own_mesh = false;
|
||||
comm = pmesh->GetComm();
|
||||
Init();
|
||||
}
|
||||
|
||||
ParMesh * GetMesh() { return pmesh; }
|
||||
ParFiniteElementSpace * GetFESpace() { return fes; }
|
||||
FiniteElementCollection * GetFECol() { return fec; }
|
||||
int GetNumDofs() { return ndofs; }
|
||||
int GetNumTDofs() { return ntdofs; }
|
||||
int GetGlobalNumDofs() { return gndofs; }
|
||||
HypreParMatrix & GetOperator()
|
||||
{
|
||||
MFEM_VERIFY(formsystem, "System not formed yet. Call FormLinearSystem()");
|
||||
return A;
|
||||
}
|
||||
Vector & GetRHS()
|
||||
{
|
||||
MFEM_VERIFY(formsystem, "System not formed yet. Call FormLinearSystem()");
|
||||
return B;
|
||||
}
|
||||
|
||||
void SetLambda(const Vector & lambda_)
|
||||
{
|
||||
lambda = lambda_;
|
||||
lambda_cf.UpdateConstants(lambda);
|
||||
}
|
||||
void SetMu(const Vector & mu_)
|
||||
{
|
||||
mu = mu_;
|
||||
mu_cf.UpdateConstants(mu);
|
||||
}
|
||||
|
||||
void FormLinearSystem();
|
||||
void UpdateLinearSystem();
|
||||
|
||||
void SetDisplacementDirichletData(const Vector & delta)
|
||||
{
|
||||
VectorConstantCoefficient delta_cf(delta);
|
||||
x.ProjectBdrCoefficient(delta_cf,ess_bdr);
|
||||
};
|
||||
|
||||
ParGridFunction & GetDisplacementGridFunction() {return x;};
|
||||
Array<int> & GetEssentialDofs() {return ess_tdof_list;};
|
||||
|
||||
~ParElasticityProblem()
|
||||
{
|
||||
delete a;
|
||||
delete fes;
|
||||
delete fec;
|
||||
if (own_mesh)
|
||||
{
|
||||
delete pmesh;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ParContactProblem
|
||||
{
|
||||
private:
|
||||
MPI_Comm comm;
|
||||
int numprocs;
|
||||
int myid;
|
||||
ParElasticityProblem * prob1 = nullptr;
|
||||
ParElasticityProblem * prob2 = nullptr;
|
||||
ParFiniteElementSpace * vfes1 = nullptr;
|
||||
ParFiniteElementSpace * vfes2 = nullptr;
|
||||
int dim;
|
||||
GridFunction nodes0;
|
||||
GridFunction *nodes1 = nullptr;
|
||||
std::set<int> contact_vertices;
|
||||
bool recompute = true;
|
||||
bool compute_hessians = true;
|
||||
std::vector<int> dof_offsets;
|
||||
std::vector<int> vertex_offsets;
|
||||
std::vector<int> constraints_offsets;
|
||||
Array<int> tdof_offsets;
|
||||
Array<int> constraints_starts;
|
||||
Array<int> globalvertices1;
|
||||
Array<int> globalvertices2;
|
||||
Array<int> vertices2;
|
||||
Array<int> vertices1;
|
||||
|
||||
protected:
|
||||
int npoints=0;
|
||||
int gnpoints=0;
|
||||
int nv, gnv;
|
||||
HypreParMatrix * K = nullptr;
|
||||
BlockVector *B = nullptr;
|
||||
Vector gapv;
|
||||
HypreParMatrix * M=nullptr;
|
||||
Array<HypreParMatrix*> dM;
|
||||
void ComputeContactVertices();
|
||||
|
||||
public:
|
||||
ParContactProblem(ParElasticityProblem * prob1_, ParElasticityProblem * prob2_);
|
||||
|
||||
ParElasticityProblem * GetElasticityProblem1() {return prob1;}
|
||||
ParElasticityProblem * GetElasticityProblem2() {return prob2;}
|
||||
MPI_Comm GetComm() {return comm;}
|
||||
int GetNumDofs() {return K->Height();}
|
||||
int GetGlobalNumDofs() {return K->GetGlobalNumRows();}
|
||||
int GetNumContraints() {return npoints;}
|
||||
int GetGlobalNumConstraints() {return gnpoints;}
|
||||
|
||||
std::vector<int> & GetDofOffets() { return dof_offsets; }
|
||||
std::vector<int> & GetVertexOffsets() { return vertex_offsets; }
|
||||
std::vector<int> & GetConstraintsOffsets() { return constraints_offsets; }
|
||||
Array<int> & GetConstraintsStarts() { return constraints_starts; }
|
||||
|
||||
Vector & GetGapFunction() {return gapv;}
|
||||
|
||||
HypreParMatrix * GetJacobian() {return M;}
|
||||
Array<HypreParMatrix*> & GetHessian() {return dM;}
|
||||
void ComputeGapFunctionAndDerivatives(const Vector & displ1, const Vector &displ2);
|
||||
|
||||
double E(const Vector & d);
|
||||
void DdE(const Vector &d, Vector &gradE);
|
||||
HypreParMatrix* DddE(const Vector &d);
|
||||
void g(const Vector &d, Vector &gd, bool compute_hessians_ = true);
|
||||
HypreParMatrix* Ddg(const Vector &d);
|
||||
HypreParMatrix* lDddg(const Vector &d, const Vector &l);
|
||||
|
||||
~ParContactProblem()
|
||||
{
|
||||
delete B;
|
||||
delete K;
|
||||
delete M;
|
||||
for (int i = 0; i<dM.Size(); i++)
|
||||
{
|
||||
delete dM[i];
|
||||
}
|
||||
delete vfes1;
|
||||
delete vfes2;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class QPOptParContactProblem
|
||||
{
|
||||
private:
|
||||
ParContactProblem * problem = nullptr;
|
||||
int dimU, dimM, dimC;
|
||||
// Array<int> block_offsets;
|
||||
Vector ml;
|
||||
HypreParMatrix * NegId = nullptr;
|
||||
public:
|
||||
QPOptParContactProblem(ParContactProblem * problem_);
|
||||
int GetDimU();
|
||||
int GetDimM();
|
||||
int GetDimC();
|
||||
Vector & Getml();
|
||||
MPI_Comm GetComm() {return problem->GetComm();}
|
||||
int * GetConstraintsStarts() {return problem->GetConstraintsStarts().GetData();}
|
||||
int GetGlobalNumConstraints() {return problem->GetGlobalNumConstraints();}
|
||||
|
||||
ParElasticityProblem * GetElasticityProblem1() {return problem->GetElasticityProblem1();}
|
||||
ParElasticityProblem * GetElasticityProblem2() {return problem->GetElasticityProblem2();}
|
||||
|
||||
HypreParMatrix * Duuf(const BlockVector &);
|
||||
HypreParMatrix * Dumf(const BlockVector &);
|
||||
HypreParMatrix * Dmuf(const BlockVector &);
|
||||
HypreParMatrix * Dmmf(const BlockVector &);
|
||||
HypreParMatrix * Duc(const BlockVector &);
|
||||
HypreParMatrix * Dmc(const BlockVector &);
|
||||
HypreParMatrix * lDuuc(const BlockVector &, const Vector &);
|
||||
void c(const BlockVector &, Vector &);
|
||||
double CalcObjective(const BlockVector &);
|
||||
void CalcObjectiveGrad(const BlockVector &, BlockVector &);
|
||||
~QPOptParContactProblem();
|
||||
};
|
||||
@@ -1,554 +0,0 @@
|
||||
#include "parproblems_util.hpp"
|
||||
|
||||
void FindPointsInMesh(Mesh & mesh, const Array<int> & gvert, const Vector & xyz, const Array<int> & s_conn, Array<int>& conn,
|
||||
Vector & xyz2, Array<int> & s_conn2, Vector& xi, DenseMatrix & coords)
|
||||
{
|
||||
const int dim = mesh.Dimension();
|
||||
const int np = xyz.Size() / dim;
|
||||
|
||||
MFEM_VERIFY(np * dim == xyz.Size(), "");
|
||||
|
||||
mesh.EnsureNodes();
|
||||
|
||||
FindPointsGSLIB finder(MPI_COMM_WORLD);
|
||||
|
||||
finder.SetDistanceToleranceForPointsFoundOnBoundary(0.5);
|
||||
|
||||
const double bb_t = 0.5;
|
||||
finder.Setup(mesh, bb_t);
|
||||
|
||||
finder.FindPoints(xyz,mfem::Ordering::byVDIM);
|
||||
|
||||
Array<unsigned int> procs = finder.GetProc();
|
||||
|
||||
/// Return code for each point searched by FindPoints: inside element (0), on
|
||||
/// element boundary (1), or not found (2).
|
||||
Array<unsigned int> codes = finder.GetCode();
|
||||
|
||||
/// Return element number for each point found by FindPoints.
|
||||
Array<unsigned int> elems = finder.GetElem();
|
||||
|
||||
/// Return reference coordinates for each point found by FindPoints.
|
||||
Vector refcrd = finder.GetReferencePosition();
|
||||
|
||||
/// Return distance between the sought and the found point in physical space,
|
||||
/// for each point found by FindPoints.
|
||||
Vector dist = finder.GetDist();
|
||||
|
||||
finder.FreeData();
|
||||
|
||||
MFEM_VERIFY(dist.Size() == np, "");
|
||||
MFEM_VERIFY(refcrd.Size() == np * dim, "");
|
||||
MFEM_VERIFY(elems.Size() == np, "");
|
||||
MFEM_VERIFY(codes.Size() == np, "");
|
||||
|
||||
bool allfound = true;
|
||||
for (auto code : codes)
|
||||
if (code == 2) { allfound = false; }
|
||||
|
||||
MFEM_VERIFY(allfound, "A point was not found");
|
||||
|
||||
// cout << "Maximum distance of projected points: " << dist.Max() << endl;
|
||||
|
||||
|
||||
Array<unsigned int> elems_recv, proc_recv;
|
||||
Vector ref_recv;
|
||||
Vector xyz_recv;
|
||||
Array<int> s_conn_recv;
|
||||
|
||||
MPICommunicator mycomm(MPI_COMM_WORLD, procs);
|
||||
mycomm.Communicate(xyz,xyz_recv,3,mfem::Ordering::byNODES);
|
||||
mycomm.Communicate(elems,elems_recv,1,mfem::Ordering::byVDIM);
|
||||
mycomm.Communicate(refcrd,ref_recv,3,mfem::Ordering::byVDIM);
|
||||
mycomm.Communicate(s_conn,s_conn_recv,1,mfem::Ordering::byVDIM);
|
||||
|
||||
proc_recv = mycomm.GetOriginProcs();
|
||||
|
||||
int np_loc = elems_recv.Size();
|
||||
Array<int> conn_loc(np_loc*4);
|
||||
Vector xi_send(np_loc*(dim-1));
|
||||
for (int i=0; i<np_loc; ++i)
|
||||
{
|
||||
int refFace, refNormal;
|
||||
// int refNormalSide;
|
||||
bool is_interior = -1;
|
||||
|
||||
Vector normal = GetNormalVector(mesh, elems_recv[i],
|
||||
ref_recv.GetData() + (i*dim),
|
||||
refFace, refNormal, is_interior);
|
||||
|
||||
// continue;
|
||||
int phyFace;
|
||||
if (is_interior)
|
||||
{
|
||||
phyFace = -1; // the id of the face that has the closest point
|
||||
FindSurfaceToProject(mesh, elems_recv[i], phyFace); // seems that this works
|
||||
|
||||
Array<int> cbdrVert;
|
||||
mesh.GetFaceVertices(phyFace, cbdrVert);
|
||||
Vector xs(dim);
|
||||
xs[0] = xyz_recv[i + 0*np_loc];
|
||||
xs[1] = xyz_recv[i + 1*np_loc];
|
||||
xs[2] = xyz_recv[i + 2*np_loc];
|
||||
|
||||
Vector xi_tmp(dim-1);
|
||||
// get nodes!
|
||||
|
||||
GridFunction *nodes = mesh.GetNodes();
|
||||
DenseMatrix coord(4,3);
|
||||
for (int j=0; j<4; j++)
|
||||
{
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
coord(j,k) = (*nodes)[cbdrVert[j]*3+k];
|
||||
}
|
||||
}
|
||||
SlaveToMaster(coord, xs, xi_tmp);
|
||||
|
||||
for (int j=0; j<dim-1; ++j)
|
||||
{
|
||||
xi_send[i*(dim-1)+j] = xi_tmp[j];
|
||||
}
|
||||
// now get get the projection to the surface
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector faceRefCrd(dim-1);
|
||||
{
|
||||
int fd = 0;
|
||||
for (int j=0; j<dim; ++j)
|
||||
{
|
||||
if (j == refNormal)
|
||||
{
|
||||
// refNormalSide = (ref_recv[(i*dim) + j] > 0.5); // not used
|
||||
}
|
||||
else
|
||||
{
|
||||
faceRefCrd[fd] = ref_recv[(i*dim) + j];
|
||||
fd++;
|
||||
}
|
||||
}
|
||||
MFEM_VERIFY(fd == dim-1, "");
|
||||
}
|
||||
|
||||
for (int j=0; j<dim-1; ++j)
|
||||
{
|
||||
xi_send[i*(dim-1)+j] = faceRefCrd[j]*2.0 - 1.0;
|
||||
}
|
||||
}
|
||||
// Get the element face
|
||||
Array<int> faces;
|
||||
Array<int> ori;
|
||||
int face;
|
||||
|
||||
if (is_interior)
|
||||
{
|
||||
face = phyFace;
|
||||
}
|
||||
else
|
||||
{
|
||||
mesh.GetElementFaces(elems_recv[i], faces, ori);
|
||||
face = faces[refFace];
|
||||
}
|
||||
|
||||
Array<int> faceVert;
|
||||
mesh.GetFaceVertices(face, faceVert);
|
||||
|
||||
for (int p=0; p<4; p++)
|
||||
{
|
||||
conn_loc[4*i+p] = faceVert[p];
|
||||
}
|
||||
}
|
||||
|
||||
if (0) // for debugging
|
||||
{
|
||||
int sz = xi_send.Size()/2;
|
||||
|
||||
for (int i = 0; i<sz; i++)
|
||||
{
|
||||
mfem::out << "("<<xi_send[i*(dim-1)]<<","<<xi_send[i*(dim-1)+1]<<"): -> ";
|
||||
for (int j = 0; j<4; j++)
|
||||
{
|
||||
double * vc = mesh.GetVertex(conn_loc[4*i+j]);
|
||||
if (j<3)
|
||||
{
|
||||
mfem::out << "("<<vc[0]<<","<<vc[1]<<","<<vc[2]<<"), ";
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem::out << "("<<vc[0]<<","<<vc[1]<<","<<vc[2]<<") \n " << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sz = xi_send.Size()/2;
|
||||
DenseMatrix coordsm(sz*4, dim);
|
||||
for (int i = 0; i<sz; i++)
|
||||
{
|
||||
for (int j = 0; j<4; j++)
|
||||
{
|
||||
for (int k=0; k<dim; k++)
|
||||
{
|
||||
coordsm(i*4+j,k) = mesh.GetVertex(conn_loc[i*4+j])[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pass global indices for conn_loc
|
||||
for (int i = 0; i<conn_loc.Size(); i++)
|
||||
{
|
||||
conn_loc[i] = gvert[conn_loc[i]];
|
||||
}
|
||||
|
||||
mycomm.UpdateDestinationProcs();
|
||||
mycomm.Communicate(xyz_recv,xyz2,3,mfem::Ordering::byNODES);
|
||||
mycomm.Communicate(xi_send,xi,2,mfem::Ordering::byVDIM);
|
||||
mycomm.Communicate(s_conn_recv,s_conn2,1,mfem::Ordering::byVDIM);
|
||||
mycomm.Communicate(conn_loc,conn,4,mfem::Ordering::byVDIM);
|
||||
mycomm.Communicate(coordsm,coords,4,mfem::Ordering::byVDIM);
|
||||
}
|
||||
|
||||
|
||||
void FindPointsInMesh(Mesh & mesh, const Array<int> & gvert, Array<int> & s_conn, const Vector &x1, Vector & xyz, Array<int>& conn,
|
||||
Vector& xi, DenseMatrix & coords)
|
||||
{
|
||||
const int dim = mesh.Dimension();
|
||||
const int np = xyz.Size() / dim;
|
||||
MFEM_VERIFY(np * dim == xyz.Size(), "");
|
||||
|
||||
mesh.EnsureNodes();
|
||||
|
||||
FindPointsGSLIB finder(MPI_COMM_WORLD);
|
||||
|
||||
finder.SetDistanceToleranceForPointsFoundOnBoundary(0.5);
|
||||
|
||||
const double bb_t = 0.5;
|
||||
finder.Setup(mesh, bb_t);
|
||||
|
||||
finder.FindPoints(xyz,mfem::Ordering::byVDIM);
|
||||
|
||||
Array<unsigned int> procs = finder.GetProc();
|
||||
|
||||
/// Return code for each point searched by FindPoints: inside element (0), on
|
||||
/// element boundary (1), or not found (2).
|
||||
Array<unsigned int> codes = finder.GetCode();
|
||||
|
||||
/// Return element number for each point found by FindPoints.
|
||||
Array<unsigned int> elems = finder.GetElem();
|
||||
|
||||
/// Return reference coordinates for each point found by FindPoints.
|
||||
Vector refcrd = finder.GetReferencePosition();
|
||||
|
||||
/// Return distance between the sought and the found point in physical space,
|
||||
/// for each point found by FindPoints.
|
||||
Vector dist = finder.GetDist();
|
||||
|
||||
finder.FreeData();
|
||||
|
||||
MFEM_VERIFY(dist.Size() == np, "");
|
||||
MFEM_VERIFY(refcrd.Size() == np * dim, "");
|
||||
MFEM_VERIFY(elems.Size() == np, "");
|
||||
MFEM_VERIFY(codes.Size() == np, "");
|
||||
|
||||
bool allfound = true;
|
||||
for (auto code : codes)
|
||||
if (code == 2) { allfound = false; }
|
||||
|
||||
MFEM_VERIFY(allfound, "A point was not found");
|
||||
|
||||
// reorder data so that the procs are in ascending order
|
||||
// sort procs and save the permutation
|
||||
std::vector<unsigned int> procs_index(np);
|
||||
std::iota(procs_index.begin(),procs_index.end(),0); //Initializing
|
||||
sort( procs_index.begin(),procs_index.end(), [&](int i,int j){return procs[i]<procs[j];} );
|
||||
|
||||
// map to sorted
|
||||
Array<unsigned int> procs_sorted(np);
|
||||
Array<unsigned int> elems_sorted(np);
|
||||
Vector xyz_sorted(np*dim);
|
||||
Vector refcrd_sorted(np*dim);
|
||||
Array<int> s_conn_sorted(np);
|
||||
for (int i = 0; i<np; i++)
|
||||
{
|
||||
int j = procs_index[i];
|
||||
procs_sorted[i] = procs[j];
|
||||
elems_sorted[i] = elems[j];
|
||||
s_conn_sorted[i] = s_conn[j];
|
||||
for (int d = 0; d<dim; d++)
|
||||
{
|
||||
xyz_sorted(i*dim+d) = xyz(j*dim+d);
|
||||
refcrd_sorted(i*dim+d) = refcrd(j*dim+d);
|
||||
}
|
||||
}
|
||||
|
||||
Array<unsigned int> elems_recv, proc_recv;
|
||||
xyz = xyz_sorted;
|
||||
s_conn = s_conn_sorted;
|
||||
Vector ref_recv;
|
||||
Vector xyz_recv;
|
||||
|
||||
MPICommunicator mycomm(MPI_COMM_WORLD, procs_sorted);
|
||||
mycomm.Communicate(xyz_sorted,xyz_recv,3,mfem::Ordering::byVDIM);
|
||||
mycomm.Communicate(elems_sorted,elems_recv,1,mfem::Ordering::byVDIM);
|
||||
mycomm.Communicate(refcrd_sorted,ref_recv,3,mfem::Ordering::byVDIM);
|
||||
|
||||
|
||||
proc_recv = mycomm.GetOriginProcs();
|
||||
|
||||
int np_loc = elems_recv.Size();
|
||||
Array<int> conn_loc(np_loc*4);
|
||||
Vector xi_send(np_loc*(dim-1));
|
||||
for (int i=0; i<np_loc; ++i)
|
||||
{
|
||||
int refFace, refNormal;
|
||||
// int refNormalSide;
|
||||
bool is_interior = -1;
|
||||
|
||||
Vector normal = GetNormalVector(mesh, elems_recv[i],
|
||||
ref_recv.GetData() + (i*dim),
|
||||
refFace, refNormal, is_interior);
|
||||
|
||||
// continue;
|
||||
int phyFace;
|
||||
if (is_interior)
|
||||
{
|
||||
phyFace = -1; // the id of the face that has the closest point
|
||||
FindSurfaceToProject(mesh, elems_recv[i], phyFace); // seems that this works
|
||||
|
||||
Array<int> cbdrVert;
|
||||
mesh.GetFaceVertices(phyFace, cbdrVert);
|
||||
Vector xs(dim);
|
||||
xs[0] = xyz_recv[i*dim + 0];
|
||||
xs[1] = xyz_recv[i*dim + 1];
|
||||
xs[2] = xyz_recv[i*dim + 2];
|
||||
|
||||
Vector xi_tmp(dim-1);
|
||||
// get nodes!
|
||||
|
||||
GridFunction *nodes = mesh.GetNodes();
|
||||
DenseMatrix coord(4,3);
|
||||
for (int j=0; j<4; j++)
|
||||
{
|
||||
for (int k=0; k<3; k++)
|
||||
{
|
||||
coord(j,k) = (*nodes)[cbdrVert[j]*3+k];
|
||||
}
|
||||
}
|
||||
SlaveToMaster(coord, xs, xi_tmp);
|
||||
|
||||
for (int j=0; j<dim-1; ++j)
|
||||
{
|
||||
xi_send[i*(dim-1)+j] = xi_tmp[j];
|
||||
}
|
||||
// now get the projection to the surface
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector faceRefCrd(dim-1);
|
||||
{
|
||||
int fd = 0;
|
||||
for (int j=0; j<dim; ++j)
|
||||
{
|
||||
if (j == refNormal)
|
||||
{
|
||||
// refNormalSide = (ref_recv[(i*dim) + j] > 0.5); // not used
|
||||
}
|
||||
else
|
||||
{
|
||||
faceRefCrd[fd] = ref_recv[(i*dim) + j];
|
||||
fd++;
|
||||
}
|
||||
}
|
||||
MFEM_VERIFY(fd == dim-1, "");
|
||||
}
|
||||
|
||||
for (int j=0; j<dim-1; ++j)
|
||||
{
|
||||
xi_send[i*(dim-1)+j] = faceRefCrd[j]*2.0 - 1.0;
|
||||
}
|
||||
}
|
||||
// Get the element face
|
||||
Array<int> faces;
|
||||
Array<int> ori;
|
||||
int face;
|
||||
|
||||
if (is_interior)
|
||||
{
|
||||
face = phyFace;
|
||||
}
|
||||
else
|
||||
{
|
||||
mesh.GetElementFaces(elems_recv[i], faces, ori);
|
||||
face = faces[refFace];
|
||||
}
|
||||
|
||||
Array<int> faceVert;
|
||||
mesh.GetFaceVertices(face, faceVert);
|
||||
|
||||
for (int p=0; p<4; p++)
|
||||
{
|
||||
conn_loc[4*i+p] = faceVert[p];
|
||||
}
|
||||
}
|
||||
|
||||
if (0) // for debugging
|
||||
{
|
||||
int sz = xi_send.Size()/2;
|
||||
|
||||
for (int i = 0; i<sz; i++)
|
||||
{
|
||||
mfem::out << "("<<xi_send[i*(dim-1)]<<","<<xi_send[i*(dim-1)+1]<<"): -> ";
|
||||
for (int j = 0; j<4; j++)
|
||||
{
|
||||
double * vc = mesh.GetVertex(conn_loc[4*i+j]);
|
||||
if (j<3)
|
||||
{
|
||||
mfem::out << "("<<vc[0]<<","<<vc[1]<<","<<vc[2]<<"), ";
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem::out << "("<<vc[0]<<","<<vc[1]<<","<<vc[2]<<") \n " << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sz = xi_send.Size()/2;
|
||||
DenseMatrix coordsm(sz*4, dim);
|
||||
for (int i = 0; i<sz; i++)
|
||||
{
|
||||
for (int j = 0; j<4; j++)
|
||||
{
|
||||
for (int k=0; k<dim; k++)
|
||||
{
|
||||
coordsm(i*4+j,k) = mesh.GetVertex(conn_loc[i*4+j])[k]+x1[dim*conn_loc[i*4+j]+k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pass global indices for conn_loc
|
||||
for (int i = 0; i<conn_loc.Size(); i++)
|
||||
{
|
||||
conn_loc[i] = gvert[conn_loc[i]];
|
||||
}
|
||||
|
||||
mycomm.UpdateDestinationProcs();
|
||||
mycomm.Communicate(xi_send,xi,2,mfem::Ordering::byVDIM);
|
||||
mycomm.Communicate(conn_loc,conn,4,mfem::Ordering::byVDIM);
|
||||
mycomm.Communicate(coordsm,coords,4,mfem::Ordering::byVDIM);
|
||||
}
|
||||
|
||||
int get_rank(int tdof, std::vector<int> & tdof_offsets)
|
||||
{
|
||||
int size = tdof_offsets.size();
|
||||
if (size == 1) { return 0; }
|
||||
std::vector<int>::iterator up;
|
||||
up=std::upper_bound(tdof_offsets.begin(), tdof_offsets.end(),tdof); //
|
||||
return std::distance(tdof_offsets.begin(),up)-1;
|
||||
}
|
||||
|
||||
void ComputeTdofOffsets(const ParFiniteElementSpace * pfes,
|
||||
std::vector<int> & tdof_offsets)
|
||||
{
|
||||
MPI_Comm comm = pfes->GetComm();
|
||||
int num_procs;
|
||||
MPI_Comm_size(comm, &num_procs);
|
||||
tdof_offsets.resize(num_procs);
|
||||
int mytoffset = pfes->GetMyTDofOffset();
|
||||
MPI_Allgather(&mytoffset,1,MPI_INT,&tdof_offsets[0],1,MPI_INT,comm);
|
||||
}
|
||||
|
||||
void ComputeTdofOffsets(MPI_Comm comm, int mytoffset, std::vector<int> & tdof_offsets)
|
||||
{
|
||||
int num_procs;
|
||||
MPI_Comm_size(comm,&num_procs);
|
||||
tdof_offsets.resize(num_procs);
|
||||
MPI_Allgather(&mytoffset,1,MPI_INT,&tdof_offsets[0],1,MPI_INT,comm);
|
||||
}
|
||||
|
||||
void ComputeTdofs(MPI_Comm comm, int mytoffs, std::vector<int> & tdofs)
|
||||
{
|
||||
int num_procs;
|
||||
MPI_Comm_size(comm,&num_procs);
|
||||
tdofs.resize(num_procs);
|
||||
MPI_Allgather(&mytoffs,1,MPI_INT,&tdofs,1,MPI_INT,comm);
|
||||
}
|
||||
|
||||
|
||||
// Performs Pᵀ * A * P for BlockOperator P (with blocks as HypreParMatrices)
|
||||
// and A a HypreParMatrix, i.e., this handles the special case
|
||||
// where P = [P₁ P₂ ⋅⋅⋅ Pₙ]
|
||||
// C = Pᵀ * A * P
|
||||
void RAP(const HypreParMatrix & A, const BlockOperator & P,
|
||||
BlockOperator & C)
|
||||
{
|
||||
int nblocks = P.NumColBlocks();
|
||||
|
||||
const HypreParMatrix * Pi = nullptr;
|
||||
const HypreParMatrix * Pj = nullptr;
|
||||
HypreParMatrix * PitAPj = nullptr;
|
||||
|
||||
for (int i = 0; i< nblocks; i++)
|
||||
{
|
||||
if (P.IsZeroBlock(0,i)) continue;
|
||||
Pi = dynamic_cast<const HypreParMatrix*>(&P.GetBlock(0,i));
|
||||
for (int j = 0; j<nblocks; j++)
|
||||
{
|
||||
if (P.IsZeroBlock(0,j)) continue;
|
||||
Pj = dynamic_cast<const HypreParMatrix*>(&P.GetBlock(0,j));
|
||||
if (i == j)
|
||||
{
|
||||
PitAPj = RAP(&A, Pj);
|
||||
}
|
||||
else
|
||||
{
|
||||
PitAPj = RAP(Pi, &A, Pj);
|
||||
}
|
||||
C.SetBlock(i,j,PitAPj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParAdd(const BlockOperator & A, const BlockOperator & B, BlockOperator & C)
|
||||
{
|
||||
int n = A.NumRowBlocks();
|
||||
int m = A.NumColBlocks();
|
||||
MFEM_VERIFY(B.NumRowBlocks() == n, "Inconsistent number of row blocks");
|
||||
MFEM_VERIFY(B.NumColBlocks() == m, "Inconsistent number of column blocks");
|
||||
|
||||
const HypreParMatrix * a;
|
||||
const HypreParMatrix * b;
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
for (int j = 0; j<m; j++)
|
||||
{
|
||||
a = nullptr;
|
||||
b = nullptr;
|
||||
if (!A.IsZeroBlock(i,j))
|
||||
{
|
||||
a = dynamic_cast<const HypreParMatrix*>(&A.GetBlock(i,j));
|
||||
}
|
||||
if (!B.IsZeroBlock(i,j))
|
||||
{
|
||||
b = dynamic_cast<const HypreParMatrix*>(&B.GetBlock(i,j));
|
||||
}
|
||||
if (a && b)
|
||||
{
|
||||
C.SetBlock(i,j,ParAdd(a,b));
|
||||
}
|
||||
else if (a)
|
||||
{
|
||||
C.SetBlock(i,j,new HypreParMatrix(*a));
|
||||
}
|
||||
else if (b)
|
||||
{
|
||||
C.SetBlock(i,j,new HypreParMatrix(*b));
|
||||
}
|
||||
else
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "problems_util.hpp"
|
||||
#include "../util/mpicomm.hpp"
|
||||
|
||||
// Coordinates in xyz are assumed to be ordered as [X, Y, Z]
|
||||
// where X is the list of x-coordinates for all points and so on.
|
||||
// conn: connectivity of the target surface elements
|
||||
// xi: surface reference cooridnates for the cloest point, involves a linear transformation from [0,1] to [-1,1]
|
||||
void FindPointsInMesh(Mesh & mesh, const Array<int> & gvert, const Vector & xyz, const Array<int> & s_conn, Array<int>& conn,
|
||||
Vector & xyz2, Array<int> & s_conn2, Vector& xi, DenseMatrix & coords);
|
||||
|
||||
// somewhat simplified version of the above
|
||||
void FindPointsInMesh(Mesh & mesh, const Array<int> & gvert, Array<int> & s_conn, const Vector &x1, Vector & xyz, Array<int>& conn,
|
||||
Vector& xi, DenseMatrix & coords);
|
||||
|
||||
int get_rank(int tdof, std::vector<int> & tdof_offsets);
|
||||
void ComputeTdofOffsets(const ParFiniteElementSpace * pfes,
|
||||
std::vector<int> & tdof_offsets);
|
||||
void ComputeTdofOffsets(MPI_Comm comm, int mytoffset, std::vector<int> & tdof_offsets);
|
||||
void ComputeTdofs(MPI_Comm comm, int mytoffs, std::vector<int> & tdofs);
|
||||
|
||||
|
||||
// Performs Pᵀ * A * P for BlockOperator P (with blocks as HypreParMatrices)
|
||||
// and A a HypreParMatrix, i.e., this handles the special case
|
||||
// where P = [P₁ P₂ ⋅⋅⋅ Pₙ]
|
||||
void RAP(const HypreParMatrix & A, const BlockOperator & P, BlockOperator & C);
|
||||
void ParAdd(const BlockOperator & A, const BlockOperator & B, BlockOperator & C);
|
||||
@@ -1,367 +0,0 @@
|
||||
#include "problems.hpp"
|
||||
|
||||
|
||||
void ElasticityProblem::Init()
|
||||
{
|
||||
int dim = mesh->Dimension();
|
||||
fec = new H1_FECollection(order,dim);
|
||||
fes = new FiniteElementSpace(mesh,fec,dim,Ordering::byVDIM);
|
||||
ndofs = fes->GetTrueVSize();
|
||||
mesh->SetNodalFESpace(fes);
|
||||
if (mesh->bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(mesh->bdr_attributes.Max());
|
||||
}
|
||||
ess_bdr = 0; ess_bdr[1] = 1;
|
||||
fes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list);
|
||||
// Solution GridFunction
|
||||
x.SetSpace(fes); x = 0.0;
|
||||
// RHS
|
||||
b.Update(fes);
|
||||
// Elasticity operator
|
||||
lambda.SetSize(mesh->attributes.Max()); lambda = 57.6923076923;
|
||||
mu.SetSize(mesh->attributes.Max()); mu = 38.4615384615;
|
||||
|
||||
lambda_cf.UpdateConstants(lambda);
|
||||
mu_cf.UpdateConstants(mu);
|
||||
a = new BilinearForm(fes);
|
||||
a->SetDiagonalPolicy(mfem::Operator::DIAG_ONE);
|
||||
a->AddDomainIntegrator(new ElasticityIntegrator(lambda_cf,mu_cf));
|
||||
}
|
||||
|
||||
void ElasticityProblem::FormLinearSystem()
|
||||
{
|
||||
if (!formsystem)
|
||||
{
|
||||
formsystem = true;
|
||||
b.Assemble();
|
||||
a->Assemble();
|
||||
a->FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
}
|
||||
}
|
||||
void ElasticityProblem::UpdateLinearSystem()
|
||||
{
|
||||
if (formsystem)
|
||||
{
|
||||
b.Update();
|
||||
a->Update();
|
||||
formsystem = false;
|
||||
}
|
||||
FormLinearSystem();
|
||||
}
|
||||
|
||||
ContactProblem::ContactProblem(ElasticityProblem * prob1_, ElasticityProblem * prob2_)
|
||||
: prob1(prob1_), prob2(prob2_)
|
||||
{
|
||||
// 1. Set up block system
|
||||
Mesh* mesh1 = prob1->GetMesh();
|
||||
int dim = mesh1->Dimension();
|
||||
|
||||
nodes0.SetSpace(mesh1->GetNodes()->FESpace());
|
||||
nodes0 = *mesh1->GetNodes();
|
||||
nodes1 = mesh1->GetNodes();
|
||||
|
||||
Vector delta1(dim);
|
||||
delta1 = 0.0; delta1[0] = 0.1;
|
||||
prob1->SetDisplacementDirichletData(delta1);
|
||||
prob1->FormLinearSystem();
|
||||
|
||||
Vector delta2(dim);
|
||||
delta2 = 0.0;
|
||||
prob2->SetDisplacementDirichletData(delta2);
|
||||
prob2->FormLinearSystem();
|
||||
|
||||
int ndof1 = prob1->GetNumDofs();
|
||||
int ndof2 = prob2->GetNumDofs();
|
||||
|
||||
Array<int> offsets(3);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = ndof1;
|
||||
offsets[2] = ndof2;
|
||||
offsets.PartialSum();
|
||||
|
||||
BlockMatrix Kb(offsets);
|
||||
SparseMatrix A1 = prob1->GetOperator();
|
||||
SparseMatrix A2 = prob2->GetOperator();
|
||||
|
||||
Kb.SetBlock(0,0,&A1);
|
||||
Kb.SetBlock(1,1,&A2);
|
||||
|
||||
K = Kb.CreateMonolithic();
|
||||
K->Threshold(0.0);
|
||||
K->SortColumnIndices();
|
||||
|
||||
B = new BlockVector(offsets);
|
||||
B->GetBlock(0).Set(1.0, prob1->GetRHS());
|
||||
B->GetBlock(1).Set(1.0, prob2->GetRHS());
|
||||
|
||||
ComputeContactVertrices();
|
||||
}
|
||||
|
||||
void ContactProblem::ComputeContactVertrices()
|
||||
{
|
||||
if (npoints>0) return;
|
||||
Mesh * mesh2 = prob2->GetMesh();
|
||||
Array<int> vert;
|
||||
for (int b=0; b<mesh2->GetNBE(); b++)
|
||||
{
|
||||
if (mesh2->GetBdrAttribute(b) == 3)
|
||||
{
|
||||
mesh2->GetBdrElementVertices(b, vert);
|
||||
for (auto v : vert)
|
||||
{
|
||||
contact_vertices.insert(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
npoints = contact_vertices.size();
|
||||
}
|
||||
|
||||
void ContactProblem::ComputeGapFunctionAndDerivatives(const Vector &displ1,
|
||||
const Vector & displ2)
|
||||
{
|
||||
ComputeContactVertrices();
|
||||
|
||||
Mesh * mesh1 = prob1->GetMesh();
|
||||
int dim = mesh1->Dimension();
|
||||
Mesh * mesh2 = prob2->GetMesh();
|
||||
|
||||
int ndof1 = prob1->GetNumDofs();
|
||||
int ndof2 = prob2->GetNumDofs();
|
||||
int ndofs = ndof1 + ndof2;
|
||||
|
||||
int nv1 = mesh1->GetNV();
|
||||
// connectivity of the second mesh
|
||||
|
||||
Array<int> conn2(npoints);
|
||||
// mesh2->MoveNodes(displ2);
|
||||
Vector xyz(dim * npoints);
|
||||
|
||||
int cnt = 0;
|
||||
for (auto v : contact_vertices)
|
||||
{
|
||||
for (int d = 0; d<dim; d++)
|
||||
{
|
||||
xyz(cnt*dim + d) = mesh2->GetVertex(v)[d]+displ2[v*dim+d];
|
||||
}
|
||||
conn2[cnt] = v + nv1;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
MFEM_VERIFY(cnt == npoints, "");
|
||||
gapv.SetSize(npoints*dim);
|
||||
|
||||
// segment reference coordinates of the closest point
|
||||
Vector xi1(npoints*(dim-1));
|
||||
Array<int> conn1(npoints*4);
|
||||
|
||||
// add(nodes0, displ1, *nodes1);
|
||||
FindPointsInMesh(*mesh1, xyz, conn1, xi1);
|
||||
|
||||
DenseMatrix coordsm(npoints*4, dim);
|
||||
for (int i=0; i<npoints; i++)
|
||||
{
|
||||
for (int j=0; j<4; j++)
|
||||
{
|
||||
for (int k=0; k<dim; k++)
|
||||
{
|
||||
coordsm(i*4+j,k) = mesh1->GetVertex(conn1[i*4+j])[k]+displ1[dim*conn1[i*4+j]+k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (M)
|
||||
{
|
||||
delete M;
|
||||
for (int i = 0; i<dM.Size(); i++)
|
||||
{
|
||||
delete dM[i];
|
||||
}
|
||||
dM.SetSize(0);
|
||||
}
|
||||
|
||||
int h = npoints;
|
||||
M = new SparseMatrix(h,ndofs);
|
||||
dM.SetSize(npoints);
|
||||
for (int i = 0; i<npoints; i++)
|
||||
{
|
||||
dM[i] = new SparseMatrix(ndofs,ndofs);
|
||||
}
|
||||
Assemble_Contact(xyz, xi1, coordsm, conn2, conn1, gapv, *M, dM);
|
||||
}
|
||||
|
||||
|
||||
double ContactProblem::E(const Vector & d)
|
||||
{
|
||||
return 0.5 * K->InnerProduct(d, d) - InnerProduct(d, *B);
|
||||
}
|
||||
|
||||
void ContactProblem::DdE(const Vector &d, Vector &gradE)
|
||||
{
|
||||
gradE.SetSize(K->Height());
|
||||
K->Mult(d, gradE);
|
||||
gradE.Add(-1.0, *B);
|
||||
}
|
||||
|
||||
SparseMatrix* ContactProblem::DddE(const Vector &d)
|
||||
{
|
||||
return K;
|
||||
}
|
||||
|
||||
void ContactProblem::g(const Vector &d, Vector &gd)
|
||||
{
|
||||
int ndof1 = prob1->GetNumDofs();
|
||||
int ndof2 = prob2->GetNumDofs();
|
||||
double * data = d.GetData();
|
||||
Vector displ1(data,ndof1);
|
||||
Vector displ2(&data[ndof1],ndof2);
|
||||
if (recompute)
|
||||
{
|
||||
ComputeGapFunctionAndDerivatives(displ1, displ2);
|
||||
recompute = false;
|
||||
}
|
||||
|
||||
gd = GetGapFunction();
|
||||
}
|
||||
|
||||
SparseMatrix* ContactProblem::Ddg(const Vector &d)
|
||||
{
|
||||
return GetJacobian();
|
||||
}
|
||||
|
||||
SparseMatrix* ContactProblem::lDddg(const Vector &d, const Vector &l)
|
||||
{
|
||||
return nullptr; // for now
|
||||
}
|
||||
|
||||
QPContactProblem::QPContactProblem(ElasticityProblem * prob1_, ElasticityProblem * prob2_)
|
||||
: ContactProblem(prob1_,prob2_)
|
||||
{
|
||||
ContactProblem::ComputeContactVertrices();
|
||||
dimS = npoints;
|
||||
dimD = K->Height();
|
||||
}
|
||||
|
||||
// E(d) = 1 / 2 d^T K d + f^T d
|
||||
double QPContactProblem::E(const Vector &d)
|
||||
{
|
||||
return ContactProblem::E(d);
|
||||
}
|
||||
|
||||
// gradient(E) = K d + f
|
||||
void QPContactProblem::DdE(const Vector &d, Vector &gradE)
|
||||
{
|
||||
ContactProblem::DdE(d,gradE);
|
||||
}
|
||||
|
||||
// Hessian(E) = K
|
||||
SparseMatrix* QPContactProblem::DddE(const Vector &d)
|
||||
{
|
||||
return ContactProblem::DddE(d);
|
||||
}
|
||||
|
||||
// g(d) = J * d + g0 >= 0
|
||||
void QPContactProblem::g(const Vector &d, Vector &gd)
|
||||
{
|
||||
Vector g0;
|
||||
ContactProblem::g(d,g0);
|
||||
M->Mult(d, gd);
|
||||
gd.Add(1.0, g0);
|
||||
}
|
||||
|
||||
// Jacobian(g) = J
|
||||
SparseMatrix* QPContactProblem::Ddg(const Vector &d)
|
||||
{
|
||||
return M;
|
||||
}
|
||||
|
||||
SparseMatrix* QPContactProblem::lDddg(const Vector &d, const Vector &l)
|
||||
{
|
||||
return ContactProblem::lDddg(d,l);
|
||||
}
|
||||
|
||||
|
||||
QPOptContactProblem::QPOptContactProblem(ContactProblem * problem_)
|
||||
: problem(problem_)
|
||||
{
|
||||
dimU = problem->GetNumDofs();
|
||||
dimM = problem->GetNumConstraints();
|
||||
dimC = problem->GetNumConstraints();
|
||||
block_offsets.SetSize(3);
|
||||
block_offsets[0] = 0;
|
||||
block_offsets[1] = dimU;
|
||||
block_offsets[2] = dimM;
|
||||
block_offsets.PartialSum();
|
||||
ml.SetSize(dimM); ml = 0.0;
|
||||
Vector negone(dimM); negone = -1.0;
|
||||
NegId = new SparseMatrix(negone);
|
||||
}
|
||||
|
||||
int QPOptContactProblem::GetDimU() { return dimU; }
|
||||
|
||||
int QPOptContactProblem::GetDimM() { return dimM; }
|
||||
|
||||
int QPOptContactProblem::GetDimC() { return dimC; }
|
||||
|
||||
Vector & QPOptContactProblem::Getml() { return ml; }
|
||||
|
||||
SparseMatrix * QPOptContactProblem::Duuf(const BlockVector & x)
|
||||
{
|
||||
return problem->DddE(x.GetBlock(0));
|
||||
}
|
||||
|
||||
SparseMatrix * QPOptContactProblem::Dumf(const BlockVector & x)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SparseMatrix * QPOptContactProblem::Dmuf(const BlockVector & x)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SparseMatrix * QPOptContactProblem::Dmmf(const BlockVector & x)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SparseMatrix * QPOptContactProblem::Duc(const BlockVector & x)
|
||||
{
|
||||
return problem->Ddg(x.GetBlock(0));
|
||||
}
|
||||
|
||||
SparseMatrix * QPOptContactProblem::Dmc(const BlockVector & x)
|
||||
{
|
||||
return NegId;
|
||||
}
|
||||
|
||||
SparseMatrix * QPOptContactProblem::lDuuc(const BlockVector & x, const Vector & l)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void QPOptContactProblem::c(const BlockVector &x, Vector & y)
|
||||
{
|
||||
Vector g0;
|
||||
problem->g(x.GetBlock(0),g0); // gap function
|
||||
g0.Add(-1.0, x.GetBlock(1));
|
||||
|
||||
problem->GetJacobian()->Mult(x.GetBlock(0),y);
|
||||
y.Add(1.0, g0);
|
||||
}
|
||||
|
||||
double QPOptContactProblem::CalcObjective(const BlockVector & x)
|
||||
{
|
||||
return problem->E(x.GetBlock(0));
|
||||
}
|
||||
|
||||
void QPOptContactProblem::CalcObjectiveGrad(const BlockVector & x, BlockVector & y)
|
||||
{
|
||||
problem->DdE(x.GetBlock(0), y.GetBlock(0));
|
||||
y.GetBlock(1) = 0.0;
|
||||
}
|
||||
|
||||
QPOptContactProblem::~QPOptContactProblem()
|
||||
{
|
||||
delete NegId;
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
#include "problems_util.hpp"
|
||||
|
||||
|
||||
class ElasticityProblem
|
||||
{
|
||||
private:
|
||||
bool formsystem = false;
|
||||
Mesh * mesh = nullptr;
|
||||
int order;
|
||||
int ndofs;
|
||||
FiniteElementCollection * fec = nullptr;
|
||||
FiniteElementSpace * fes = nullptr;
|
||||
Vector lambda, mu;
|
||||
PWConstCoefficient lambda_cf, mu_cf;
|
||||
Array<int> ess_bdr, ess_tdof_list;
|
||||
BilinearForm *a=nullptr;
|
||||
LinearForm b;
|
||||
GridFunction x;
|
||||
SparseMatrix A;
|
||||
Vector B,X;
|
||||
void Init();
|
||||
public:
|
||||
ElasticityProblem(const char *mesh_file , int ref, int order_ = 1) : order(order_)
|
||||
{
|
||||
mesh = new Mesh(mesh_file,1,1);
|
||||
for (int i = 0; i<ref; i++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
Init();
|
||||
}
|
||||
|
||||
Mesh * GetMesh() { return mesh; }
|
||||
FiniteElementSpace * GetFESpace() { return fes; }
|
||||
int GetNumDofs() { return ndofs; }
|
||||
SparseMatrix & GetOperator()
|
||||
{
|
||||
MFEM_VERIFY(formsystem, "System not formed yet. Call FormLinearSystem()");
|
||||
return A;
|
||||
}
|
||||
|
||||
Vector & GetRHS()
|
||||
{
|
||||
MFEM_VERIFY(formsystem, "System not formed yet. Call FormLinearSystem()");
|
||||
return B;
|
||||
}
|
||||
|
||||
void FormLinearSystem();
|
||||
void UpdateLinearSystem();
|
||||
|
||||
void SetDisplacementDirichletData(const Vector & delta)
|
||||
{
|
||||
VectorConstantCoefficient delta_cf(delta);
|
||||
x.ProjectBdrCoefficient(delta_cf,ess_bdr);
|
||||
};
|
||||
|
||||
void UpdateDisplacement(const Vector & x_)
|
||||
{
|
||||
// x = x_;
|
||||
// mesh->MoveVertices(x);
|
||||
// mesh->NodesUpdated();
|
||||
};
|
||||
|
||||
GridFunction & GetDisplacementGridFunction() {return x;};
|
||||
Array<int> & GetEssentialDofs() {return ess_tdof_list;};
|
||||
|
||||
~ElasticityProblem()
|
||||
{
|
||||
delete a;
|
||||
delete fes;
|
||||
delete fec;
|
||||
delete mesh;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ContactProblem
|
||||
{
|
||||
private:
|
||||
ElasticityProblem * prob1 = nullptr;
|
||||
ElasticityProblem * prob2 = nullptr;
|
||||
GridFunction nodes0;
|
||||
GridFunction *nodes1 = nullptr;
|
||||
std::set<int> contact_vertices;
|
||||
bool recompute = true;
|
||||
|
||||
protected:
|
||||
int npoints=0;
|
||||
SparseMatrix *K =nullptr;
|
||||
BlockVector *B = nullptr;
|
||||
Vector gapv;
|
||||
Array<SparseMatrix*> dM;
|
||||
SparseMatrix * M=nullptr;
|
||||
void ComputeContactVertrices();
|
||||
public:
|
||||
ContactProblem(ElasticityProblem * prob1_, ElasticityProblem * prob2_);
|
||||
|
||||
ElasticityProblem * GetElasticityProblem1() {return prob1;}
|
||||
ElasticityProblem * GetElasticityProblem2() {return prob2;}
|
||||
|
||||
int GetNumDofs() {return K->Height();}
|
||||
int GetNumConstraints() {return npoints;}
|
||||
Vector & GetGapFunction() {return gapv;}
|
||||
SparseMatrix * GetJacobian() {return M;}
|
||||
Array<SparseMatrix*> & GetHessian() {return dM;}
|
||||
void ComputeGapFunctionAndDerivatives(const Vector & displ1, const Vector &displ2);
|
||||
|
||||
virtual double E(const Vector & d);
|
||||
virtual void DdE(const Vector &d, Vector &gradE);
|
||||
virtual SparseMatrix* DddE(const Vector &d);
|
||||
void g(const Vector &d, Vector &gd);
|
||||
virtual SparseMatrix* Ddg(const Vector &d);
|
||||
virtual SparseMatrix* lDddg(const Vector &d, const Vector &l);
|
||||
|
||||
~ContactProblem()
|
||||
{
|
||||
delete B;
|
||||
delete K;
|
||||
delete M;
|
||||
for (int i = 0; i<dM.Size(); i++)
|
||||
{
|
||||
delete dM[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class QPContactProblem : public ContactProblem
|
||||
{
|
||||
private:
|
||||
int dimD, dimS;
|
||||
public:
|
||||
QPContactProblem(ElasticityProblem * prob1_, ElasticityProblem * prob2_);
|
||||
|
||||
double E(const Vector & d);
|
||||
void DdE(const Vector &d, Vector &gradE);
|
||||
SparseMatrix* DddE(const Vector &d);
|
||||
void g(const Vector &d, Vector &gd);
|
||||
SparseMatrix* Ddg(const Vector &d);
|
||||
SparseMatrix* lDddg(const Vector &d, const Vector &l);
|
||||
};
|
||||
|
||||
|
||||
class QPOptContactProblem
|
||||
{
|
||||
private:
|
||||
ContactProblem * problem = nullptr;
|
||||
int dimU, dimM, dimC;
|
||||
Array<int> block_offsets;
|
||||
Vector ml;
|
||||
SparseMatrix * NegId = nullptr;
|
||||
public:
|
||||
QPOptContactProblem(ContactProblem * problem_);
|
||||
int GetDimU();
|
||||
int GetDimM();
|
||||
int GetDimC();
|
||||
Vector & Getml();
|
||||
SparseMatrix * Duuf(const BlockVector &);
|
||||
SparseMatrix * Dumf(const BlockVector &);
|
||||
SparseMatrix * Dmuf(const BlockVector &);
|
||||
SparseMatrix * Dmmf(const BlockVector &);
|
||||
SparseMatrix * Duc(const BlockVector &);
|
||||
SparseMatrix * Dmc(const BlockVector &);
|
||||
SparseMatrix * lDuuc(const BlockVector &, const Vector &);
|
||||
void c(const BlockVector &, Vector &);
|
||||
double CalcObjective(const BlockVector &);
|
||||
void CalcObjectiveGrad(const BlockVector &, BlockVector &);
|
||||
~QPOptContactProblem();
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,61 +0,0 @@
|
||||
|
||||
#include "mfem.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
void BasisEval(const Vector xi, Vector &N, DenseMatrix &dNdxi); // dNdxi is 2*4
|
||||
void BasisEvalDerivs(const Vector xi, Vector& N, DenseMatrix& dNdxi,
|
||||
DenseMatrix& dN2dxi);
|
||||
// returns the vector and matrix form of the shape functions and its derivative
|
||||
void BasisVectorDerivs(const Vector xi, DenseMatrix& N, DenseMatrix& dNdxi,
|
||||
DenseMatrix& ddNdxi);
|
||||
void cross(const Vector a, const Vector b, Vector& c);
|
||||
// a outer b
|
||||
void outer(const Vector a, const Vector b, DenseMatrix& c);
|
||||
// dphidxi 2*4
|
||||
// coords 4*3
|
||||
void ComputeNormal(const DenseMatrix& dphidxi, const DenseMatrix& coords,
|
||||
Vector& normal, double& nnorm);
|
||||
void SlaveToMaster(const DenseMatrix& m_coords, const Vector& s_x, Vector& xi);
|
||||
|
||||
// m_coords is expected to be 4 * 3
|
||||
void ComputeGapJacobian(const Vector x_s, const Vector xi,
|
||||
const DenseMatrix m_coords,
|
||||
double& gap, Vector& normal, Vector& dgdxm, Vector& dgdxs);
|
||||
|
||||
void ComputeGapHessian(const Vector x_s, const Vector xi,
|
||||
const DenseMatrix m_coords,
|
||||
DenseMatrix& dg2dx);
|
||||
void NodeSegConPairs(const Vector x1, const Vector xi2,
|
||||
const DenseMatrix coords2,
|
||||
double& node_g, Vector& node_dg, DenseMatrix& node_dg2);
|
||||
// coordsm : (npoints*4, 3) use what class?
|
||||
// m_conn: (npoints*4)
|
||||
void Assemble_Contact(const Vector x_s,
|
||||
const Vector xi, const DenseMatrix coordsm, const Array<int> s_conn,
|
||||
const Array<int> m_conn, Vector& g, SparseMatrix& M,
|
||||
Array<SparseMatrix *> & dM);
|
||||
|
||||
void Assemble_Contact(const Vector x_s,
|
||||
const Vector xi, const DenseMatrix coordsm, const Array<int> s_conn,
|
||||
const Array<int> m_conn, Vector & g, SparseMatrix & M1, SparseMatrix & M2,
|
||||
Array<SparseMatrix *> & dM11,
|
||||
Array<SparseMatrix *> & dM12,
|
||||
Array<SparseMatrix *> & dM21,
|
||||
Array<SparseMatrix *> & dM22);
|
||||
void Assemble_Contact(const Vector x_s,
|
||||
const Vector xi, const DenseMatrix coordsm, const Array<int> s_conn,
|
||||
const Array<int> m_conn, Vector & g, SparseMatrix & M1, SparseMatrix & M2,const Array<int> & points_map);
|
||||
|
||||
void FindSurfaceToProject(Mesh& mesh, const int elem, int& cbdrface);
|
||||
|
||||
Vector GetNormalVector(Mesh & mesh, const int elem, const double *ref,
|
||||
int & refFace, int & refNormal, bool & interior);
|
||||
int GetHexVertex(int cdim, int c, int fa, int fb, Vector & refCrd);
|
||||
|
||||
// Coordinates in xyz are assumed to be ordered as [X, Y, Z]
|
||||
// where X is the list of x-coordinates for all points and so on.
|
||||
// conn: connectivity of the target surface elements
|
||||
// xi: surface reference cooridnates for the cloest point, involves a linear transformation from [0,1] to [-1,1]
|
||||
void FindPointsInMesh(Mesh & mesh, Vector const& xyz, Array<int>& conn, Vector& xi);
|
||||
@@ -1,530 +0,0 @@
|
||||
#include "mpicomm.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
|
||||
MPICommunicator::MPICommunicator(MPI_Comm comm_, int offset_, int gsize)
|
||||
: comm(comm_), offset(offset_)
|
||||
{
|
||||
MPI_Comm_size(comm,&num_procs);
|
||||
MPI_Comm_rank(comm,&myid);
|
||||
offsets.resize(num_procs);
|
||||
MPI_Allgather(&offset,1,MPI_INT,&offsets[0],1,MPI_INT,comm);
|
||||
lsize = (myid == num_procs-1) ? gsize - offsets[myid]
|
||||
: offsets[myid+1]-offsets[myid];
|
||||
|
||||
send_count.SetSize(num_procs); send_count = 0;
|
||||
send_displ.SetSize(num_procs); send_displ = 0;
|
||||
recv_count.SetSize(num_procs); recv_count = 0;
|
||||
recv_displ.SetSize(num_procs); recv_displ = 0;
|
||||
}
|
||||
|
||||
MPICommunicator::MPICommunicator(MPI_Comm comm_, Array<unsigned int> & destination_procs_)
|
||||
: comm(comm_), destination_procs(destination_procs_)
|
||||
{
|
||||
MPI_Comm_size(comm,&num_procs);
|
||||
MPI_Comm_rank(comm,&myid);
|
||||
send_count.SetSize(num_procs);
|
||||
send_displ.SetSize(num_procs);
|
||||
recv_count.SetSize(num_procs);
|
||||
recv_displ.SetSize(num_procs);
|
||||
resetcounts();
|
||||
}
|
||||
|
||||
|
||||
int MPICommunicator::get_rank(int dof)
|
||||
{
|
||||
if (num_procs == 1) { return 0; }
|
||||
std::vector<int>::iterator up;
|
||||
up=std::upper_bound(offsets.begin(), offsets.end(),dof);
|
||||
return std::distance(offsets.begin(),up)-1;
|
||||
}
|
||||
|
||||
|
||||
void MPICommunicator::Communicate(const Vector & x_s, Vector & x_r, int vdim, int ordering)
|
||||
{
|
||||
int npts = x_s.Size()/vdim;
|
||||
MFEM_VERIFY(npts == destination_procs.Size(), "Inconsistent number of points to be send");
|
||||
|
||||
// construct send count
|
||||
for (int i = 0; i<npts; i++)
|
||||
{
|
||||
int rank = destination_procs[i];
|
||||
send_count[rank] += vdim + 1; // including the sending processor id
|
||||
}
|
||||
|
||||
// 2. Compute recv_count
|
||||
MPI_Alltoall(&send_count[0],1,MPI_INT,&recv_count[0],1,MPI_INT,comm);
|
||||
|
||||
// 3. Compute displacements
|
||||
for (int k=0; k<num_procs-1; k++)
|
||||
{
|
||||
send_displ[k+1] = send_displ[k] + send_count[k];
|
||||
recv_displ[k+1] = recv_displ[k] + recv_count[k];
|
||||
}
|
||||
int sbuff_size = send_count.Sum();
|
||||
int rbuff_size = recv_count.Sum();
|
||||
|
||||
// 4. Allocate memory and fill in send buffers
|
||||
Array<double> sendvals(sbuff_size); sendvals = 0.0;
|
||||
Array<int> sendoffs(num_procs); sendoffs = 0;
|
||||
for (int i = 0; i<npts; i++)
|
||||
{
|
||||
int rank = destination_procs[i];
|
||||
int j = send_displ[rank] + sendoffs[rank];
|
||||
sendoffs[rank] += vdim+1;
|
||||
sendvals[j] = (double)myid;
|
||||
for (int k = 0; k<vdim; k++)
|
||||
{
|
||||
int kk = (ordering == mfem::Ordering::byNODES) ? k*npts+i : i*vdim + k;
|
||||
sendvals[j+k+1] = x_s(kk);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Communication
|
||||
Array<double> recvvals(rbuff_size);
|
||||
|
||||
double * sendvals_ptr = nullptr;
|
||||
double * recvvals_ptr = nullptr;
|
||||
if (sbuff_size !=0 ) { sendvals_ptr = &sendvals[0]; }
|
||||
if (rbuff_size !=0 ) { recvvals_ptr = &recvvals[0]; }
|
||||
|
||||
MPI_Alltoallv(sendvals_ptr, send_count, send_displ, MPI_DOUBLE, recvvals_ptr,
|
||||
recv_count, recv_displ, MPI_DOUBLE, comm);
|
||||
|
||||
// 6. Unpack
|
||||
int n = rbuff_size/(vdim+1);
|
||||
origin_procs.SetSize(n);
|
||||
x_r.SetSize(vdim*n);
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
origin_procs[i] = (unsigned int)recvvals[(vdim+1)*i];
|
||||
for (int j=0; j<vdim; j++)
|
||||
{
|
||||
int kk = (ordering == mfem::Ordering::byNODES) ? j*n+i : i*vdim + j;
|
||||
x_r(kk) = recvvals[(vdim+1)*i + j+1];
|
||||
}
|
||||
}
|
||||
resetcounts();
|
||||
}
|
||||
|
||||
void MPICommunicator::Communicate(const Array<unsigned int> & x_s, Array<unsigned int> & x_r, int vdim, int ordering)
|
||||
{
|
||||
int npts = x_s.Size()/vdim;
|
||||
MFEM_VERIFY(npts == destination_procs.Size(), "Inconsistent number of points to be send");
|
||||
|
||||
// construct send count
|
||||
for (int i = 0; i<npts; i++)
|
||||
{
|
||||
int rank = destination_procs[i];
|
||||
send_count[rank] += vdim + 1; // including the sending processor id
|
||||
}
|
||||
|
||||
// 2. Compute recv_count
|
||||
MPI_Alltoall(&send_count[0],1,MPI_INT,&recv_count[0],1,MPI_INT,comm);
|
||||
|
||||
// 3. Compute displacements
|
||||
for (int k=0; k<num_procs-1; k++)
|
||||
{
|
||||
send_displ[k+1] = send_displ[k] + send_count[k];
|
||||
recv_displ[k+1] = recv_displ[k] + recv_count[k];
|
||||
}
|
||||
int sbuff_size = send_count.Sum();
|
||||
int rbuff_size = recv_count.Sum();
|
||||
|
||||
// 4. Allocate memory and fill in send buffers
|
||||
Array<unsigned int> sendvals(sbuff_size); sendvals = 0.0;
|
||||
Array<int> sendoffs(num_procs); sendoffs = 0;
|
||||
for (int i = 0; i<npts; i++)
|
||||
{
|
||||
int rank = destination_procs[i];
|
||||
int j = send_displ[rank] + sendoffs[rank];
|
||||
sendoffs[rank] += vdim+1;
|
||||
sendvals[j] = myid;
|
||||
for (int k = 0; k<vdim; k++)
|
||||
{
|
||||
int kk = (ordering == mfem::Ordering::byNODES) ? k*npts+i : i*vdim + k;
|
||||
sendvals[j+k+1] = x_s[kk];
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Communication
|
||||
Array<unsigned int> recvvals(rbuff_size);
|
||||
|
||||
unsigned int * sendvals_ptr = nullptr;
|
||||
unsigned int * recvvals_ptr = nullptr;
|
||||
if (sbuff_size !=0 ) { sendvals_ptr = &sendvals[0]; }
|
||||
if (rbuff_size !=0 ) { recvvals_ptr = &recvvals[0]; }
|
||||
|
||||
MPI_Alltoallv(sendvals_ptr, send_count, send_displ, MPI_UNSIGNED, recvvals_ptr,
|
||||
recv_count, recv_displ, MPI_UNSIGNED, comm);
|
||||
|
||||
// 6. Unpack
|
||||
int n = rbuff_size/(vdim+1);
|
||||
origin_procs.SetSize(n);
|
||||
x_r.SetSize(vdim*n);
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
origin_procs[i] = recvvals[(vdim+1)*i];
|
||||
for (int j=0; j<vdim; j++)
|
||||
{
|
||||
int kk = (ordering == mfem::Ordering::byNODES) ? j*n+i : i*vdim + j;
|
||||
x_r[kk] = recvvals[(vdim+1)*i + j+1];
|
||||
}
|
||||
}
|
||||
resetcounts();
|
||||
}
|
||||
|
||||
void MPICommunicator::Communicate(const Array<int> & x_s, Array<int> & x_r, int vdim, int ordering)
|
||||
{
|
||||
int npts = x_s.Size()/vdim;
|
||||
MFEM_VERIFY(npts == destination_procs.Size(), "Inconsistent number of points to be send");
|
||||
|
||||
// construct send count
|
||||
for (int i = 0; i<npts; i++)
|
||||
{
|
||||
int rank = destination_procs[i];
|
||||
send_count[rank] += vdim + 1; // including the sending processor id
|
||||
}
|
||||
|
||||
// 2. Compute recv_count
|
||||
MPI_Alltoall(&send_count[0],1,MPI_INT,&recv_count[0],1,MPI_INT,comm);
|
||||
|
||||
// 3. Compute displacements
|
||||
for (int k=0; k<num_procs-1; k++)
|
||||
{
|
||||
send_displ[k+1] = send_displ[k] + send_count[k];
|
||||
recv_displ[k+1] = recv_displ[k] + recv_count[k];
|
||||
}
|
||||
int sbuff_size = send_count.Sum();
|
||||
int rbuff_size = recv_count.Sum();
|
||||
|
||||
// 4. Allocate memory and fill in send buffers
|
||||
Array<int> sendvals(sbuff_size); sendvals = 0.0;
|
||||
Array<int> sendoffs(num_procs); sendoffs = 0;
|
||||
for (int i = 0; i<npts; i++)
|
||||
{
|
||||
int rank = destination_procs[i];
|
||||
int j = send_displ[rank] + sendoffs[rank];
|
||||
sendoffs[rank] += vdim+1;
|
||||
sendvals[j] = myid;
|
||||
for (int k = 0; k<vdim; k++)
|
||||
{
|
||||
int kk = (ordering == mfem::Ordering::byNODES) ? k*npts+i : i*vdim + k;
|
||||
sendvals[j+k+1] = x_s[kk];
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Communication
|
||||
Array<int> recvvals(rbuff_size);
|
||||
|
||||
int * sendvals_ptr = nullptr;
|
||||
int * recvvals_ptr = nullptr;
|
||||
if (sbuff_size !=0 ) { sendvals_ptr = &sendvals[0]; }
|
||||
if (rbuff_size !=0 ) { recvvals_ptr = &recvvals[0]; }
|
||||
|
||||
MPI_Alltoallv(sendvals_ptr, send_count, send_displ, MPI_INT, recvvals_ptr,
|
||||
recv_count, recv_displ, MPI_INT, comm);
|
||||
|
||||
// 6. Unpack
|
||||
int n = rbuff_size/(vdim+1);
|
||||
origin_procs.SetSize(n);
|
||||
x_r.SetSize(vdim*n);
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
origin_procs[i] = (unsigned int)recvvals[(vdim+1)*i];
|
||||
for (int j=0; j<vdim; j++)
|
||||
{
|
||||
int kk = (ordering == mfem::Ordering::byNODES) ? j*n+i : i*vdim + j;
|
||||
x_r[kk] = recvvals[(vdim+1)*i + j+1];
|
||||
}
|
||||
}
|
||||
resetcounts();
|
||||
}
|
||||
|
||||
void MPICommunicator::Communicate(const DenseMatrix & A_s, DenseMatrix & A_r, int vdim, int ordering)
|
||||
{
|
||||
// matrix width corresponds to dim coordinates
|
||||
// matrix rows might include vdim copies
|
||||
int npts = A_s.Height()/vdim;
|
||||
int dim = A_s.Width();
|
||||
MFEM_VERIFY(npts == destination_procs.Size(), "Inconsistent number of points to be send");
|
||||
|
||||
// construct send count
|
||||
for (int i = 0; i<npts; i++)
|
||||
{
|
||||
int rank = destination_procs[i];
|
||||
send_count[rank] += dim*vdim + 1; // including the sending processor id
|
||||
}
|
||||
|
||||
// 2. Compute recv_count
|
||||
MPI_Alltoall(&send_count[0],1,MPI_INT,&recv_count[0],1,MPI_INT,comm);
|
||||
|
||||
// 3. Compute displacements
|
||||
for (int k=0; k<num_procs-1; k++)
|
||||
{
|
||||
send_displ[k+1] = send_displ[k] + send_count[k];
|
||||
recv_displ[k+1] = recv_displ[k] + recv_count[k];
|
||||
}
|
||||
int sbuff_size = send_count.Sum();
|
||||
int rbuff_size = recv_count.Sum();
|
||||
|
||||
// 4. Allocate memory and fill in send buffers
|
||||
Array<double> sendvals(sbuff_size); sendvals = 0.0;
|
||||
Array<int> sendoffs(num_procs); sendoffs = 0;
|
||||
for (int i = 0; i<npts; i++)
|
||||
{
|
||||
int rank = destination_procs[i];
|
||||
int j = send_displ[rank] + sendoffs[rank];
|
||||
sendoffs[rank] += dim*vdim+1;
|
||||
sendvals[j] = myid;
|
||||
for (int k = 0; k<vdim; k++)
|
||||
{
|
||||
int kk = (ordering == mfem::Ordering::byNODES) ? k*npts+i : i*vdim + k;
|
||||
for (int d=0; d<dim; d++)
|
||||
{
|
||||
sendvals[j+k*dim+d+1] = A_s(kk,d);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 5. Communication
|
||||
Array<double> recvvals(rbuff_size);
|
||||
|
||||
double * sendvals_ptr = nullptr;
|
||||
double * recvvals_ptr = nullptr;
|
||||
if (sbuff_size !=0 ) { sendvals_ptr = &sendvals[0]; }
|
||||
if (rbuff_size !=0 ) { recvvals_ptr = &recvvals[0]; }
|
||||
|
||||
MPI_Alltoallv(sendvals_ptr, send_count, send_displ, MPI_DOUBLE, recvvals_ptr,
|
||||
recv_count, recv_displ, MPI_DOUBLE, comm);
|
||||
|
||||
// 6. Unpack
|
||||
int n = rbuff_size/(dim*vdim+1);
|
||||
origin_procs.SetSize(n);
|
||||
A_r.SetSize(vdim*n,dim);
|
||||
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
origin_procs[i] = (unsigned int)recvvals[(dim*vdim+1)*i];
|
||||
for (int j=0; j<vdim; j++)
|
||||
{
|
||||
int kk = (ordering == mfem::Ordering::byNODES) ? j*n+i : i*vdim + j;
|
||||
for (int d=0; d<dim; d++)
|
||||
{
|
||||
A_r(kk,d) = recvvals[(dim*vdim+1)*i + j*dim + d+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
resetcounts();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void MPICommunicator::Communicate(const SparseMatrix & mat_s , SparseMatrix & mat_r)
|
||||
{
|
||||
// 1. Compute send_count
|
||||
int n = mat_s.NumRows();
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
int rsize = mat_s.RowSize(i);
|
||||
if (rsize == 0) continue;
|
||||
int rank = get_rank(i);
|
||||
send_count[rank] += rsize+2;
|
||||
}
|
||||
// 2. Compute recv_count
|
||||
MPI_Alltoall(&send_count[0],1,MPI_INT,&recv_count[0],1,MPI_INT,comm);
|
||||
|
||||
// 3. Compute displacements
|
||||
for (int k=0; k<num_procs-1; k++)
|
||||
{
|
||||
send_displ[k+1] = send_displ[k] + send_count[k];
|
||||
recv_displ[k+1] = recv_displ[k] + recv_count[k];
|
||||
}
|
||||
int sbuff_size = send_count.Sum();
|
||||
int rbuff_size = recv_count.Sum();
|
||||
|
||||
// 4. Allocate memory and fill in send buffers
|
||||
Array<double> sendvals(sbuff_size); sendvals = 0.0;
|
||||
Array<int> sendcols(sbuff_size); sendcols = 0;
|
||||
Array<int> sendoffs(num_procs); sendoffs = 0;
|
||||
Array<int> cols;
|
||||
Vector vals;
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
int rsize = mat_s.RowSize(i);
|
||||
if (rsize == 0) continue;
|
||||
int rank = get_rank(i);
|
||||
int j = send_displ[rank] + sendoffs[rank];
|
||||
mat_s.GetRow(i,cols,vals);
|
||||
sendoffs[rank] += rsize+2;
|
||||
sendvals[j] = (double)i;
|
||||
sendvals[j+1] = (double)rsize;
|
||||
sendcols[j] = i;
|
||||
sendcols[j+1] = rsize;
|
||||
for (int l=0; l<rsize ; l++)
|
||||
{
|
||||
sendvals[j+l+2] = vals[l];
|
||||
sendcols[j+l+2] = cols[l];
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Communication
|
||||
Array<double> recvvals(rbuff_size);
|
||||
Array<int> recvcols(rbuff_size);
|
||||
|
||||
double * sendvals_ptr = nullptr;
|
||||
double * recvvals_ptr = nullptr;
|
||||
int * sendcols_ptr = nullptr;
|
||||
int * recvcols_ptr = nullptr;
|
||||
if (sbuff_size !=0 )
|
||||
{
|
||||
sendvals_ptr = &sendvals[0];
|
||||
sendcols_ptr = &sendcols[0];
|
||||
}
|
||||
if (rbuff_size !=0 )
|
||||
{
|
||||
recvvals_ptr = &recvvals[0];
|
||||
recvcols_ptr = &recvcols[0];
|
||||
}
|
||||
|
||||
MPI_Alltoallv(sendvals_ptr, send_count, send_displ, MPI_DOUBLE, recvvals_ptr,
|
||||
recv_count, recv_displ, MPI_DOUBLE, comm);
|
||||
|
||||
MPI_Alltoallv(sendcols_ptr, send_count, send_displ, MPI_INT, recvcols_ptr,
|
||||
recv_count, recv_displ, MPI_INT, comm);
|
||||
|
||||
// 6. Unpack and store to the output SparseMatrix
|
||||
MFEM_VERIFY(mat_r.Height() == lsize, "Inconsistent row size of output SparseMatrix");
|
||||
MFEM_VERIFY(mat_r.Width() == mat_s.Width(), "Inconsistent column size of output SparseMatrix");
|
||||
|
||||
int counter = 0;
|
||||
while (counter < rbuff_size)
|
||||
{
|
||||
int row = recvcols[counter] - offset;
|
||||
int size = recvcols[counter+1];
|
||||
vals.SetSize(size);
|
||||
cols.SetSize(size);
|
||||
for (int i = 0; i<size; i++)
|
||||
{
|
||||
vals[i] = recvvals[counter+2 + i];
|
||||
cols[i] = recvcols[counter+2 + i];
|
||||
}
|
||||
mat_r.AddRow(row,cols,vals);
|
||||
counter += size+2;
|
||||
}
|
||||
MFEM_VERIFY(counter == rbuff_size, "inconsistent rbuff size");
|
||||
mat_r.Finalize();
|
||||
mat_r.SortColumnIndices();
|
||||
resetcounts();
|
||||
}
|
||||
|
||||
void MPICommunicator::Communicate(const Array<SparseMatrix*> & vmat_s, Array<SparseMatrix*> & vmat_r)
|
||||
{
|
||||
// 1. Compute send_count
|
||||
for (int k = 0; k<vmat_s.Size(); k++)
|
||||
{
|
||||
if (!vmat_s[k]) continue;
|
||||
if (vmat_s[k]->NumNonZeroElems() == 0) continue;
|
||||
int nrows = vmat_s[k]->NumRows();
|
||||
for (int i = 0; i<nrows; i++)
|
||||
{
|
||||
int rsize = vmat_s[k]->RowSize(i);
|
||||
if (rsize == 0) continue;
|
||||
int rank = get_rank(i);
|
||||
send_count[rank] += rsize+3;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Compute recv_count
|
||||
MPI_Alltoall(&send_count[0],1,MPI_INT,&recv_count[0],1,MPI_INT,comm);
|
||||
|
||||
// 3. Compute displacements
|
||||
for (int k=0; k<num_procs-1; k++)
|
||||
{
|
||||
send_displ[k+1] = send_displ[k] + send_count[k];
|
||||
recv_displ[k+1] = recv_displ[k] + recv_count[k];
|
||||
}
|
||||
int sbuff_size = send_count.Sum();
|
||||
int rbuff_size = recv_count.Sum();
|
||||
|
||||
// 4. Allocate memory and fill in send buffers
|
||||
Array<double> sendvals(sbuff_size); sendvals = 0.0;
|
||||
Array<int> sendcols(sbuff_size); sendcols = 0;
|
||||
Array<int> sendoffs(num_procs); sendoffs = 0;
|
||||
for (int k = 0; k<vmat_s.Size(); k++)
|
||||
{
|
||||
if (!vmat_s[k]) continue;
|
||||
if (vmat_s[k]->NumNonZeroElems() == 0) continue;
|
||||
int nrows = vmat_s[k]->NumRows();
|
||||
for (int i = 0; i<nrows; i++)
|
||||
{
|
||||
int rsize = vmat_s[k]->RowSize(i);
|
||||
if (rsize == 0) continue;
|
||||
int rank = get_rank(i);
|
||||
int j = send_displ[rank] + sendoffs[rank];
|
||||
Array<int> cols;
|
||||
Vector vals;
|
||||
vmat_s[k]->GetRow(i,cols,vals);
|
||||
sendoffs[rank] += rsize+3;
|
||||
sendvals[j] = (double)k;
|
||||
sendvals[j+1] = (double)i;
|
||||
sendvals[j+2] = (double)rsize;
|
||||
sendcols[j] = k;
|
||||
sendcols[j+1] = i;
|
||||
sendcols[j+2] = rsize;
|
||||
for (int l=0; l<rsize ; l++)
|
||||
{
|
||||
sendvals[j+l+3] = vals[l];
|
||||
sendcols[j+l+3] = cols[l];
|
||||
}
|
||||
}
|
||||
}
|
||||
// 5. Communication
|
||||
Array<double> recvvals(rbuff_size);
|
||||
Array<int> recvcols(rbuff_size);
|
||||
double * sendvals_ptr = nullptr;
|
||||
double * recvvals_ptr = nullptr;
|
||||
int * sendcols_ptr = nullptr;
|
||||
int * recvcols_ptr = nullptr;
|
||||
if (sbuff_size !=0 )
|
||||
{
|
||||
sendvals_ptr = &sendvals[0];
|
||||
sendcols_ptr = &sendcols[0];
|
||||
}
|
||||
if (rbuff_size !=0 )
|
||||
{
|
||||
recvvals_ptr = &recvvals[0];
|
||||
recvcols_ptr = &recvcols[0];
|
||||
}
|
||||
|
||||
MPI_Alltoallv(sendvals_ptr, send_count, send_displ, MPI_DOUBLE, recvvals_ptr,
|
||||
recv_count, recv_displ, MPI_DOUBLE,comm);
|
||||
|
||||
MPI_Alltoallv(sendcols_ptr, send_count, send_displ, MPI_INT, recvcols_ptr,
|
||||
recv_count, recv_displ, MPI_INT,comm);
|
||||
|
||||
// 6. Unpack and store to the output SparseMatrix
|
||||
int counter = 0;
|
||||
while (counter < rbuff_size)
|
||||
{
|
||||
int npt = recvcols[counter];
|
||||
int row = recvcols[counter+1] - offset;
|
||||
int size = recvcols[counter+2];
|
||||
Vector vals(size);
|
||||
Array<int> cols(size);
|
||||
for (int i = 0; i<size; i++)
|
||||
{
|
||||
vals[i] = recvvals[counter+3 + i];
|
||||
cols[i] = recvcols[counter+3 + i];
|
||||
}
|
||||
vmat_r[npt]->AddRow(row,cols,vals);
|
||||
counter += size+3;
|
||||
}
|
||||
MFEM_VERIFY(counter == rbuff_size, "inconsistent size");
|
||||
|
||||
for (int i = 0; i<vmat_r.Size(); i++)
|
||||
{
|
||||
vmat_r[i]->Finalize();
|
||||
vmat_r[i]->SortColumnIndices();
|
||||
}
|
||||
resetcounts();
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#include "mfem.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
class MPICommunicator
|
||||
{
|
||||
private:
|
||||
MPI_Comm comm;
|
||||
int myid, num_procs;
|
||||
Array<unsigned int > origin_procs;
|
||||
Array<unsigned int > destination_procs;
|
||||
int offset, lsize;
|
||||
std::vector<int> offsets;
|
||||
Array<int> send_count;
|
||||
Array<int> send_displ;
|
||||
Array<int> recv_count;
|
||||
Array<int> recv_displ;
|
||||
void resetcounts()
|
||||
{
|
||||
send_count = 0;
|
||||
send_displ = 0;
|
||||
recv_count = 0;
|
||||
recv_displ = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
MPICommunicator(MPI_Comm comm_, int offset_, int gsize);
|
||||
MPICommunicator(MPI_Comm comm_, Array<unsigned int> & destination_procs_);
|
||||
|
||||
int get_rank(int dof);
|
||||
|
||||
Array<unsigned int> & GetOriginProcs() {return origin_procs;}
|
||||
void UpdateDestinationProcs()
|
||||
{
|
||||
destination_procs.SetSize(origin_procs.Size());
|
||||
destination_procs = origin_procs;
|
||||
resetcounts();
|
||||
}
|
||||
void Communicate(const Vector & x_s, Vector & x_r, int vdim, int ordering);
|
||||
void Communicate(const Array<int> & x_s, Array<int> & x_r, int vdim, int ordering);
|
||||
void Communicate(const DenseMatrix & A_s, DenseMatrix & A_r, int vdim, int ordering);
|
||||
void Communicate(const Array<unsigned int> & x_s, Array<unsigned int> & x_r, int vdim, int ordering);
|
||||
void Communicate(const SparseMatrix & mat_s , SparseMatrix & mat_r);
|
||||
void Communicate(const Array<SparseMatrix*> & vmat_s, Array<SparseMatrix*> & vmat_r);
|
||||
};
|
||||
@@ -1,171 +0,0 @@
|
||||
#include "util.hpp"
|
||||
|
||||
|
||||
void PrintVertex(Mesh * mesh, int vertex)
|
||||
{
|
||||
Array<int> vertices;
|
||||
mfem::out << "vertex: " << vertex << ": ";
|
||||
double *coords = mesh->GetVertex(vertex);
|
||||
mfem::out << "(" << coords[0] << ", " << coords[1] << ", " << coords[2] << ") \n";
|
||||
}
|
||||
|
||||
void PrintElementVertices(Mesh * mesh, int elem)
|
||||
{
|
||||
Array<int> vertices;
|
||||
mfem::out << "elem: " << elem << ". Vertices = \n" ;
|
||||
mesh->GetElementVertices(elem,vertices);
|
||||
for (int i = 0; i<vertices.Size(); i++)
|
||||
{
|
||||
PrintVertex(mesh,vertices[i]);
|
||||
}
|
||||
mfem::out << endl;
|
||||
}
|
||||
|
||||
void PrintFaceVertices(Mesh * mesh, int face)
|
||||
{
|
||||
Array<int> vertices;
|
||||
mfem::out << "face: " << face << ". Vertices = \n" ;
|
||||
mesh->GetFaceVertices(face,vertices);
|
||||
for (int i = 0; i<vertices.Size(); i++)
|
||||
{
|
||||
PrintVertex(mesh,vertices[i]);
|
||||
}
|
||||
mfem::out << endl;
|
||||
}
|
||||
|
||||
void PrintSet(const std::set<int> & a, const char *aname)
|
||||
{
|
||||
mfem::out << aname << " = " ;
|
||||
for (std::set<int>::iterator it = a.begin(); it!= a.end(); it++)
|
||||
{
|
||||
mfem::out << *it << " ";
|
||||
}
|
||||
mfem::out << endl;
|
||||
}
|
||||
|
||||
void PrintVector(const Vector & a, const char *aname)
|
||||
{
|
||||
int sz = a.Size();
|
||||
mfem::out << aname << " = " ;
|
||||
for (int i = 0; i<sz; i++)
|
||||
{
|
||||
mfem::out << a[i] << " ";
|
||||
}
|
||||
mfem::out << endl;
|
||||
}
|
||||
|
||||
void PrintVertex(Mesh * mesh, int vertex, int printid)
|
||||
{
|
||||
int myid = Mpi::WorldRank();
|
||||
if (myid == printid)
|
||||
{
|
||||
mfem::out << "myid = " << myid <<": " << "vertex: " << vertex << ": ";
|
||||
double *coords = mesh->GetVertex(vertex);
|
||||
mfem::out << "(" << coords[0] << ", " << coords[1] << ", " << coords[2] << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
void PrintElementVertices(Mesh * mesh, int elem, int printid)
|
||||
{
|
||||
int myid = Mpi::WorldRank();
|
||||
Array<int> vertices;
|
||||
if (myid == printid)
|
||||
{
|
||||
mfem::out << "myid = " << myid <<": " << "elem: " << elem <<
|
||||
". Vertices = \n" ;
|
||||
mesh->GetElementVertices(elem,vertices);
|
||||
for (int i = 0; i<vertices.Size(); i++)
|
||||
{
|
||||
PrintVertex(mesh,vertices[i],printid);
|
||||
}
|
||||
mfem::out << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintFaceVertices(Mesh * mesh, int face, int printid)
|
||||
{
|
||||
int myid = Mpi::WorldRank();
|
||||
Array<int> vertices;
|
||||
if (myid == printid)
|
||||
{
|
||||
mfem::out << "myid = " << myid <<": " << "face: " << face <<
|
||||
". Vertices = \n" ;
|
||||
mesh->GetFaceVertices(face,vertices);
|
||||
for (int i = 0; i<vertices.Size(); i++)
|
||||
{
|
||||
PrintVertex(mesh,vertices[i],printid);
|
||||
}
|
||||
mfem::out << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PrintSet(const std::set<int> & a, const char *aname, int printid)
|
||||
{
|
||||
int myid = Mpi::WorldRank();
|
||||
if (myid == printid)
|
||||
{
|
||||
mfem::out << "myid = " << myid <<": " << aname << " = " ;
|
||||
for (std::set<int>::iterator it = a.begin(); it!= a.end(); it++)
|
||||
{
|
||||
mfem::out << *it << " ";
|
||||
}
|
||||
mfem::out << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintVector(const Vector & a, const char *aname, int printid)
|
||||
{
|
||||
int myid = Mpi::WorldRank();
|
||||
if (myid == printid)
|
||||
{
|
||||
int sz = a.Size();
|
||||
mfem::out << "myid = " << myid <<": " << aname << " = " ;
|
||||
for (int i = 0; i<sz; i++)
|
||||
{
|
||||
mfem::out << a[i] << " ";
|
||||
}
|
||||
mfem::out << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintVector(const std::vector<int> & a, const char *aname, int printid)
|
||||
{
|
||||
int myid = Mpi::WorldRank();
|
||||
if (myid == printid)
|
||||
{
|
||||
int sz = a.size();
|
||||
mfem::out << "myid = " << myid <<": " << aname << " = " ;
|
||||
for (int i = 0; i<sz; i++)
|
||||
{
|
||||
mfem::out << a[i] << " ";
|
||||
}
|
||||
mfem::out << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintVector(const std::vector<unsigned int> & a, const char *aname, int printid)
|
||||
{
|
||||
int myid = Mpi::WorldRank();
|
||||
if (myid == printid)
|
||||
{
|
||||
int sz = a.size();
|
||||
mfem::out << "myid = " << myid <<": " << aname << " = " ;
|
||||
for (int i = 0; i<sz; i++)
|
||||
{
|
||||
mfem::out << a[i] << " ";
|
||||
}
|
||||
mfem::out << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintSparseMatrix(const SparseMatrix & a, const char *aname, int printid)
|
||||
{
|
||||
int myid = Mpi::WorldRank();
|
||||
if (myid == printid)
|
||||
{
|
||||
mfem::out << "myid = " << myid <<": " << aname << " = " ;
|
||||
a.PrintMatlab(mfem::out);
|
||||
}
|
||||
mfem::out << endl;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
#include "mfem.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
void PrintVertex(Mesh * mesh, int vertex);
|
||||
void PrintElementVertices(Mesh * mesh, int elem);
|
||||
void PrintFaceVertices(Mesh * mesh, int face);
|
||||
template <class T>
|
||||
void PrintArray(const Array<T> & a, const char *aname)
|
||||
{
|
||||
int sz = a.Size();
|
||||
mfem::out << aname << " = " ;
|
||||
for (int i = 0; i<sz; i++)
|
||||
{
|
||||
mfem::out << a[i] << " ";
|
||||
}
|
||||
mfem::out << endl;
|
||||
}
|
||||
void PrintSet(const std::set<int> & a, const char *aname);
|
||||
void PrintVector(const Vector & a, const char *aname);
|
||||
|
||||
// for parallel
|
||||
void PrintVertex(Mesh * mesh, int vertex, int printid);
|
||||
void PrintElementVertices(Mesh * mesh, int elem, int printid);
|
||||
void PrintFaceVertices(Mesh * mesh, int face, int printid);
|
||||
template <class T>
|
||||
void PrintArray(const Array<T> & a, const char *aname, int printid)
|
||||
{
|
||||
int myid = Mpi::WorldRank();
|
||||
if (myid == printid)
|
||||
{
|
||||
int sz = a.Size();
|
||||
mfem::out << "myid = " << myid <<": " << aname << " = " ;
|
||||
for (int i = 0; i<sz; i++)
|
||||
{
|
||||
mfem::out << a[i] << " ";
|
||||
}
|
||||
mfem::out << endl;
|
||||
}
|
||||
}
|
||||
void PrintSet(const std::set<int> & a, const char *aname, int printid);
|
||||
void PrintVector(const Vector & a, const char *aname, int printid);
|
||||
void PrintVector(const std::vector<int> & a, const char *aname, int printid);
|
||||
void PrintVector(const std::vector<unsigned int> & a, const char *aname, int printid);
|
||||
void PrintSparseMatrix(const SparseMatrix & a, const char *aname, int printid);
|
||||
@@ -271,7 +271,7 @@ int main(int argc, char *argv[])
|
||||
socketstream p_out_i;
|
||||
|
||||
double err0 = 0.;
|
||||
int dof0;
|
||||
int dof0 = 0; // init to suppress gcc warning
|
||||
|
||||
std::cout << "\n Ref |"
|
||||
<< " Dofs |"
|
||||
|
||||
@@ -273,7 +273,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
double res0 = 0.;
|
||||
double err0 = 0.;
|
||||
int dof0;
|
||||
int dof0 = 0; // init to suppress gcc warning
|
||||
std::cout << "\n Ref |"
|
||||
<< " Dofs |"
|
||||
<< " L2 Error |"
|
||||
|
||||
@@ -345,7 +345,7 @@ int main(int argc, char *argv[])
|
||||
socketstream E_out_i;
|
||||
|
||||
double err0 = 0.;
|
||||
int dof0;
|
||||
int dof0 = 0; // init to suppress gcc warning
|
||||
|
||||
std::cout << "\n Ref |"
|
||||
<< " Dofs |"
|
||||
|
||||
@@ -333,7 +333,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
double res0 = 0.;
|
||||
double err0 = 0.;
|
||||
int dof0;
|
||||
int dof0 = 0; // init to suppress gcc warning
|
||||
if (myid == 0)
|
||||
{
|
||||
std::cout << " Ref |"
|
||||
|
||||
@@ -728,7 +728,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
double res0 = 0.;
|
||||
double err0 = 0.;
|
||||
int dof0;
|
||||
int dof0 = 0; // init to suppress gcc warning
|
||||
|
||||
Array<int> elements_to_refine;
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ int main(int argc, char *argv[])
|
||||
// We can use the same material maps for both problems.
|
||||
|
||||
std::map<int, double> sigmaMap, InvTcondMap, TcapMap, InvTcapMap;
|
||||
double sigmaAir;
|
||||
double sigmaAir = 0.0; // init to suppress gcc warning
|
||||
double TcondAir;
|
||||
double TcapAir;
|
||||
if (strcmp(problem,"rod")==0 || strcmp(problem,"coil")==0)
|
||||
|
||||
@@ -13,9 +13,11 @@ set(MESH_FILES
|
||||
amr-quad-q2.mesh
|
||||
blade.mesh
|
||||
cube.mesh
|
||||
cube-tet.mesh
|
||||
icf.mesh
|
||||
jagged.mesh
|
||||
square01.mesh
|
||||
square01-tri.mesh
|
||||
stretched2D.mesh
|
||||
)
|
||||
|
||||
@@ -109,6 +111,12 @@ if (MFEM_USE_MPI)
|
||||
LIBRARIES mfem mfem-common)
|
||||
add_dependencies(pmesh-fitting copy_miniapps_meshing_data)
|
||||
|
||||
add_mfem_miniapp(fit-node-position
|
||||
MAIN fit-node-position.cpp
|
||||
${MFEM_MINIAPPS_COMMON_HEADERS}
|
||||
LIBRARIES mfem mfem-common)
|
||||
add_dependencies(fit-node-position copy_miniapps_meshing_data)
|
||||
|
||||
add_mfem_miniapp(pminimal-surface
|
||||
MAIN pminimal-surface.cpp
|
||||
LIBRARIES mfem)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,227 @@
|
||||
// Copyright (c) 2010-2023, 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.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
// Fitting of Selected Mesh Nodes to Specified Physical Positions
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This example fits a selected set of the mesh nodes to given physical
|
||||
// positions while maintaining a valid mesh with good quality.
|
||||
//
|
||||
// Sample runs:
|
||||
// mpirun -np 4 fit-node-position
|
||||
// mpirun -np 4 fit-node-position -m square01-tri.mesh
|
||||
// mpirun -np 4 fit-node-position -m ./cube.mesh
|
||||
// mpirun -np 4 fit-node-position -m ./cube-tet.mesh -rs 0
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "../common/mfem-common.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using namespace std;
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
int wsize = 350;
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
// Initialize MPI.
|
||||
Mpi::Init();
|
||||
int myid = Mpi::WorldRank();
|
||||
|
||||
const char *mesh_file = "square01.mesh";
|
||||
int rs_levels = 2;
|
||||
int mesh_poly_deg = 2;
|
||||
int quad_order = 5;
|
||||
bool glvis = true;
|
||||
|
||||
// Parse command-line options.
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&rs_levels, "-rs", "--refine-serial",
|
||||
"Number of times to refine the mesh uniformly in serial.");
|
||||
args.AddOption(&mesh_poly_deg, "-o", "--order",
|
||||
"Polynomial degree of mesh finite element space.");
|
||||
args.AddOption(&quad_order, "-qo", "--quad_order",
|
||||
"Order of the quadrature rule.");
|
||||
args.AddOption(&glvis, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0) { args.PrintUsage(cout); }
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0) { args.PrintOptions(cout); }
|
||||
|
||||
// Read and refine the mesh.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1, false);
|
||||
for (int lev = 0; lev < rs_levels; lev++) { mesh->UniformRefinement(); }
|
||||
ParMesh pmesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
const int dim = pmesh.Dimension();
|
||||
|
||||
// Setup mesh curvature and GridFunction that stores the coordinates.
|
||||
FiniteElementCollection *fec_mesh;
|
||||
if (mesh_poly_deg <= 0)
|
||||
{
|
||||
fec_mesh = new QuadraticPosFECollection;
|
||||
mesh_poly_deg = 2;
|
||||
}
|
||||
else { fec_mesh = new H1_FECollection(mesh_poly_deg, dim); }
|
||||
ParFiniteElementSpace pfes_mesh(&pmesh, fec_mesh, dim);
|
||||
pmesh.SetNodalFESpace(&pfes_mesh);
|
||||
ParGridFunction coord(&pfes_mesh);
|
||||
pmesh.SetNodalGridFunction(&coord);
|
||||
ParGridFunction x0(coord);
|
||||
|
||||
// Pick which nodes to fit and select the target positions.
|
||||
// (attribute 2 would have a prescribed deformation in y-direction, same x).
|
||||
Array<bool> fit_marker(pfes_mesh.GetNDofs());
|
||||
ParGridFunction fit_marker_vis_gf(&pfes_mesh);
|
||||
ParGridFunction coord_target(&pfes_mesh);
|
||||
Array<int> vdofs;
|
||||
fit_marker = false;
|
||||
coord_target = coord;
|
||||
fit_marker_vis_gf = 0.0;
|
||||
for (int e = 0; e < pmesh.GetNBE(); e++)
|
||||
{
|
||||
const int nd = pfes_mesh.GetBE(e)->GetDof();
|
||||
const int attr = pmesh.GetBdrElement(e)->GetAttribute();
|
||||
if (attr != 2) { continue; }
|
||||
|
||||
pfes_mesh.GetBdrElementVDofs(e, vdofs);
|
||||
for (int j = 0; j < nd; j++)
|
||||
{
|
||||
int j_x = vdofs[j], j_y = vdofs[nd+j];
|
||||
const double x = coord(j_x),
|
||||
z = (dim == 2) ? 0.0 : coord(vdofs[2*nd + j]);
|
||||
fit_marker[pfes_mesh.VDofToDof(j_x)] = true;
|
||||
fit_marker_vis_gf(j_x) = 1.0;
|
||||
if (coord(j_y) < 0.5)
|
||||
{
|
||||
coord_target(j_y) = 0.1 * sin(4 * M_PI * x) * cos(M_PI * z);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (coord(j_x) < 0.5)
|
||||
{
|
||||
coord_target(j_y) = 1.0 + 0.1 * sin(2 * M_PI * x);
|
||||
}
|
||||
else
|
||||
{
|
||||
coord_target(j_y) = 1.0 + 0.1 * sin(2 * M_PI * (x + 0.5));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Visualize the selected nodes and their target positions.
|
||||
if (glvis)
|
||||
{
|
||||
socketstream vis1;
|
||||
coord = coord_target;
|
||||
common::VisualizeField(vis1, "localhost", 19916, fit_marker_vis_gf,
|
||||
"Target positions (DOFS with value 1)",
|
||||
0, 0, 400, 400, (dim == 2) ? "Rjm" : "");
|
||||
coord = x0;
|
||||
}
|
||||
|
||||
// Allow slipping along the remaining boundaries.
|
||||
// (attributes 1 and 3 would slip, while 4 is completely fixed).
|
||||
int n = 0;
|
||||
for (int i = 0; i < pmesh.GetNBE(); i++)
|
||||
{
|
||||
const int nd = pfes_mesh.GetBE(i)->GetDof();
|
||||
const int attr = pmesh.GetBdrElement(i)->GetAttribute();
|
||||
MFEM_VERIFY(!(dim == 2 && attr == 3),
|
||||
"Boundary attribute 3 must be used only for 3D meshes. "
|
||||
"Adjust the attributes (1/2/3/4 for fixed x/y/z/all "
|
||||
"components, rest for free nodes), or use -fix-bnd.");
|
||||
if (attr == 1 || attr == 3) { n += nd; }
|
||||
if (attr == 4) { n += nd * dim; }
|
||||
}
|
||||
Array<int> ess_vdofs(n);
|
||||
n = 0;
|
||||
for (int i = 0; i < pmesh.GetNBE(); i++)
|
||||
{
|
||||
const int nd = pfes_mesh.GetBE(i)->GetDof();
|
||||
const int attr = pmesh.GetBdrElement(i)->GetAttribute();
|
||||
pfes_mesh.GetBdrElementVDofs(i, vdofs);
|
||||
if (attr == 1) // Fix x components.
|
||||
{
|
||||
for (int j = 0; j < nd; j++)
|
||||
{ ess_vdofs[n++] = vdofs[j]; }
|
||||
}
|
||||
else if (attr == 3) // Fix z components.
|
||||
{
|
||||
for (int j = 0; j < nd; j++)
|
||||
{ ess_vdofs[n++] = vdofs[j+2*nd]; }
|
||||
}
|
||||
else if (attr == 4) // Fix all components.
|
||||
{
|
||||
for (int j = 0; j < vdofs.Size(); j++)
|
||||
{ ess_vdofs[n++] = vdofs[j]; }
|
||||
}
|
||||
}
|
||||
|
||||
// TMOP setup.
|
||||
TMOP_QualityMetric *metric;
|
||||
if (dim == 2) { metric = new TMOP_Metric_002; }
|
||||
else { metric = new TMOP_Metric_302; }
|
||||
TargetConstructor target(TargetConstructor::IDEAL_SHAPE_UNIT_SIZE,
|
||||
pfes_mesh.GetComm());
|
||||
ConstantCoefficient fit_weight(100.0);
|
||||
auto integ = new TMOP_Integrator(metric, &target, nullptr);
|
||||
integ->EnableSurfaceFitting(coord_target, fit_marker, fit_weight);
|
||||
|
||||
// Linear solver.
|
||||
MINRESSolver minres(pfes_mesh.GetComm());
|
||||
minres.SetMaxIter(100);
|
||||
minres.SetRelTol(1e-12);
|
||||
minres.SetAbsTol(0.0);
|
||||
|
||||
// Nonlinear solver.
|
||||
ParNonlinearForm a(&pfes_mesh);
|
||||
a.SetEssentialVDofs(ess_vdofs);
|
||||
a.AddDomainIntegrator(integ);
|
||||
const IntegrationRule &ir =
|
||||
IntRules.Get(pfes_mesh.GetFE(0)->GetGeomType(), quad_order);
|
||||
TMOPNewtonSolver solver(pfes_mesh.GetComm(), ir, 0);
|
||||
solver.SetOperator(a);
|
||||
solver.SetPreconditioner(minres);
|
||||
solver.SetPrintLevel(1);
|
||||
solver.SetMaxIter(200);
|
||||
solver.SetRelTol(1e-10);
|
||||
solver.SetAbsTol(0.0);
|
||||
solver.EnableAdaptiveSurfaceFitting();
|
||||
solver.SetTerminationWithMaxSurfaceFittingError(1e-3);
|
||||
|
||||
// Solve.
|
||||
Vector b(0);
|
||||
coord.SetTrueVector();
|
||||
solver.Mult(b, coord.GetTrueVector());
|
||||
coord.SetFromTrueVector();
|
||||
|
||||
if (glvis)
|
||||
{
|
||||
socketstream vis2;
|
||||
common::VisualizeMesh(vis2, "localhost", 19916, pmesh, "Final mesh",
|
||||
400, 0, 400, 400);
|
||||
}
|
||||
|
||||
delete metric;
|
||||
return 0;
|
||||
}
|
||||
@@ -28,7 +28,7 @@ MFEM_LIB_FILE = mfem_is_not_built
|
||||
SEQ_MINIAPPS = mobius-strip klein-bottle toroid trimmer twist mesh-explorer\
|
||||
shaper extruder mesh-optimizer minimal-surface polar-nc reflector\
|
||||
mesh-quality
|
||||
PAR_MINIAPPS = pmesh-optimizer pminimal-surface pmesh-fitting
|
||||
PAR_MINIAPPS = pmesh-optimizer pminimal-surface pmesh-fitting fit-node-position
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
MINIAPPS = $(SEQ_MINIAPPS)
|
||||
else
|
||||
@@ -62,11 +62,11 @@ lib-common:
|
||||
# Rules to copy the *.mesh files - needed for running the sample runs when
|
||||
# building out-of-source:
|
||||
ifneq ($(SRC),)
|
||||
MESH_FILES = amr-quad-q2.mesh blade.mesh cube.mesh icf.mesh jagged.mesh\
|
||||
square01.mesh stretched2D.mesh
|
||||
MESH_FILES = amr-quad-q2.mesh blade.mesh cube.mesh cube-tet.mesh icf.mesh\
|
||||
jagged.mesh square01.mesh square01-tri.mesh stretched2D.mesh
|
||||
$(MESH_FILES): %: $(SRC)%
|
||||
ln -sf $(<) .
|
||||
mesh-optimizer pmesh-optimizer pmesh-fitting: | $(MESH_FILES)
|
||||
mesh-optimizer pmesh-optimizer pmesh-fitting fit-node-position: | $(MESH_FILES)
|
||||
.PHONY: copy-data
|
||||
copy-data: | $(MESH_FILES)
|
||||
endif
|
||||
@@ -92,6 +92,8 @@ mesh-quality-test-seq: mesh-quality
|
||||
@$(call mfem-test,$<,, Mesh quality miniapp)
|
||||
pmesh-fitting-test-par: pmesh-fitting
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel mesh fitting miniapp)
|
||||
fit-node-position-test-par: fit-node-position
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel position fitting miniapp)
|
||||
minimal-surface-test-seq: minimal-surface
|
||||
@$(call mfem-test,$<,, Meshing miniapp)
|
||||
pminimal-surface-test-par: pminimal-surface
|
||||
@@ -117,7 +119,7 @@ clean-build:
|
||||
rm -f *.o *~ mobius-strip klein-bottle toroid twist
|
||||
rm -f mesh-explorer shaper extruder trimmer reflector
|
||||
rm -f mesh-optimizer pmesh-optimizer pmesh-fitting polar-nc
|
||||
rm -f minimal-surface pminimal-surface mesh-quality
|
||||
rm -f minimal-surface pminimal-surface mesh-quality fit-node-position
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
|
||||
@@ -259,10 +259,16 @@ public:
|
||||
double small = 0.001, big = 0.01;
|
||||
if (dim == 3) { small = 0.005, big = 0.1; }
|
||||
const double xc = pos(0) - 0.5, yc = pos(1) - 0.5;
|
||||
double zc;
|
||||
if (dim == 3) { zc = pos(2) - 0.5; }
|
||||
double r = sqrt(xc*xc + yc*yc);
|
||||
if (dim == 3) { r = sqrt(xc*xc + yc*yc + zc*zc); }
|
||||
double r;
|
||||
if (dim == 2)
|
||||
{
|
||||
r = sqrt(xc*xc + yc*yc);
|
||||
}
|
||||
else
|
||||
{
|
||||
const double zc = pos(2) - 0.5;
|
||||
r = sqrt(xc*xc + yc*yc + zc*zc);
|
||||
}
|
||||
double r1 = 0.15; double r2 = 0.35; double sf=30.0;
|
||||
|
||||
const double tan1 = std::tanh(sf*(r-r1)),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user