diff --git a/.gitignore b/.gitignore index d11eda74a..1a4ad3d6f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ .project .tags .tags_sorted_by_file -.settings \ No newline at end of file +.settings +/Debug/ diff --git a/.travis.yml b/.travis.yml index ff8b4a9e7..7cfbb2029 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,6 @@ os: env: global: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then PATH=/usr/lib/ccache:${PATH}; fi - TIMEOUT_BUILD='30m' - BUILD_SUCCESS=true @@ -36,12 +35,19 @@ env: cache: ccache: true directories: - - travis_root + #- travis_root + - $HOME/.ccache #- $HOME/Library/Caches/Homebrew # TODO: how can we do conditional caching of directories (depending on OS) #- /home/travis/build/miho/OCC-CSG/oce-OCE-0.18.3 #- /Users/travis/build/miho/ugcore/ +# prepare ccache for (optional) install step +before_install: + # installing ccache and freetype packages via homebrew + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install ccache; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PATH="/usr/local/opt/ccache/libexec:$PATH"; fi + # prepare compilation before_script: - mkdir -p travis_root && cd travis_root @@ -83,5 +89,5 @@ script: # ccache fixes global build time of ~ 50 minutes # we still need to use travis_wait to prevent "build error because of no output" # https://docs.travis-ci.com/user/common-build-problems/#Build-times-out-because-no-output-was-received - - travis_wait 45 make -j3 + - travis_wait 45 make -j2 - if [[ "$TARGET" == "ugshell" ]]; then ../bin/ugshell -call "print(\"it works\")"; fi diff --git a/README.md b/README.md index 07a22aefc..5e1e63389 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,198 @@ [![Code Climate](https://codeclimate.com/github/UG4/ugcore/badges/gpa.svg)](https://codeclimate.com/github/UG4/ugcore) [![Issue Count](https://codeclimate.com/github/UG4/ugcore/badges/issue_count.svg)](https://codeclimate.com/github/UG4/ugcore) -core functionality of **UG4**. Includes sources, build-scripts, -and utility-scripts +This repository contains the core functionality of **UG4**. Includes sources, build-scripts, and utility-scripts -Copyright 2009-2015 Goethe Center for Scientific Computing, University Frankfurt +Copyright 2009-2018 Goethe Center for Scientific Computing, Goethe-University Frankfurt am Main -Please install/clone this repository through UG4's package manager -[ughub](https://github.com/UG4/ughub). +Please install/clone this repository through *UG4's* package manager +[*ughub*](https://github.com/UG4/ughub). + +# + +# Introduction to UG4 + +*UG4* is an extensive, flexible, cross-platform open source simulation framework for the numerical solution of systems of partial differential equations. Using *Finite Element* and *Finite Volume* methods on hybrid, adaptive, unstructured multigrid hierarchies, *UG4* allows for the simulation of complex real world models (physical, biological etc.) on massively parallel computer architectures. + +*UG4* is implemented in the *C++* programming language and provides grid management, discretization and (linear as well as non-linear) solver utilities. It is extensible and customizable via its plugin mechanism. The highly scalable *MPI* based parallelization of *UG4* has been shown to scale to hundred thousands of cores. + +Simulation workflows are defined either using the *Lua* scripting language or the graphical VRL interface [https://vrl-studio.mihosoft.eu/](https://vrl-studio.mihosoft.eu/). Besides that, UG4 can be used as a library for third-party code. + +Several examples are provided in the *Examples* application that can be used for simulations of the corresponding phenomena but also serve as demonstration modules for implementing user-defined plugins and scripts. By developing custom plugins, users can extend the functionality of the framework for their particular purposes. The framework provides coupling facilities for the models implemented in different plugins. + +The source code is commented using the Doxygen markup language. + +*UG4* is licensed under the *LGPL v3* license with amendments. Please have a look at the accompanying *LICENSE* file. + +## Preparation of Data and Visualization of Results + +The `.ugx` grids provided in the *Examples* applications can be created, visualized, and edited with *ProMesh*, a versatile graphical meshing solution for the generation, visualization, and preparation of computational domains for scientific computing on structured and unstructured grids. It allows users to process complicated geometries with curved boundaries and low-dimensional manifolds. *ProMesh* is based on *UG4's* grid manager and is available at http://www.promesh3d.com + +![promesh](docs/ug4/img/readme/promesh.png "ProMesh") + +The computational domain, the underlying grid, the boundary and initial conditions, as well as the problem coefficients can be specified as part of the simulation workflow in *Lua* scripts or the *VRL-Studio* GUI. However, for performance reasons, if the evaluation of the spatial and time dependence of the certain parameters is numerically expensive, one can implement those in C+\+, too, e.g. in a custom plugin. + +Intermediate and final results of a simulation can be written to *VTK's* `.vtu` file format. These can be visualized using a number of free and open-source toolkits, e.g. *ParaView* [http://www.paraview.org](http://www.paraview.org) and *VisIt* [https://wci.llnl.gov/simulation/computer-codes/visit/](https://wci.llnl.gov/simulation/computer-codes/visit/). + +## Setup and Compilation: + +For the following guide, we assume the compilation is performed on a Linux/Unix machine using a standard Bash terminal. Please note that Microsoft Windows features such an environment through the free 'Ubuntu' app in the Microsoft Windows Store. If a native Windows executable is required, Visual Studio compilers can also be used. + +**Requirements:** + +- C++ Compiler (tested with GCC on Linux, Clang on macOS and MSVC 2017 on Windows) +- CMake >= 2.8 +- ughub (https://github.com/UG4/ughub) +- git +- python + +For a detailed list on required software and corresponding installation instructions, please visit https://github.com/UG4/ughub + +Please start by creating a *UG4* root directory, e.g. `$HOME/ug4`. In your `ug4` directory please run the following commands to obtain all required sources: + + ughub init + ughub install Examples + +This will clone *ugcore*, the *Examples* app and all required plugins. + +Starting from *UG4's* root directory, please execute the following to build *UG4*: + + mkdir build + cd build + cmake -DENABLE_ALL_PLUGINS=ON -DDIM="2;3" -DCPU="1;2" -DCMAKE_BUILD_TYPE=Release .. + make -j + cd .. + + +## Running Examples: +Starting from *UG4's* root directory, please execute the following: + + source ugcore/scripts/shell/ugbash + mkdir runs + cd runs + ugshell -ex Examples/poisson.lua + ugshell -ex Examples/poisson.lua -dim 3 + ugshell -ex Examples/solmech.lua + ugshell -ex Examples/elder_adapt.lua + ugshell -ex Examples/navier_stokes.lua + ugshell -ex Examples/electromagnetism_pan.lua -numRefs 3 + +## Examples: + +Please make sure that you installed *UG4's* *Examples* application as described above. + +### Poisson Problem +A script computing the solution of the *Poisson* problem is given in `apps/Examples/poisson.lua`. The right hand side of the differential equation can be specified through a callback method in the *Lua* script. +A projector is used during grid refinement to approximate a circle with additional refinements. + +![poisson](docs/ug4/img/readme/poisson.png "poisson") + +### Linear Elasticity +A 3d simulation of deformation using *linear elasticity* is provided in `apps/Examples/solmech.lua`. + +![springboard](docs/ug4/img/readme/springboard.png "springboard") + +### Density Driven Flow +An adaptive simulation of the *Elder* problem is provided in `apps/Examples/elder_adapt.lua`. A gradient based error indicator is used to refine areas of interest. + +![elder adaptive](docs/ug4/img/readme/elder_adapt.png "elder adaptive") + +### Navier Stokes +Simulations of fluid flow in a channel with a cylindrical cutout are performed in `apps/Examples/navier_stokes.lua`. + +![navier_stokes](docs/ug4/img/readme/navier_stokes.png "navier_stokes") + +### Electromagnetism (induction heating) +Simulation of the eddy currents and the corresponding heat sources induced by alternating electromagnetic field in a conductive plate is represented in `apps/Examlpes/electromagnetism_pan.lua`. The stationary E-based formulation of the eddy current model for the complex-valued fields is used. The discretization of the Maxwell equations is done by the Nedelec elements on a tetrahedral grid. This example demonstrates in particular the projection of the refined elements to the curved boundaries in 3d. + +![heat](docs/ug4/img/readme/PanSolution3d-coil_and_pan_v3-lev4.png "pan_solution") + +## UG4 for VRL-Studio: + +VRL-Studio is an innovative and powerful IDE for rapid prototyping, learning, teaching and experimentation. It introduces *Visual Reflection* for automatic user interface generation. It combines textual and visual programming in an intuitive user interface. *UG4* provides a built-in VRL binding that allows to setup and execute visual simulation workflows from VRL-Studio. The supplementary VRL-Studio folder contains a precompiled version of VRL-Studio which includes *UG4* and a sample project to briefly demonstrate UG4s capabilities as VRL-Studio plugin. + +Download Links for supplementary software folder: + +- **Linux x64:** [http://vrl-studio.mihosoft.eu/releases/vrl-studio-ug4-release-2018/vrl-ug4-linux.zip](http://vrl-studio.mihosoft.eu/releases/vrl-studio-ug4-release-2018/vrl-ug4-linux.zip) +- **Windows x64:** [http://vrl-studio.mihosoft.eu/releases/vrl-studio-ug4-release-2018/vrl-ug4-windows.zip](http://vrl-studio.mihosoft.eu/releases/vrl-studio-ug4-release-2018/vrl-ug4-windows.zip) +- **macOS x64:** [http://vrl-studio.mihosoft.eu/releases/vrl-studio-ug4-release-2018/vrl-ug4-macos.zip](http://vrl-studio.mihosoft.eu/releases/vrl-studio-ug4-release-2018/vrl-ug4-macos.zip) + +### Opening the Sample Project: + +The supplementary software folder contains a version of VRL-Studio that includes a precompiled version of *UG4* and additional plugins for visualization. + +**Running VRL-Studio on Linux:** + +To run VRL-Studio from the supplementary software folder, execute the following commands: + +``` +cd path/to/VRL-Studio-For-UG4 +./run.sh +``` + +**Running VRL-Studio on macOS:** + +For macOS, VRL-Studio is provided as application bundle. Simply double-click the application bundle to run VRL-Studio. + +**Running VRL-Studio on Windows:** + +For Windows, VRL-Studio is provided as application bundle. Just run the VRL-Studio.exe file inside the VRL-Studio folder. + +If VRL-Studio runs for the first time, it will install several plugins (e.g. the *UG4* plugin) before showing the main user interface. + +To open the sample project, click on `File->Load Project` and navigate to the sample project (`skin-2d.vrlp`). + +Now do the following: + +1. Select the desired output file (for this example `vtk-output/skin-2d.vtu`). +2. Select the desired geometry (for this example `skin-2d.ugx`). +3. Press `Start` to run the simulation. +4. Check the log window for simulation progress (click on `View->Show Log in Window` or drag up the divider at the bottom of the VRL-Studio main window to reveal the log). +5. The `vtk-output/` folder contains the simulation output that can be post-processed with a VTK viewer such as ParaView. + +![vrl-skin2d](docs/ug4/img/readme/vrl_skin2d.png "UG4 as VRL-Studio Plugin") + +### Compiling UG4 for VRL-Studio: + +The following steps explain how to manually compile *UG4* for VRL-Studio. + +In addition to the aforementioned requirements, compilation for VRL-Studio requires Java (JDK >= 1.8). Additionally, VRL-Studio needs to run at least once to make sure the required folder structure is created. + +To build *UG4* as shared library for VRL-Studio, different CMake options have to be used. The following commands build *UG4* for VRL-Studio: + +``` +cd path/to/ug4 +mkdir build_vrl && cd build_vrl +cmake .. -DTARGET=vrl -DLAPACK=OFF -DBLAS=OFF -DDIM=all -DCPU="1;2" -DCOMPILE_INFO=OFF -DEMBEDDED_PLUGINS=ON -DSTATIC_BUILD=OFF +make -j +``` + +After successful compilation, the shared library libug4.so (libug4.dylib on macOS and ug4.dll on Windows) needs to be copied to the VRL plugin folder. For VRL-Studio installed from the supplementary software folder, the following command can be used to copy the shared library to the correct plugin folder: + +``` +cp path/to/libug4.so $HOME/.vrl/0.4.3/default-ug4/plugins/VRL-UG/natives/linux/x64/ +``` + +After restarting VRL-Studio, the new library is used. + +## Further Documentation: + +Installation instructions are given at https://github.com/UG4/ughub + +Further documentation on *UG4* is available at http://ug4.github.io/docs/ + +Please have a look at http://ug4.github.io/docs/page_external_libraries.html for more information on used libraries and their licenses. + +## Related Articles: +``` + Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. + A massively parallel geometric multigrid solver on hierarchically distributed grids. + Computing and visualization in science 16, 4 (2013), 151-164, + DOI: 10.1007/s00791-014-0231-x + + Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. + UG4 -- a novel flexible software system for simulating pde based models on high performance computers. + Computing and visualization in science 16, 4 (2013), 165-179, + DOI: 10.1007/s00791-014-0232-9 +``` +(Cf. the bib-file in the ugcore directory.) diff --git a/cmake/toolchain/juwels.cmake b/cmake/toolchain/juwels.cmake new file mode 100644 index 000000000..4da2d156d --- /dev/null +++ b/cmake/toolchain/juwels.cmake @@ -0,0 +1,62 @@ +################################################################################ +# created by Markus Breit +# markus.breit@gcsc.uni-frankfurt.de +# adapted from corresponding JuQueen file created by Ingo Heppner +# +# Toolchain file for JUWELS (type Dual Intel Xeon Skylake 8168) FZ Juelich. +# +# This toolchain file is included in a CMake run by (check with 'cmake --trace') +# 'Modules/CMakeDetermineSystem.cmake', and (later again) by the - generated - +# file '/CMakeFiles/CMakeSystem.cmake'. +# (Subdirectory 'Modules/' is part of your CMake installation, on JURECA's login +# nodes: '/usr/local/software/jureca/Stage3/software/Core/CMake/3.2.3/share/cmake-3.2/Modules'). +# +# Appropriate platform files for the "CMake system name" and compilers chosen +# here are called later (by 'Modules/CMakeSystemSpecificInformation.cmake'): +# +# I.e., for "BlueGeneQ-static" as "CMake system name" the platform file +# 'Modules/Platform/BlueGeneQ-static.cmake' is included first, which includes +# 'Modules/Platform/BlueGeneQ-base.cmake'. Afterwards CMake calls +# 'Modules/Platform/BlueGeneQ-static-GNU-C.cmake' and +# 'Modules/Platform/BlueGeneQ-static-GNU-CXX.cmake' +# if GNU compilers are chosen (by 'Modules/CMakeCInformation.cmake' and +# 'Modules/CMakeCXXInformation.cmake' respectively). +# +################################################################################ + +# Important: Setting the "cmake system name" will lead to automatic inclusion of +# the corresponding platform files: +set(CMAKE_SYSTEM_NAME Linux) +#list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/ugcore/cmake/modules") + +# This option tells cmake/ug_includes.cmake to add the -dynamic option to the compiler flags. +SET(enableDynamicOption OFF) + +# This option tells cmake/ug_includes.cmake to add the -fno-strict-aliasing option to the compiler flags. +SET(enableNoStrictAliasingOption ON) + +# Some variables for paths +set(GCC_ROOT "/gpfs/software/juwels/stages/2018a/software/icc/2018.2.199-GCC-5.5.0/compilers_and_libraries_2018.2.199/linux/") +set(MPI_ROOT "/gpfs/software/juwels/stages/2018a/software/impi/2018.2.199-iccifort-2018.2.199-GCC-5.5.0/") +set(FTR_ROOT "/gpfs/software/juwels/stages/2018a/software/ifort/2018.2.199-GCC-5.5.0/compilers_and_libraries_2018.2.199/linux/") + +# The serial GNU compilers +set(CMAKE_C_COMPILER ${GCC_ROOT}/bin/intel64/icc) +set(CMAKE_CXX_COMPILER ${GCC_ROOT}/bin/intel64/icpc) +set(CMAKE_Fortran_COMPILER ${FTR_ROOT}/bin/intel64/ifort) + +# The MPI wrappers for the GNU compilers +set(MPI_C_COMPILER ${MPI_ROOT}/bin64/mpicc) +set(MPI_CXX_COMPILER ${MPI_ROOT}/bin64/mpicxx) +set(MPI_Fortran_COMPILER ${MPI_ROOT}/bin64/mpif90) + +#message(STATUS "TMP INFO: Value of '\${CMAKE_C_COMPILER}' is: ${CMAKE_C_COMPILER}") # TMP +#message(STATUS "TMP INFO: Value of '\${CMAKE_CXX_COMPILER}' is: ${CMAKE_CXX_COMPILER}") # TMP +#message(STATUS "TMP INFO: Value of '\${CMAKE_Fortran_COMPILER}' is: ${CMAKE_Fortran_COMPILER}") # TMP + +message(STATUS "TMP INFO: Value of '\${MPI_C_COMPILER}' is: ${MPI_C_COMPILER}") # TMP +message(STATUS "TMP INFO: Value of '\${MPI_CXX_COMPILER}' is: ${MPI_CXX_COMPILER}") # TMP +message(STATUS "TMP INFO: Value of '\${MPI_Fortran_COMPILER}' is: ${MPI_Fortran_COMPILER}") # TMP + +# For debugging purposes +#include(CMakePrintSystemInformation) diff --git a/docs/ug4/additional_pages/development/tips_guides/install_eclipse.doxygen b/docs/ug4/additional_pages/development/tips_guides/install_eclipse.doxygen index 2a59a276b..2ebac58ee 100644 --- a/docs/ug4/additional_pages/development/tips_guides/install_eclipse.doxygen +++ b/docs/ug4/additional_pages/development/tips_guides/install_eclipse.doxygen @@ -60,10 +60,10 @@ The page is an install guide for the usage of Eclipse for ug.
\section secUG4AndEclipse Generating Eclipse Project-Files In order work with UG4 in Eclipse, please download and install UG4 as usual through -UG4's package-manager (see https://github.com/UG4/ughub/wiki). +UG4's package-manager (see https://github.com/UG4/ughub). You'll find detailed instructions on how to generate and import UG4 project-files -for Eclipse here: https://github.com/UG4/ughub/wiki#importing-ug4-into-your-ide +for Eclipse here: https://github.com/UG4/ughub#importing-ug4-into-your-ide
diff --git a/docs/ug4/additional_pages/introduction.doxygen b/docs/ug4/additional_pages/introduction.doxygen index d06dfb85d..eb1c09663 100644 --- a/docs/ug4/additional_pages/introduction.doxygen +++ b/docs/ug4/additional_pages/introduction.doxygen @@ -44,7 +44,12 @@ Scientific Computing at the University of Frankfurt, Germany (http://www.g-csc.de). -Features are: +https://github.com/UG4/ugcore gives a quick overview on how to obtain and to install +\ug4. Several example applications are described and tools for pre- and +postprocessing are linked. + + +Features of \ug4 are: - Fast and efficient numerical solvers for serial and parallel environments. - A flexible discretization module, allowing to freely couple equations, input @@ -96,9 +101,8 @@ Features are: - \ug4 is a cross-platform tool and supports \em Linux, \em MacOSX and Microsoft Windows. - - -Take a look at the current release for more information: -\ref pageUG4Releases - */ + +// Removed the 'releases page' from the Overview for a lack of meaningful releases: +//Take a look at the current release for more information: +//\ref pageUG4Releases diff --git a/docs/ug4/additional_pages/main_page.doxygen b/docs/ug4/additional_pages/main_page.doxygen index df05b4791..012ea1517 100644 --- a/docs/ug4/additional_pages/main_page.doxygen +++ b/docs/ug4/additional_pages/main_page.doxygen @@ -44,6 +44,11 @@ Scientific Computing at the University of Frankfurt, Germany (http://www.g-csc.de). +https://github.com/UG4/ugcore gives a quick overview on how to obtain and to install +\ug4. Several example applications are described and tools for pre- and +postprocessing are linked. + +
\section secWhatIsIt What is ug4? diff --git a/docs/ug4/additional_pages/setup/buildug.doxygen b/docs/ug4/additional_pages/setup/buildug.doxygen index 055caf1cb..f4fabcb17 100644 --- a/docs/ug4/additional_pages/setup/buildug.doxygen +++ b/docs/ug4/additional_pages/setup/buildug.doxygen @@ -40,7 +40,7 @@ for your system. Beginners might also want to have a look at \ref pageShellAndLi
\section secBuildUG4Checkout Checkout ug4 -Please follow the description at https://github.com/UG4/ughub/wiki to obtain a copy of UG4. +Please follow the description at https://github.com/UG4/ughub to obtain a copy of UG4.
\section secBuildUG4BashTools Bash Tools diff --git a/docs/ug4/additional_pages/setup/install/install.doxygen b/docs/ug4/additional_pages/setup/install/install.doxygen index 3e6c27ee8..0aa985b9c 100644 --- a/docs/ug4/additional_pages/setup/install/install.doxygen +++ b/docs/ug4/additional_pages/setup/install/install.doxygen @@ -35,7 +35,7 @@ \note Because of the great variety of systems, this installation guides may be outdated quickly. So if this guide is not working for you \em please open an issue on github.com/UG4/docs - Additionally to the pages below, please have a look at https://github.com/UG4/ughub/wiki. + Additionally to the pages below, please have a look at https://github.com/UG4/ughub. Installation and usage of UG4's package manager is detailed there. - \subpage pageAdditionalSoftware "OS-independent (old)" diff --git a/docs/ug4/additional_pages/setup/install/ug4_on_linux.doxygen b/docs/ug4/additional_pages/setup/install/ug4_on_linux.doxygen index 8f95b9081..3a48233b5 100644 --- a/docs/ug4/additional_pages/setup/install/ug4_on_linux.doxygen +++ b/docs/ug4/additional_pages/setup/install/ug4_on_linux.doxygen @@ -39,7 +39,7 @@ from your cluster management tool (e.g. module load).
\section secInstallUg Installation of UG4 -Please follow the description at https://github.com/UG4/ughub/wiki to obtain a +Please follow the description at https://github.com/UG4/ughub to obtain a copy of \ug4. Below you'll find hints on how to obtain additional software which is required to build \ug4. diff --git a/docs/ug4/additional_pages/setup/install/ug4_on_mac.doxygen b/docs/ug4/additional_pages/setup/install/ug4_on_mac.doxygen index d6f2fbb17..9bff40177 100644 --- a/docs/ug4/additional_pages/setup/install/ug4_on_mac.doxygen +++ b/docs/ug4/additional_pages/setup/install/ug4_on_mac.doxygen @@ -50,7 +50,7 @@ The \em terminal is located in Programs -> utilities (Dienstprogramme) -> Te
\section secInstallUg Installation of UG4 -Please follow the description at https://github.com/UG4/ughub/wiki to obtain a +Please follow the description at https://github.com/UG4/ughub to obtain a copy of \ug4. Below you'll find hints on how to obtain additional software which is required to build \ug4. diff --git a/docs/ug4/additional_pages/setup/install/ug4_on_windows.doxygen b/docs/ug4/additional_pages/setup/install/ug4_on_windows.doxygen index 5b05085f1..694faaaec 100644 --- a/docs/ug4/additional_pages/setup/install/ug4_on_windows.doxygen +++ b/docs/ug4/additional_pages/setup/install/ug4_on_windows.doxygen @@ -36,7 +36,7 @@
\section secInstallUg Installation of UG4 -Please follow the description at https://github.com/UG4/ughub/wiki to obtain a +Please follow the description at https://github.com/UG4/ughub to obtain a copy of \ug4. Below you'll find hints on how to obtain additional software which is required to build \ug4. diff --git a/docs/ug4/additional_pages/usage/usage.doxygen b/docs/ug4/additional_pages/usage/usage.doxygen index e0eaa5a9d..0477898d4 100644 --- a/docs/ug4/additional_pages/usage/usage.doxygen +++ b/docs/ug4/additional_pages/usage/usage.doxygen @@ -47,7 +47,7 @@ As well some tutorials targeting special features of \ug4 are available. - \ref pageugsubmit - \ref pageParallelRendering - \ug4 and \em VRL -- ug4-Scripting Reference Documentation +- ug4-Scripting Reference Documentation - \subpage pageLuaScript - \subpage pageBashTools - \subpage pageLUA2C diff --git a/docs/ug4/img/readme/Pan-Scheme.png b/docs/ug4/img/readme/Pan-Scheme.png new file mode 100644 index 000000000..292313ef4 Binary files /dev/null and b/docs/ug4/img/readme/Pan-Scheme.png differ diff --git a/docs/ug4/img/readme/PanSolution3d-coil_and_pan_v3-lev4.png b/docs/ug4/img/readme/PanSolution3d-coil_and_pan_v3-lev4.png new file mode 100644 index 000000000..4121f7572 Binary files /dev/null and b/docs/ug4/img/readme/PanSolution3d-coil_and_pan_v3-lev4.png differ diff --git a/docs/ug4/img/readme/elder_adapt.png b/docs/ug4/img/readme/elder_adapt.png new file mode 100644 index 000000000..26251299c Binary files /dev/null and b/docs/ug4/img/readme/elder_adapt.png differ diff --git a/docs/ug4/img/readme/navier_stokes.png b/docs/ug4/img/readme/navier_stokes.png new file mode 100644 index 000000000..8d122de2e Binary files /dev/null and b/docs/ug4/img/readme/navier_stokes.png differ diff --git a/docs/ug4/img/readme/poisson.png b/docs/ug4/img/readme/poisson.png new file mode 100644 index 000000000..0e938de65 Binary files /dev/null and b/docs/ug4/img/readme/poisson.png differ diff --git a/docs/ug4/img/readme/promesh.png b/docs/ug4/img/readme/promesh.png new file mode 100644 index 000000000..e7d572c49 Binary files /dev/null and b/docs/ug4/img/readme/promesh.png differ diff --git a/docs/ug4/img/readme/springboard.png b/docs/ug4/img/readme/springboard.png new file mode 100644 index 000000000..bac72e3b6 Binary files /dev/null and b/docs/ug4/img/readme/springboard.png differ diff --git a/docs/ug4/img/readme/vrl_skin2d.png b/docs/ug4/img/readme/vrl_skin2d.png new file mode 100644 index 000000000..5863b6d33 Binary files /dev/null and b/docs/ug4/img/readme/vrl_skin2d.png differ diff --git a/scripts/shell/clusterdetect b/scripts/shell/clusterdetect index 562046e52..d44483a0f 100755 --- a/scripts/shell/clusterdetect +++ b/scripts/shell/clusterdetect @@ -5,29 +5,31 @@ if [ -z "$UGSUBMIT_TYPE" ]; then - if [ $HOSTNAME == "cekon.gcsc.uni-frankfurt.de" ]; then + if [ "$HOSTNAME" = "cekon.gcsc.uni-frankfurt.de" ]; then UGSUBMIT_TYPE=cekon - elif [ $HOSTNAME == "cesari.gcsc.uni-frankfurt.de" ]; then + elif [ "$HOSTNAME" = "cesari.gcsc.uni-frankfurt.de" ]; then UGSUBMIT_TYPE=cesari - elif [[ "$HOSTNAME" == "juqueen"* ]]; then + elif [[ "$HOSTNAME" = "juqueen"* ]]; then UGSUBMIT_TYPE=Juqueen - elif [[ "$HOSTNAME" == "jrl"* ]]; then + elif [[ "$HOSTNAME" = "jrl"* ]]; then UGSUBMIT_TYPE=Jureca - elif [[ "$HOSTNAME" == "dora"* ]]; then + elif [[ "$HOSTNAME" = "juwels"* ]]; then + UGSUBMIT_TYPE=Juwels + elif [[ "$HOSTNAME" = "dora"* ]]; then UGSUBMIT_TYPE=DoraCSCS - elif [[ "$HOSTNAME" == "eslogin"* ]]; then - if [ "$SITE_PLATFORM_NAME" == "hornet" ]; then + elif [[ "$HOSTNAME" = "eslogin"* ]]; then + if [ "$SITE_PLATFORM_NAME" = "hornet" ]; then UGSUBMIT_TYPE=Hornet - elif [ "$SITE_PLATFORM_NAME" == "hazelhen" ]; then + elif [ "$SITE_PLATFORM_NAME" = "hazelhen" ]; then UGSUBMIT_TYPE=Hazelhen - elif [ "$SITE_PLATFORM_NAME" == "hermit" ]; then + elif [ "$SITE_PLATFORM_NAME" = "hermit" ]; then UGSUBMIT_TYPE=Hermit else echo "WARNING: couldn't detect cluster type!" fi else unamestr=`uname` - if [[ "$unamestr" == 'Darwin' ]]; then + if [[ "$unamestr" = 'Darwin' ]]; then UGSUBMIT_TYPE=mpi export UGSUBMIT_MORE=Apple else diff --git a/scripts/shell/clusters b/scripts/shell/clusters index 11cd9fbd3..29d10a4d2 100755 --- a/scripts/shell/clusters +++ b/scripts/shell/clusters @@ -29,6 +29,9 @@ elif [ $UGSUBMIT_TYPE == "Juqueen" ]; then elif [ $UGSUBMIT_TYPE == "Jureca" ]; then source $scriptpath/schedulers/jureca +elif [ $UGSUBMIT_TYPE == "Juwels" ]; then + source $scriptpath/schedulers/juwels + elif [ $UGSUBMIT_TYPE == "mpi" ] || [ $UGSUBMIT_TYPE == "mpi-foreground" ]; then source $scriptpath/schedulers/mpi diff --git a/scripts/shell/install_scripts/SuperLU/make.inc_jureca b/scripts/shell/install_scripts/SuperLU/make.inc_juelich similarity index 60% rename from scripts/shell/install_scripts/SuperLU/make.inc_jureca rename to scripts/shell/install_scripts/SuperLU/make.inc_juelich index d11916b69..cbfd9d87c 100644 --- a/scripts/shell/install_scripts/SuperLU/make.inc_jureca +++ b/scripts/shell/install_scripts/SuperLU/make.inc_juelich @@ -21,16 +21,25 @@ # PLAT = +stagePath= +system=$(shell cat /etc/FZJ/systemname) +ifeq ($(system),jureca) + stagePath=/usr/local/software/jureca/Stages/2018a +endif +ifeq ($(system),juwels) + stagePath=/gpfs/software/juwels/stages/2018a +endif + # # The name of the libraries to be created/linked to # SuperLUroot = $(UG4_LOCAL_INSTALL_DIR)/SuperLU/4.3 SUPERLULIB = $(SuperLUroot)/lib/libsuperlu_4.3.a -BLASLIB = /usr/local/software/jureca/Stages/2018a/software/imkl/2018.2.199-ipsmpi-2018a/mkl/lib/intel64/libmkl_intel_lp64.so \ - /usr/local/software/jureca/Stages/2018a/software/imkl/2018.2.199-ipsmpi-2018a/mkl/lib/intel64/libmkl_intel_thread.so \ - /usr/local/software/jureca/Stages/2018a/software/imkl/2018.2.199-ipsmpi-2018a/mkl/lib/intel64/libmkl_core.so \ - /usr/local/software/jureca/Stages/2018a/software/imkl/2018.2.199-ipsmpi-2018a/mkl/lib/intel64/libmkl_intel_thread.so \ - /usr/local/software/jureca/Stages/2018a/software/imkl/2018.2.199-ipsmpi-2018a/lib/intel64/libiomp5.so \ +BLASLIB = $(stagePath)/software/imkl/2018.2.199-ipsmpi-2018a/mkl/lib/intel64/libmkl_intel_lp64.so \ + $(stagePath)/software/imkl/2018.2.199-ipsmpi-2018a/mkl/lib/intel64/libmkl_intel_thread.so \ + $(stagePath)/software/imkl/2018.2.199-ipsmpi-2018a/mkl/lib/intel64/libmkl_core.so \ + $(stagePath)/software/imkl/2018.2.199-ipsmpi-2018a/mkl/lib/intel64/libmkl_intel_thread.so \ + $(stagePath)/software/imkl/2018.2.199-ipsmpi-2018a/lib/intel64/libiomp5.so \ /usr/lib64/libpthread.so TMGLIB = libtmglib.a LIBS = $(SUPERLULIB) $(BLASLIB) @@ -43,10 +52,10 @@ ARCH = ar ARCHFLAGS = cr RANLIB = ranlib -CC = /usr/local/software/jureca/Stages/2018a/software/psmpi/5.2.1-1-iccifort-2018.2.199-GCC-5.5.0/bin/mpicc +CC = $(stagePath)/software/impi/2018.2.199-iccifort-2018.2.199-GCC-5.5.0/bin64/mpicc CFLAGS = -O3 -fPIC NOOPTS = -fPIC -FORTRAN = /usr/local/software/jureca/Stages/2018a/software/psmpi/5.2.1-1-iccifort-2018.2.199-GCC-5.5.0/bin/mpif90 +FORTRAN = $(stagePath)/software/impi/2018.2.199-iccifort-2018.2.199-GCC-5.5.0/bin64/mpif90 FFLAGS = -O3 -cpu:g5 -YEXT_NAMES=LCS -s -YEXT_SFX=_ LOADER = $(CC) LOADOPTS = diff --git a/scripts/shell/schedulers/juwels b/scripts/shell/schedulers/juwels new file mode 100644 index 000000000..bb726529c --- /dev/null +++ b/scripts/shell/schedulers/juwels @@ -0,0 +1,197 @@ +#!/bin/bash +###################################################################### +# JUWELS +#------------------------------------------------------------------- +# using SLURM +###################################################################### + +function UJS_Submit +{ + echo "Cluster: JUWELS. Scheduler: SLURM." + + # check consistency + if [ $((npe%nppn)) -ne 0 ]; then + echo "npe=$npe is not divisible by nppn=$nppn" + exit + fi + + if [ $nnodes -gt 256 ]; then + echo "Juwels does not provide more than 256 nodes for one job atm." + return + elif [ $nnodes -gt 64 ] && [ "$juwels_part" = "mem192" ]; then + echo "Juwels does not provide more than 64 nodes on the mem192 partition for one job atm." + return + fi + + ## interactive (devel) jobs (not tested) + # Once an allocation has been made, the salloc command will start a bash + # on the login node where the submission was done. After a successful + # allocation the users can execute srun from that shell and they can + # spawn interactively their applications. The interactive session is + # terminated by exiting the shell. + # In order to obtain a shell on the first allocated compute nodes + # (like command “msub -I“ from Moab), the users can start a remote shell + # from within the current session and connect it to a pseudo terminal + # (pty) using the srun command with a shell as an argument. For example: + # srun --cpu_bind=none --nodes=2 --pty /bin/bash + # After gaining access to the remote shell it is possible to run srun + # again from that remote shell in order to execute interactively + # applications without any delays (no scheduling delays since the + # allocation has already been granted). + # For more details, cf.: + # "JUWELS - User's Manual for the Batch System - Slurm" + if [ $interactive == true ]; then + + if [ $walltime == "unlimited" ]; then + walltime=00:30:00 + fi + + # todo: check walltime is <= 2h + + if [ $nnodes -gt 8 ]; then + echo "ERROR: The maximum number of nodes for interactive jobs is 8." + return + fi + + commandline="salloc -N $nnodes -n $npe --partition=devel --job-name=$jobname --time=$walltime" + echo " command: "$commandline >> info.txt + + echo "[[ current cluster allocation" + squeue + echo "]] current cluster allocation" + + if [ $test == true ]; then + echo "ONLY testing - NOT executing." + echo "Submit/Start: $commandline" + return + fi + + echo "Start: $commandline" + + $commandline | tee $outdir/job.output + return=$? + if [ ! $return == 0 ]; then + echo "ERROR: srun returned $return. Job has NOT been started." + exit + fi + + ## BATCH jobs + else + + # walltime handling + if [ $walltime == "unlimited" ]; then + walltime=00:30:00 + fi + + echo "Create: $outdir/job.sh" + + #MYLDLPATH=/bgsys/drivers/ppcfloor/comm/lib/ + + # mail notification handling + juwelsNotification="NONE" + if [ $mail = true ]; then + if [ -z "$UGSUBMIT_EMAIL" ]; then + echo "please set UGSUBMIT_EMAIL or specify email with -email. Aborting." + exit + fi + + if [ $mailStart = true ]; then + juwelsNotification="BEGIN" + fi + if [ $mailEnd = true ]; then + if [ $juwelsNotification == "NONE" ]; then + juwelsNotification="END" + else + juwelsNotification="ALL" + fi + fi + if [ $mailError = true ]; then + if [ $juwelsNotification == "NONE" ]; then + juwelsNotification="FAIL" + else + juwelsNotification="ALL" + fi + fi + fi + + # partition + if [ -z "$juwels_part" ]; then + juwels_part="batch" + elif [ ! "$juwels_part" = "batch" ] && [ ! "$juwels_part" = "mem192" ]; then + echo "Juwels-partition parameter not set correctly. Valid values are: batch, mem192." + return + fi + + + # write job script + cat > job.sh << EOF +#!/bin/bash +#SBATCH --job-name=$jobname +#SBATCH --nodes=$nnodes +#SBATCH --ntasks=$npe +#SBATCH --time=$walltime +#SBATCH --partition=$juwels_part +#SBATCH --error=job.error +#SBATCH --output=job.output +#SBATCH --mail-type=$juwelsNotification +#SBATCH --mail-user=$UGSUBMIT_EMAIL + +$profilePrefix +srun -N $nnodes -n $npe -p $juwels_part $executable $args +EOF + + # execute command (or only print it in test case) + commandline="sbatch -N $nnodes -n $npe job.sh" + echo " command: "$commandline >> info.txt + + if [ $test == true ]; then + echo "ONLY testing - NOT executing." + echo "Submit/Start: $commandline" + return + fi + + echo "Submit: $commandline" + commlineoutput=$($commandline) + echo "$commlineoutput" + jobid=$(echo $commlineoutput | sed 's/.*[^0-9]\([0-9]\+\)[^0-9]*$/\1/') + fi +} + + + +function UJS_GetOptions +{ + nppnmax=48 + pemax=109008 +} + +function UJS_Info +{ + echo "UGSUBMIT Info for JUWELS:" + if [ ! -z $1 ] && [ $1 == "all" ]; then + #echo "squeue -o \"%.7i %6C %6D %.20j %.10u %.8T %.10M %.10l %.6D %19R\"" + squeue -o "%.7i %6C %6D %.20j %.10u %.8T %.10M %.10l %.6D %19R" + else + #echo "squeue -u $USER -o \"%.7i %6C %6D %.20j %.10u %.8T %.10M %.10l %.6D %19R\"" + squeue -u $USER -o "%.7i %6C %6D %.20j %.10u %.8T %.10M %.10l %.6D %19R" + fi +} + +function UJS_Cancel +{ + echo "Using SLURM on JUWELS" + if [ ! -z $1 ] && [ $1 == "all" ]; then + echo "your jobs:" + squeue -u $USER -o "%.7i %6C %6D %.50j %.10u %.8T %.10M %.10l %.6D %19R" + echo " " + read -p "Are you sure you want to cancel all your jobs (yes=j,J,y or Y) ? " -n 1 -r + echo " " + if [[ $REPLY =~ ^[JjYy]$ ]] + then + scancel --user=$USER + fi + + else + scancel $1 + fi +} \ No newline at end of file diff --git a/scripts/shell/ugconfig b/scripts/shell/ugconfig index ead9e1c02..243daa0ad 100755 --- a/scripts/shell/ugconfig +++ b/scripts/shell/ugconfig @@ -10,7 +10,8 @@ # e.g. loading of lapack/blas/compiler modules. ###################################################################### -scriptpath="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +#scriptpath="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +scriptpath=${UG4_ROOT}/ugcore/scripts/shell if [ -z "$UGSUBMIT_TYPE" ]; then source $scriptpath/clusterdetect @@ -21,7 +22,7 @@ fi -if [ $UGSUBMIT_TYPE == "Hermit" ] || [ $UGSUBMIT_TYPE == "Hazelhen" ] || [ $UGSUBMIT_TYPE == "Hornet" ]; then +if [ "$UGSUBMIT_TYPE" = "Hermit" ] || [ "$UGSUBMIT_TYPE" = "Hazelhen" ] || [ "$UGSUBMIT_TYPE" = "Hornet" ]; then echo "ugconfig: On Hermit/Hornet/Hazelhen, loading gnu compilers" module list 2>&1 | grep PrgEnv-gnu > /dev/null if [ $? == 1 ]; then @@ -31,32 +32,38 @@ if [ $UGSUBMIT_TYPE == "Hermit" ] || [ $UGSUBMIT_TYPE == "Hazelhen" ] || [ $UGSU echo PrgEnv-gnu already loaded. fi -elif [ $UGSUBMIT_TYPE == "cekon" ]; then +elif [ "$UGSUBMIT_TYPE" = "cekon" ]; then echo "ugconfig: Nothing to configure on cekon. Have a nice day." -elif [ $UGSUBMIT_TYPE == "cesari" ]; then +elif [ "$UGSUBMIT_TYPE" = "cesari" ]; then echo "ugconfig: Nothing to configure on cesari. Have a nice day." -elif [ $UGSUBMIT_TYPE == "mpi" ] && [ $UGSUBMIT_MORE == "Apple" ]; then +elif [ "$UGSUBMIT_TYPE" = "mpi" ] && [ "$UGSUBMIT_MORE" = "Apple" ]; then echo "ugconfig: Nothing to configure on Apple systems. Have a nice day." -elif [ $UGSUBMIT_TYPE == "mpi" ]; then +elif [ "$UGSUBMIT_TYPE" = "mpi" ]; then echo "ugconfig: Nothing to configure on $UGSUBMIT_TYPE. Have a nice day." -elif [[ $UGSUBMIT_TYPE == "Juqueen" ]]; then - echo "ugconfig: On Juqueen, loading cmake and lapack." +elif [ "$UGSUBMIT_TYPE" = "Juqueen" ]; then + echo "ugconfig: Loading cmake and lapack." module load cmake module load lapack -elif [[ $UGSUBMIT_TYPE == "Jureca" ]]; then - echo "ugconfig: On Jureca, loading toolchain with compilers and lapack as well as cmake." - module load intel-para - module load CMake/3.2.3 - echo "ugconfig: export library paths for LAPACk and BLAS" - export LAPACK_LIB_DIR=/usr/local/software/jureca/Stage3/software/Toolchain/ipsmpi/2015.07/imkl/11.2.3.187/mkl/lib/int$ - export BLAS_LIB_DIR=/usr/local/software/jureca/Stage3/software/Toolchain/ipsmpi/2015.07/imkl/11.2.3.187/mkl/lib/intel$ +elif [ "$UGSUBMIT_TYPE" = "Jureca" ]; then + echo "ugconfig: Loading toolchain with compilers and lapack as well as cmake." + module load Intel # compilers + module load ParaStationMPI # MPI wrappers + module load imkl # BLAS / LAPACK + module load CMake -elif [ "$UGSUBMIT_TYPE" == "unknown" ]; then +elif [ "$UGSUBMIT_TYPE" = "Juwels" ]; then + echo "ugconfig: Loading toolchain with compilers and BLAS / LAPACK as well as CMake." + module load Intel # compilers + module load IntelMPI/2018.2.199 # MPI wrappers + module load imkl # BLAS / LAPACK + module load CMake + +elif [ "$UGSUBMIT_TYPE" = "unknown" ]; then echo "Hello, this is ugconfig trying to config your cluster" echo "The UGSUBMIT_TYPE could not be set automatically." echo "please set UGSUBMIT_TYPE manually." diff --git a/scripts/shell/uginstall b/scripts/shell/uginstall index 2c430373e..714dd04d4 100755 --- a/scripts/shell/uginstall +++ b/scripts/shell/uginstall @@ -565,9 +565,9 @@ make_SuperLU() echo "UGINSTALL: On Juqueen, using make.inc_juqueen_xl" cp $scriptpath/install_scripts/SuperLU/make.inc_juqueen_xl make.inc make blaslib - elif [[ "$HOSTNAME" == "jrl"* ]]; then - echo "UGINSTALL: On Jureca, using make.inc_jureca" - cp $scriptpath/install_scripts/SuperLU/make.inc_jureca make.inc + elif [[ "$HOSTNAME" == "jrl"* || "$HOSTNAME" == "juwels"* ]]; then + echo "UGINSTALL: On Juelich supercomputer, using make.inc_juelich" + cp $scriptpath/install_scripts/SuperLU/make.inc_juelich make.inc else echo "UGINSTALL: Not yet supported cluster, using internal BLAS" cp $scriptpath/install_scripts/SuperLU/make.inc_default make.inc diff --git a/scripts/shell/ugsubmit b/scripts/shell/ugsubmit index d65f0597f..af210f811 100755 --- a/scripts/shell/ugsubmit +++ b/scripts/shell/ugsubmit @@ -14,6 +14,7 @@ # - Jugene, Forschungszentrum Juelich (ll_submit) # - Juqueen, Forschungszentrum Juelich (ll_submit) # - Jureca, Forschungszentrum Juelich (SLURM) +# - Juwelsjureca, Forschungszentrum Juelich (SLURM) # - Hermit, HLRS Stuttgart (qsub, aprun) # - NecNehalem, HLRS Stuttgart (qsub, mpirun) # - MOAB Workload Manager (msub, srun) @@ -115,7 +116,7 @@ function usage echo " -queue : the queue to use" echo " -nosubdir : don't create job in a subdir, but in dir (default .) directly. (warning: might be dangerous)" echo " " - echo " Supported Cluster Types: NecNehalem, Hermit, Jugene, Juqueen, Jureca, mpi (mpi-foreground, mpi-background), cekon, cesari, moab." + echo " Supported Cluster Types: NecNehalem, Hermit, Jugene, Juqueen, Jureca, Juwels, mpi (mpi-foreground, mpi-background), cekon, cesari, moab." if [ -n "$UGSUBMIT_TYPE" ]; then echo " Only UGSUBMIT_TYPE=$UGSUBMIT_TYPE specific parameters are shown ! Set UGSUBMIT_TYPE=\"\" to see all." fi @@ -147,6 +148,14 @@ function usage echo " -Jureca-memory : Amount of memory to use (default is 128): 128, 256, 512." echo "" fi + if [ -z "$UGSUBMIT_TYPE" ] || [ $UGSUBMIT_TYPE == "Juwels" ]; then + echo "" + echo " Juwels Parameters" + echo " -Juwels-partition : Partition to use; default is batch:" + echo " batch (max. 24h walltime, 256 nodes, 96G memory/node)" + echo " mem192 (192G memory/node)" + echo "" + fi if [ -z "$UGSUBMIT_TYPE" ] || [ $UGSUBMIT_TYPE == "DoraCSCS" ]; then echo "" echo " Dora Parameters" @@ -312,6 +321,9 @@ do elif [ $1 == "-Jureca-memory" ]; then jureca_mem=$2 shift 2 + elif [ $1 == "-Juwels-partition" ]; then + juwels_part=$2 + shift 2 elif [ $1 == "---" ]; then shift break @@ -518,7 +530,7 @@ if [ $interactive == false ]; then sleep 1; tail -f job.output -c +0 fi - if [ $jobid != "?" ] && [ $nosubdir == false ]; then + if [ ! "$jobid" = "?" ] && [ $nosubdir = false ]; then cd $execpath ln -s $outdir jobid.$jobid fi diff --git a/scripts/util/gnuplot.lua b/scripts/util/gnuplot.lua index 6b3ef2469..919f2bb43 100644 --- a/scripts/util/gnuplot.lua +++ b/scripts/util/gnuplot.lua @@ -505,7 +505,7 @@ function gnuplot.plot(filename, data, options) -- check valid term if not table.contains(supportedTerms, terminal) then - write("Gnuplot Error: unsupprted terminal: '"..terminal.."'\n") + write("Gnuplot Error: unsupported terminal: '"..terminal.."'\n") write("Gnuplot Error: supported are: "..table.concat(supportedTerms, ", ").."\n") return 2 end diff --git a/scripts/util/load_balancing_util.lua b/scripts/util/load_balancing_util.lua index 7ba061ea2..d80b666f5 100644 --- a/scripts/util/load_balancing_util.lua +++ b/scripts/util/load_balancing_util.lua @@ -181,8 +181,6 @@ function balancer.CreateLoadBalancer(domain) if balancer.communicationWeights ~= nil then partitioner:set_communication_weights(balancer.communicationWeights) end - partitioner:set_child_weight(balancer.childWeight) - partitioner:set_sibling_weight(balancer.siblingWeight) partitioner:set_itr_factor(balancer.itrFactor) partitioner:set_allowed_imbalance_factor(balancer.imbalanceFactor) partitioner:set_verbose(false) diff --git a/scripts/util/load_balancing_util_2.lua b/scripts/util/load_balancing_util_2.lua index f3ba72e08..46ea6569a 100644 --- a/scripts/util/load_balancing_util_2.lua +++ b/scripts/util/load_balancing_util_2.lua @@ -101,8 +101,6 @@ util.balancer.defaults = balanceWeights = nil, communicationWeights = nil, options = nil, - childWeight = 2, - siblingWeight = 2, itrFactor = 1000, verbose = false, clusteredSiblings = true, @@ -290,8 +288,6 @@ function util.balancer.CreatePartitioner(dom, partitionerDesc) RequiredPlugins({"Parmetis"}) partitioner = Partitioner_Parmetis(dom) - partitioner:set_child_weight(desc.childWeight or defaults.childWeight) - partitioner:set_sibling_weight(desc.siblingWeight or defaults.siblingWeight) partitioner:set_itr_factor(desc.itrFactor or defaults.itrFactor) partitioner:set_verbose(verbose) diff --git a/scripts/util/solver_util.lua b/scripts/util/solver_util.lua index 6546f2155..12cf42e5b 100644 --- a/scripts/util/solver_util.lua +++ b/scripts/util/solver_util.lua @@ -821,10 +821,6 @@ function util.solver.CreatePreconditioner(precondDesc, solverutil) end gmg:set_transfer(transfer) - if (desc.debug or defaults.debug) then - gmg:set_debug(GridFunctionDebugWriter(approxSpace)) - end - local discretization = desc.discretization or util.solver.defaults.discretization if discretization then gmg:set_discretization(discretization) @@ -854,42 +850,26 @@ function util.solver.CreatePreconditioner(precondDesc, solverutil) util.solver.CondAbort(precond == nil, "Invalid preconditioner specified: " .. name) - - - -- check: - local rprecond = precond - if desc and desc.debugSolver then - print(solver) - local dsolver = - util.solver.CreateLinearSolver( - desc.debugSolver, solverutil) - - local err = GridFunction(approxSpace) - rprecond = DebugIterator(precond, dsolver) - rprecond:set_solution(err) - else - - - - end - - -- create debug writer (optional) - if desc and ((desc.debug == true) or (desc.debugSolver and desc.debugSolver.debug==true)) then - if approxSpace == nil then - print("An ApproximationSpace is required to create a DebugWriter for the '" .. name .. "'' preconditioner.") - print("Consider setting the 'approxSpace' property of your preconditioner,") - print("or alternatively the util.solver.defaults.approxSpace property or") - print("alternatively set the option 'debug=false' in your preconditioner.") - exit() - end - - local dbgWriter = GridFunctionDebugWriter(approxSpace) - dbgWriter:set_vtk_output(true) - rprecond:set_debug(dbgWriter) - end - - if desc then desc.instance = rprecond end - + -- check: + local rprecond = precond + if desc and desc.debugSolver then + print(solver) + local dsolver = + util.solver.CreateLinearSolver( + desc.debugSolver, solverutil) + + local err = GridFunction(approxSpace) + rprecond = DebugIterator(precond, dsolver) + rprecond:set_solution(err) + else + + end + + -- create debug writer (optional) + util.solver.SetDebugWriter(rprecond, desc, defaults) + + if desc then desc.instance = rprecond end + return rprecond end @@ -1012,6 +992,9 @@ end function util.solver.SetDebugWriter(obj, desc, defaults) local dbgDesc = desc.debug + if dbgDesc == nil then + dbgDesc = desc.debugSolver + end if dbgDesc == nil then if defaults then dbgDesc = defaults.debug end end @@ -1024,7 +1007,11 @@ function util.solver.SetDebugWriter(obj, desc, defaults) if type (dbgDesc) == "boolean" then debug = dbgDesc elseif type (dbgDesc) == "table" then - debug = true + if dbgDesc.debug ~= nil then + debug = dbgDesc.debug + else + debug = true + end if dbgDesc.vtk ~= nil then vtk = dbgDesc.vtk end if dbgDesc.conn_viewer ~= nil then conn_viewer = dbgDesc.conn_viewer end else @@ -1033,25 +1020,28 @@ function util.solver.SetDebugWriter(obj, desc, defaults) end if debug then - - local approxSpace = nil - if desc then - approxSpace = desc.approxSpace or util.solver.defaults.approxSpace - end - - if approxSpace == nil then - print("An ApproximationSpace is required to create a DebugWriter.") - print("Consider setting the 'approxSpace' property of of the object to debug,") - print("or alternatively the util.solver.defaults.approxSpace property.") - print("Otherwiese set the option 'debug=false' for the object.") - exit() - end - - local dbgWriter = GridFunctionDebugWriter(approxSpace) - dbgWriter:set_vtk_output(vtk) - dbgWriter:set_conn_viewer_output(conn_viewer) - obj:set_debug(dbgWriter) + if (util.debug_writer ~= nil) then + obj:set_debug(util.debug_writer) + else + local approxSpace = nil + if desc then + approxSpace = desc.approxSpace or util.solver.defaults.approxSpace + end + + if approxSpace == nil then + print("An ApproximationSpace is required to create the standard DebugWriter.") + print("Consider setting the 'approxSpace' property of of the object to debug,") + print("or alternatively the util.solver.defaults.approxSpace property.") + print("Otherwise set util.debug_writer to your own debug writer object.") + exit() + end + + local dbgWriter = GridFunctionDebugWriter(approxSpace) + dbgWriter:set_vtk_output(vtk) + dbgWriter:set_conn_viewer_output(conn_viewer) + obj:set_debug(dbgWriter) + end end end end diff --git a/scripts/util/time_step_util.lua b/scripts/util/time_step_util.lua index 1375ec2c6..91c3a1eeb 100644 --- a/scripts/util/time_step_util.lua +++ b/scripts/util/time_step_util.lua @@ -305,7 +305,9 @@ function util.SolveNonlinearTimeProblem( while (endTime == nil or ((time < endTime) and ((endTime-time)/maxStepSize > relPrecisionBound))) and ((endTSNo == nil) or (step < endTSNo)) do step = step+1 print("++++++ TIMESTEP " .. step .. " BEGIN (current time: " .. time .. ") ++++++"); - + + local solver_call = 0; + -- initial t-step size local currdt = maxStepSize -- adjust in case of over-estimation @@ -363,6 +365,11 @@ function util.SolveNonlinearTimeProblem( -- setup time Disc for old solutions and timestep size timeDisc:prepare_step(solTimeSeries, currdt) + -- enter debug section (if the debug_writer is specified) + if util.debug_writer ~= nil then + util.debug_writer:enter_section ("TIMESTEP-"..step.."-SolverCall-"..solver_call) + end + -- prepare newton solver if newtonSolver:prepare(u) == false then print ("\n++++++ Newton solver failed."); exit(); @@ -371,6 +378,11 @@ function util.SolveNonlinearTimeProblem( -- apply newton solver newtonSuccess = newtonSolver:apply(u) + -- exit debug section (if the debug_writer is specified) + if util.debug_writer ~= nil then + util.debug_writer:leave_section () + end + -- start over again if failed if newtonSuccess == false then break end diff --git a/ug4.bib b/ug4.bib new file mode 100644 index 000000000..c33c73eee --- /dev/null +++ b/ug4.bib @@ -0,0 +1,32 @@ +# This file is part of UG4. +# +# If you use UG4 to produce results of your publications, you are obliged to +# cite ug4_ref1_2013 and ug4_ref2_2013. + +@article +{ ug4_ref1_2013, + author = {Reiter, Sebastian and Vogel, Andreas and Heppner, Ingo and Rupp, Martin and Wittum, Gabriel}, + title = {A massively parallel geometric multigrid solver on hierarchically distributed grids}, + journal = {Computing and Visualization in Science}, + year = 2013, + month = {August}, + volume = 16, + number = 4, + pages = {151--164}, + doi = {10.1007/s00791-014-0231-x} +} + +@article +{ ug4_ref2_2013, + author = {Vogel, Andreas and Reiter, Sebastian and Rupp, Martin and N{\"a}gel, Arne and Wittum, Gabriel}, + title = {UG 4: A novel flexible software system for simulating PDE based models on high performance computers}, + journal = {Computing and Visualization in Science}, + year = 2013, + month = {August}, + volume = 16, + number = 4, + pages = {165--179}, + doi = {10.1007/s00791-014-0232-9} +} + +# End of File diff --git a/ugbase/bindings/lua/info_commands.cpp b/ugbase/bindings/lua/info_commands.cpp index 71f005b9c..e87cf8ae7 100644 --- a/ugbase/bindings/lua/info_commands.cpp +++ b/ugbase/bindings/lua/info_commands.cpp @@ -1209,7 +1209,7 @@ bool PluginRequired(const char *name) { if(PluginLoaded(name) == false) { - string msg = string("plugin ") + name + string(" not loaded. Please use 'cmake -D") + name + string("=ON ..' in your build directory."); + string msg = string("plugin ") + name + string(" not loaded. Please use 'cmake -D") + name + string("=ON .' in your build directory."); std::string file; size_t line; if(GetLuaFileAndLine(script::GetDefaultLuaState(), file, line)) throw UGError(msg.c_str(), file.c_str(), line); diff --git a/ugbase/bindings/vrl/messaging.h b/ugbase/bindings/vrl/messaging.h index 919ac9dbe..8a1025506 100644 --- a/ugbase/bindings/vrl/messaging.h +++ b/ugbase/bindings/vrl/messaging.h @@ -58,18 +58,6 @@ namespace ug { namespace vrl { -/** - * DO NOT USE THIS METHOD! - * (totally broken) - * @param msg - */ -void soutPrintln(std::string msg); -/** - * DO NOT USE THIS METHOD! - * (totally broken) - * @param msg - */ -void serrPrintln(std::string msg); /** * Replaces each substring of target string that is equal to diff --git a/ugbase/bridge/CMakeLists.txt b/ugbase/bridge/CMakeLists.txt index 44354e91e..83ff4d8a0 100644 --- a/ugbase/bridge/CMakeLists.txt +++ b/ugbase/bridge/CMakeLists.txt @@ -83,7 +83,7 @@ if(buildAlgebra) algebra_bridges/solver_bridge.cpp algebra_bridges/eigensolver_bridge.cpp algebra_bridges/domain_dependent_preconditioner_bridge.cpp - algebra_bridges/constrained_linear_iterator_bridge.cpp + #algebra_bridges/constrained_linear_iterator_bridge.cpp algebra_bridges/pilut_bridge.cpp algebra_bridges/obstacle_bridge.cpp algebra_bridges/restart_bridge.cpp diff --git a/ugbase/bridge/algebra_bridges/solver_bridge.cpp b/ugbase/bridge/algebra_bridges/solver_bridge.cpp index 698560e4b..1e8269723 100644 --- a/ugbase/bridge/algebra_bridges/solver_bridge.cpp +++ b/ugbase/bridge/algebra_bridges/solver_bridge.cpp @@ -52,6 +52,7 @@ #include "lib_algebra/operator/linear_solver/lu.h" #include "lib_algebra/operator/linear_solver/agglomerating_solver.h" #include "lib_algebra/operator/linear_solver/debug_iterator.h" +#include "lib_algebra/operator/linear_solver/external_solvers/external_solvers.h" #ifdef UG_PARALLEL #include "lib_algebra/operator/linear_solver/feti.h" #endif @@ -318,6 +319,16 @@ static void Algebra(Registry& reg, string grp) } #endif + // ExternalSolver + { + typedef IExternalSolver T; + string name = string("ExternalSolver").append(suffix); + reg.add_class_(name, grp) + .add_method("set_disable_preprocessing", &T::set_disable_preprocessing, "", "", "") + .add_method("enable_consistent_interfaces", &T::enable_consistent_interfaces, "", "", ""); + + } + } diff --git a/ugbase/bridge/bridge.cpp b/ugbase/bridge/bridge.cpp index 623b6421f..4828872c3 100644 --- a/ugbase/bridge/bridge.cpp +++ b/ugbase/bridge/bridge.cpp @@ -249,7 +249,7 @@ void RegisterStandardBridges(Registry& reg, string parentGroup) RegisterBridge_Solver(reg, parentGroup); RegisterBridge_Eigensolver(reg, parentGroup); RegisterBridge_DomainDependentPreconditioner(reg, parentGroup); - RegisterBridge_ConstrainedLinearIterator(reg, parentGroup); + //RegisterBridge_ConstrainedLinearIterator(reg, parentGroup); RegisterBridge_Restart(reg, parentGroup); diff --git a/ugbase/bridge/disc_bridges/constraints_bridge.cpp b/ugbase/bridge/disc_bridges/constraints_bridge.cpp index c0c1b8bca..90d4a6bca 100644 --- a/ugbase/bridge/disc_bridges/constraints_bridge.cpp +++ b/ugbase/bridge/disc_bridges/constraints_bridge.cpp @@ -128,6 +128,9 @@ static void DomainAlgebra(Registry& reg, string grp) reg.add_class_(name, grp) .template add_constructor() .template add_constructor() +#ifdef LAGRANGE_DIRICHLET_ADJ_TRANSFER_FIX + .template add_constructor() +#endif .add_method("add", static_cast >, const char*, const char*)>(&T::add), "", "Value#Function#Subsets") .add_method("add", static_cast >, const std::vector&, const std::vector&)>(&T::add), diff --git a/ugbase/bridge/disc_bridges/grid_function_bridge.cpp b/ugbase/bridge/disc_bridges/grid_function_bridge.cpp index 521e51a0f..cf1a03699 100644 --- a/ugbase/bridge/disc_bridges/grid_function_bridge.cpp +++ b/ugbase/bridge/disc_bridges/grid_function_bridge.cpp @@ -259,6 +259,24 @@ static void DomainAlgebra(Registry& reg, string grp) reg.add_class_to_group(name, "L2ComponentSpace", tag); } + // L2QuotientSpace (= L2ComponentSpace which factors out constants) + { + typedef L2QuotientSpace T; + typedef IComponentSpace TBase; + typedef typename L2Integrand::weight_type TWeight; + + string name = string("L2QuotientSpace").append(suffix); + reg.add_class_(name, grp) + .template add_constructor("fctNames") + .template add_constructor("fctNames, order") + .template add_constructor("fctNames, order, weight") + .template add_constructor("fctNames, order, weight, ssNames") + .template add_constructor) >("fctNames, order, weight") + .template add_constructor, const char *) >("fctNames, order, weight, ssNames") + .set_construct_as_smart_pointer(true); + reg.add_class_to_group(name, "L2QuotientSpace", tag); + } + // H1SemiComponentSpace { typedef H1SemiComponentSpace T; @@ -334,6 +352,7 @@ static void DomainAlgebra(Registry& reg, string grp) reg.add_class_(name, grp) .template add_constructor("fctNames") .template add_constructor("fctNames, order") + .template add_constructor("fctNames, subsetNames, order") //.template add_constructor("fctNames, order, scale") .set_construct_as_smart_pointer(true); reg.add_class_to_group(name, "H1ComponentSpace", tag); diff --git a/ugbase/bridge/disc_bridges/multigrid_bridge.cpp b/ugbase/bridge/disc_bridges/multigrid_bridge.cpp index fc1abf31f..6277caf35 100644 --- a/ugbase/bridge/disc_bridges/multigrid_bridge.cpp +++ b/ugbase/bridge/disc_bridges/multigrid_bridge.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2015: G-CSC, Goethe University Frankfurt + * Copyright (c) 2011-2018: G-CSC, Goethe University Frankfurt * Author: Andreas Vogel * * This file is part of UG4. @@ -35,6 +35,7 @@ #include #include + // include bridge #include "bridge/bridge.h" #include "bridge/util.h" @@ -50,6 +51,7 @@ #include "lib_disc/operator/linear_operator/multi_grid_solver/mg_solver.h" #include "lib_disc/operator/linear_operator/element_gauss_seidel/element_gauss_seidel.h" #include "lib_disc/operator/linear_operator/element_gauss_seidel/component_gauss_seidel.h" +#include "lib_disc/operator/linear_operator/subspace_correction/sequential_subspace_correction.h" #include "lib_disc/operator/linear_operator/uzawa/uzawa.h" using namespace std; @@ -209,6 +211,7 @@ static void DomainAlgebra(Registry& reg, string grp) .add_method("set_comm_comp_overlap", &T::set_comm_comp_overlap) .add_method("ignore_init_for_base_solver", static_cast(&T::ignore_init_for_base_solver), "", "ignore") .add_method("ignore_init_for_base_solver", static_cast(&T::ignore_init_for_base_solver), "is ignored", "") + .add_method("force_reinit", &T::force_reinit) .set_construct_as_smart_pointer(true); reg.add_class_to_group(name, "GeometricMultiGrid", tag); } @@ -246,25 +249,56 @@ static void DomainAlgebra(Registry& reg, string grp) reg.add_class_to_group(name, "ComponentGaussSeidel", tag); } + // SequentialSubspaceCorrection + { + typedef SequentialSubspaceCorrection T; + typedef IPreconditioner TBase; + string name = string("SequentialSubspaceCorrection").append(suffix); + reg.add_class_(name, grp, "Sequential subspace correction") + .template add_constructor("omega") + .add_method("set_vertex_subspace", &T::set_vertex_subspace, "", "subspace") + .set_construct_as_smart_pointer(true); + reg.add_class_to_group(name, "SequentialSubspaceCorrection", tag); + } + + + // ILocalSubspace (i.e. base class for any 'subspace' in subspace correction methods) + { + typedef ILocalSubspace TVertexSubspace; + string name = string("ILocalSubspace").append(suffix); + reg.add_class_(name, grp, "ILocalSubspace base"); + reg.add_class_to_group(name, "ILocalSubspace", tag); + + // VertexCenteredVankaSubspace + { + typedef VertexCenteredVankaSubspace T; + // typedef IPreconditioner TBase; + string name = string("VertexCenteredVankaSubspace").append(suffix); + reg.add_class_(name, grp, "Vertex centered Vanka") + .template add_constructor&, const std::vector&)>("primary functions, secondary functions") + .set_construct_as_smart_pointer(true); + reg.add_class_to_group(name, "VertexCenteredVankaSubspace", tag); + } + } + // Uzawa (smoother/iteration) - { - typedef UzawaBase T; - typedef ILinearIterator TBase; - typedef DebugWritingObject TBase2; - string name = string("UzawaBase").append(suffix); - - reg.add_class_(name, grp) - .ADD_CONSTRUCTOR( (const std::vector&) ) ("String ID for Schur") - .ADD_CONSTRUCTOR( (const char *) ) ("String ID for Schur") - .add_method("set_forward_iter", &T::set_forward_iter, "forward iteration", "beta") - .add_method("set_schur_iter", &T::set_schur_iter, "Schur iteration", "beta") - .add_method("set_backward_iter", &T::set_backward_iter, "backward iteration", "beta") - .add_method("set_schur_operator_update", &T::set_schur_operator_update, "update for Schur", "beta") - .set_construct_as_smart_pointer(true); - - reg.add_class_to_group(name, "UzawaBase", tag); - //std::cout <<"Registered "<< name << std::endl; - } + { + typedef UzawaBase T; + typedef ILinearIterator TBase; + typedef DebugWritingObject TBase2; + string name = string("UzawaBase").append(suffix); + + reg.add_class_(name, grp) + .ADD_CONSTRUCTOR( (const std::vector&) ) ("String IDs for Schur complement") + .ADD_CONSTRUCTOR( (const char *) ) ("String ID for Schur") + .add_method("set_forward_iter", &T::set_forward_iter, "forward iteration", "beta") + .add_method("set_schur_iter", &T::set_schur_iter, "Schur iteration", "beta") + .add_method("set_backward_iter", &T::set_backward_iter, "backward iteration", "beta") + .add_method("set_schur_operator_update", &T::set_schur_operator_update, "update for Schur", "beta") + .set_construct_as_smart_pointer(true); + + reg.add_class_to_group(name, "UzawaBase", tag); + } } diff --git a/ugbase/bridge/disc_bridges/output_bridge.cpp b/ugbase/bridge/disc_bridges/output_bridge.cpp index 01555a046..d01f10b5c 100644 --- a/ugbase/bridge/disc_bridges/output_bridge.cpp +++ b/ugbase/bridge/disc_bridges/output_bridge.cpp @@ -140,7 +140,9 @@ static void DomainAlgebra(Registry& reg, string grp) .add_method("set_conn_viewer_output", &T::set_conn_viewer_output, "", "bCVOutput") .add_method("set_conn_viewer_indices", &T::set_conn_viewer_indices, "", "bIndicesOutput") .add_method("set_print_consistent", &T::set_print_consistent, "", "printConsistent") - .add_method("set_base_dir", &T::set_base_dir, "", "") + .add_method("set_base_dir", &T::set_base_dir, "Sets the base directory for output", "dir") + .add_method("enter_section", &T::enter_section, "Enters a debugging section", "dirName") + .add_method("leave_section", &T::leave_section, "Leaves the current debugging section", "") .add_method("set_grid_level", &T::set_grid_level, "Sets the grid level", "GridLevel") .set_construct_as_smart_pointer(true); reg.add_class_to_group(name, "GridFunctionDebugWriter", tag); diff --git a/ugbase/bridge/domain_bridges/refinement_bridge.cpp b/ugbase/bridge/domain_bridges/refinement_bridge.cpp index f66503d3b..154dfdd70 100644 --- a/ugbase/bridge/domain_bridges/refinement_bridge.cpp +++ b/ugbase/bridge/domain_bridges/refinement_bridge.cpp @@ -1579,10 +1579,12 @@ void SetSmoothSubdivisionVolumesBoundaryRefinementRule(std::string bndRefRule) SetBoundaryRefinementRule(SUBDIV_SURF_LOOP_SCHEME); else if(bndRefRule.compare("subdiv_surf_averaging_scheme") == 0) SetBoundaryRefinementRule(SUBDIV_SURF_AVERAGING_SCHEME); + else if(bndRefRule.compare("subdiv_surf_butterfly_scheme") == 0) + SetBoundaryRefinementRule(SUBDIV_SURF_BUTTERFLY_SCHEME); else if(bndRefRule.compare("subdiv_vol") == 0) SetBoundaryRefinementRule(SUBDIV_VOL); else - UG_THROW("ERROR in SetBoundaryRefinementRule: Unknown boundary refinement rule! Known rules are: 'linear', 'subdiv_surf_loop_scheme', 'subdiv_surf_averaging_scheme' or 'subdiv_vol'."); + UG_THROW("ERROR in SetBoundaryRefinementRule: Unknown boundary refinement rule! Known rules are: 'linear', 'subdiv_surf_loop_scheme', 'subdiv_surf_averaging_scheme', 'subdiv_surf_butterfly_scheme' or 'subdiv_vol'."); } diff --git a/ugbase/bridge/misc_bridges/vec_math_bridge.cpp b/ugbase/bridge/misc_bridges/vec_math_bridge.cpp index 66c86d7ed..af7922135 100644 --- a/ugbase/bridge/misc_bridges/vec_math_bridge.cpp +++ b/ugbase/bridge/misc_bridges/vec_math_bridge.cpp @@ -103,45 +103,53 @@ static void RegisterVecMathBridge_DimIndep(Registry& reg, string grp) typedef MathVector<1, number> vec_type; reg.add_class_("Vec1d", grp) .add_constructor() +#ifndef UG_FOR_VRL // TODO can we add all constructors to base class of class group? For now, use MakeVec for Java API. .add_constructor() +#endif .add_method("set_coord", &vec_type::set_coord, "", "index # value", "sets the value of the coordinate with the given index") .add_method("coord", static_cast(&vec_type::coord)); - //reg.add_class_to_group("Vec1d", "Vec", GetDimensionTag<1>()); + reg.add_class_to_group("Vec1d", "Vec", GetDimensionTag<1>()); } { typedef MathVector<2, number> vec_type; reg.add_class_("Vec2d", grp) .add_constructor() +#ifndef UG_FOR_VRL // TODO can we add all constructors to base class of class group? For now, use MakeVec for Java API. .add_constructor() +#endif .add_method("set_coord", &vec_type::set_coord, "", "index # value", "sets the value of the coordinate with the given index") .add_method("coord", static_cast(&vec_type::coord)); - //reg.add_class_to_group("Vec2d", "Vec", GetDimensionTag<2>()); + reg.add_class_to_group("Vec2d", "Vec", GetDimensionTag<2>()); } { typedef MathVector<3, number> vec_type; reg.add_class_("Vec3d", grp) .add_constructor() +#ifndef UG_FOR_VRL // TODO can we add all constructors to base class of class group? For now, use MakeVec for Java API. .add_constructor() +#endif .add_method("set_coord", &vec_type::set_coord, "", "index # value", "sets the value of the coordinate with the given index") .add_method("coord", static_cast(&vec_type::coord)); - //reg.add_class_to_group("Vec3d", "Vec", GetDimensionTag<3>()); + reg.add_class_to_group("Vec3d", "Vec", GetDimensionTag<3>()); } { typedef MathVector<4, number> vec_type; reg.add_class_("Vec4d", grp) .add_constructor() +#ifndef UG_FOR_VRL // TODO can we add all constructors to base class of class group? For now, use MakeVec for Java API. .add_constructor() +#endif .add_method("set_coord", &vec_type::set_coord, "", "index # value", "sets the value of the coordinate with the given index") .add_method("coord", static_cast(&vec_type::coord)); - //reg.add_class_to_group("Vec4d", "Vec", GetDimensionTag<4>()); + reg.add_class_to_group("Vec4d", "Vec", GetDimensionTag<4>()); } // register make-methods diff --git a/ugbase/common/math/math_vector_matrix/math_matrix_vector_functions_common_impl.hpp b/ugbase/common/math/math_vector_matrix/math_matrix_vector_functions_common_impl.hpp index c992ccc67..96325b353 100644 --- a/ugbase/common/math/math_vector_matrix/math_matrix_vector_functions_common_impl.hpp +++ b/ugbase/common/math/math_vector_matrix/math_matrix_vector_functions_common_impl.hpp @@ -34,6 +34,7 @@ #define __H__LGMATH__MATRIX_VECTOR_FUNCTIONS_COMMON_IMPL__ #include +#include "common/error.h" #include "math_matrix.h" #include "math_vector.h" diff --git a/ugbase/common/progress.cpp b/ugbase/common/progress.cpp index c5de322db..0bffebf7e 100644 --- a/ugbase/common/progress.cpp +++ b/ugbase/common/progress.cpp @@ -45,6 +45,7 @@ Progress::Progress(int minSecondsUntilProgress) m_minSecondsUntilProgress = minSecondsUntilProgress; m_length=100; bStarted=false; + posNow=-1; myDepth = totalDepth++; } /* diff --git a/ugbase/common/progress.h b/ugbase/common/progress.h index fe017ab76..b55d8fe85 100644 --- a/ugbase/common/progress.h +++ b/ugbase/common/progress.h @@ -48,8 +48,6 @@ namespace ug class Progress { - int m_minSecondsUntilProgress; - double startS; public: Progress(int minSecondsUntilProgress=-1); @@ -89,7 +87,11 @@ class Progress void setD(double now); void stop(); + private: + + int m_minSecondsUntilProgress; + double startS; double dNextValueToUpdate; size_t iNextValueToUpdate; int posNow; diff --git a/ugbase/common/util/file_util.h b/ugbase/common/util/file_util.h index 8340158bd..e5ef9c9a6 100644 --- a/ugbase/common/util/file_util.h +++ b/ugbase/common/util/file_util.h @@ -123,6 +123,19 @@ static inline size_t FileSize( std::string filename) */ UG_API bool FileTypeIs( const char *filename, const char *extension ); +/** + * \brief Creates a directory + * + * This is a hack. This function does the same as CreateDirectory. + * But there is a linking problem in MS VC, so that the name CreateDirectory + * cannot be used. + * + * \param[in] directory name of the directory + * \return true if successfull + * + */ +UG_API bool CreateDirectoryTMP(const char *directory); + /** * \brief Creates a directory * @@ -130,14 +143,29 @@ UG_API bool FileTypeIs( const char *filename, const char *extension ); * \param[in] mode (optional, default 0777) Sets ownership options for the file. * Ignored on windows. * \return true if successfull - * \{ */ -UG_API bool CreateDirectoryTMP(const char *directory); UG_API bool CreateDirectory(const char *directory); + +/** + * \brief Creates a directory + * + * \param[in] directory name of the directory + * \param[in] mode (optional, default 0777) Sets ownership options for the file. + * Ignored on windows. + * \return true if successfull + */ UG_API bool CreateDirectory(const char *directory, int mode); -UG_API bool CreateDirectory(std::string directory); -/** \} */ +/** + * \brief Creates a directory + * + * \param[in] directory name of the directory + * \return true if successfull + */ +static inline bool CreateDirectory(std::string directory) +{ + return CreateDirectoryTMP(directory.c_str()); +} /** * \brief Compares two files by their content diff --git a/ugbase/common/util/os_dependent_impl/file_util_posix.cpp b/ugbase/common/util/os_dependent_impl/file_util_posix.cpp index dd40ea32e..807eec99a 100644 --- a/ugbase/common/util/os_dependent_impl/file_util_posix.cpp +++ b/ugbase/common/util/os_dependent_impl/file_util_posix.cpp @@ -156,11 +156,6 @@ bool CreateDirectory(const char *directory, int mode) return mkdir(directory, mode) == 0; } -bool CreateDirectory(std::string directory) -{ - return CreateDirectory(directory.c_str()); -} - std::string GetTmpPath() { return string("/tmp"); diff --git a/ugbase/common/util/os_dependent_impl/file_util_win.cpp b/ugbase/common/util/os_dependent_impl/file_util_win.cpp index 27a82314d..0774d18b2 100644 --- a/ugbase/common/util/os_dependent_impl/file_util_win.cpp +++ b/ugbase/common/util/os_dependent_impl/file_util_win.cpp @@ -154,11 +154,6 @@ bool CreateDirectory(const char *directory, int mode) return _mkdir(directory) == 0; } -bool CreateDirectory(std::string directory) -{ - return CreateDirectory(directory.c_str()); -} - std::string GetTmpPath() { static string userDataPath(""); diff --git a/ugbase/lib_algebra/CMakeLists.txt b/ugbase/lib_algebra/CMakeLists.txt index abf4afe33..3fa108768 100644 --- a/ugbase/lib_algebra/CMakeLists.txt +++ b/ugbase/lib_algebra/CMakeLists.txt @@ -52,6 +52,7 @@ set(src_Algebra ${src_Algebra} operator/preconditioner/line_smoothers.cpp operator/linear_solver/analyzing_solver.cpp algebra_common/permutation_util.cpp + operator/preconditioner/schur/schur.cpp ) add_subdirectory(common/matrixio) @@ -59,7 +60,6 @@ add_subdirectory(common/matrixio) if(PARALLEL) # add parallelization set(src_Algebra ${src_Algebra} - operator/preconditioner/schur/schur.cpp operator/preconditioner/schur/schur_precond.cpp operator/preconditioner/schur/schur_complement_operator.cpp operator/preconditioner/schur/parallel_progress.cpp diff --git a/ugbase/lib_algebra/operator/debug_writer.h b/ugbase/lib_algebra/operator/debug_writer.h index f923a1555..770dcef56 100644 --- a/ugbase/lib_algebra/operator/debug_writer.h +++ b/ugbase/lib_algebra/operator/debug_writer.h @@ -36,7 +36,7 @@ #include "common/util/smart_pointer.h" #include "common/math/ugmath.h" #include "common/profiler/profiler.h" - +#include "common/util/file_util.h" namespace ug{ @@ -70,6 +70,17 @@ class IVectorDebugWriter /// write vector virtual void write_vector(const vector_type& vec, const char* name) = 0; + + /// prints a message + virtual void print_message(const char * msg) + { + UG_LOG ("DBG > "); + for (size_t i = 0; i < m_secDir.size (); i++) + { + UG_LOG (":" << m_secDir[i]); + } + UG_LOG (" > " << msg); + } /// returns the current dimension int current_dimension() const {return m_currentDim;} @@ -111,20 +122,51 @@ class IVectorDebugWriter return m_currentDim; } - /// set the base directory for output files (.vec and .mat) + /// set the base directory for output files (.vec and .mat) inline void set_base_dir(const char* const baseDir) {m_baseDir = std::string(baseDir);} std::string get_base_dir() { return m_baseDir; } + + /// enter a new debugging section + inline void enter_section(const char* secDir) {m_secDir.push_back(secDir);} + + /// leave the current debugging section + inline void leave_section () {m_secDir.pop_back();} protected: - /// base directory for output - std::string m_baseDir; - + /// returns the positions and sets the current dim template std::vector >& positions() { m_currentDim = dim; return get_pos(Int2Type()); } + + /// composes the path for the files and creates the intermediate directories (up to the base one): + void compose_file_path(std::string& path) + { + path = get_base_dir (); + if (! FileExists (path)) + { + UG_WARNING ("IVectorDebugWriter: Directory '" << path << "' does not exist. Using cwd instead.\n"); + path = "."; + } + for (size_t i = 0; i < m_secDir.size (); i++) + { + path.append ("/"); //TODO: This can be OS-dependent. + path.append (m_secDir[i]); + if ((! FileExists (path)) && ! CreateDirectory (path)) + UG_WARNING ("IVectorDebugWriter: Could not create directory '" << path << "'. Using cwd instead.\n"); + } + path.append ("/"); //TODO: This can be OS-dependent. + } + + protected: + + /// base directory for the debugging output + std::string m_baseDir; + + /// debuging section subdirectories + std::vector m_secDir; /// current dimension int m_currentDim; @@ -194,20 +236,23 @@ class VectorDebugWritingObject SmartPtr > vector_debug_writer() {return m_spVectorDebugWriter;} ConstSmartPtr > vector_debug_writer() const {return m_spVectorDebugWriter;} + /// returns true if the debug writer is set + bool vector_debug_writer_valid() const {return m_spVectorDebugWriter.valid();} + protected: - virtual void write_debug(const vector_type& vec, std::string filename) + /// writing debug output for a vector (if debug writer set) + void write_debug(const vector_type& vec, const char* filename) { - write_debug(vec, filename.c_str()); + write_debug(vec, std::string (filename)); } /// writing debug output for a vector (if debug writer set) - virtual void write_debug(const vector_type& vec, const char* filename) + virtual void write_debug(const vector_type& vec, std::string name) { PROFILE_FUNC_GROUP("algebra debug"); // if no debug writer set, we're done if(m_spVectorDebugWriter.invalid()) return; // check ending - std::string name(filename); size_t iExtPos = name.find_last_of("."); if(iExtPos != std::string::npos && name.substr(iExtPos).compare(".vec") != 0) UG_THROW("Only '.vec' format supported for vectors, but" @@ -219,7 +264,35 @@ class VectorDebugWritingObject // write m_spVectorDebugWriter->write_vector(vec, name.c_str()); } + + /// prints a debugger message (listing all the sections) + void print_debugger_message(std::string msg) + { + print_debugger_message(msg.c_str()); + } + /// prints a debugger message (listing all the sections) + void print_debugger_message(const char * msg) + { + if (m_spVectorDebugWriter.valid()) m_spVectorDebugWriter->print_message(msg); + } + /// enters a debugging section + void enter_vector_debug_writer_section(std::string secDir) + { + enter_vector_debug_writer_section(secDir.c_str()); + } + /// enters a debugging section + void enter_vector_debug_writer_section(const char * secDir) + { + if (m_spVectorDebugWriter.valid()) m_spVectorDebugWriter->enter_section(secDir); + } + + /// leaves a debugging section + void leave_vector_debug_writer_section() + { + if (m_spVectorDebugWriter.valid()) m_spVectorDebugWriter->leave_section(); + } + protected: /// Debug Writer SmartPtr > m_spVectorDebugWriter; @@ -267,16 +340,23 @@ class DebugWritingObject : public VectorDebugWritingObject > debug_writer() {return m_spDebugWriter;} ConstSmartPtr > debug_writer() const {return m_spDebugWriter;} + /// returns true if the debug writer is set + bool debug_writer_valid() const {return m_spDebugWriter.valid();} + protected: /// write debug output for a matrix (if debug writer set) - virtual void write_debug(const matrix_type& mat, const char* filename) + void write_debug(const matrix_type& mat, const char* filename) + { + write_debug(mat, std::string(filename)); + } + /// write debug output for a matrix (if debug writer set) + void write_debug(const matrix_type& mat, std::string name) { PROFILE_FUNC_GROUP("algebra debug"); // if no debug writer set, we're done if(m_spDebugWriter.invalid()) return; // check ending - std::string name(filename); size_t iExtPos = name.find_last_of("."); if(iExtPos != std::string::npos && name.substr(iExtPos).compare(".mat") != 0) UG_THROW("Only '.mat' format supported for matrices, but" @@ -289,6 +369,23 @@ class DebugWritingObject : public VectorDebugWritingObjectwrite_matrix(mat, name.c_str()); } + /// enters a debugging section + void enter_debug_writer_section(std::string secDir) + { + enter_debug_writer_section(secDir.c_str()); + } + /// enters a debugging section + void enter_debug_writer_section(const char * secDir) + { + if (m_spDebugWriter.valid()) m_spDebugWriter->enter_section(secDir); + } + + /// leaves a debugging section + void leave_debug_writer_section() + { + if (m_spDebugWriter.valid()) m_spDebugWriter->leave_section(); + } + protected: /// Debug Writer SmartPtr > m_spDebugWriter; diff --git a/ugbase/lib_algebra/operator/eigensolver/power_method.h b/ugbase/lib_algebra/operator/eigensolver/power_method.h index 28dca3c24..9994e08a2 100644 --- a/ugbase/lib_algebra/operator/eigensolver/power_method.h +++ b/ugbase/lib_algebra/operator/eigensolver/power_method.h @@ -187,6 +187,15 @@ class PowerMethod else m_spEigenvector = m_spResidual->clone(); + // reset Dirichlet rows to 0 + for(size_t i = 0; i < m_spEigenvector->size(); i++) + { + if(m_vbDirichlet[i]) + { + (*m_spEigenvector)[i] = 0.0; + } + } + // v = v / ||v||_B normalize_approximations(); @@ -250,6 +259,15 @@ class PowerMethod // v = A^-1 B v or v = A^-1 v m_spSolver->apply(*m_spEigenvector, *m_spResidual); + // reset Dirichlet rows to 0 + for(size_t i = 0; i < m_spEigenvector->size(); i++) + { + if(m_vbDirichlet[i]) + { + (*m_spEigenvector)[i] = 0.0; + } + } + // v = v / ||v||_B normalize_approximations(); diff --git a/ugbase/lib_algebra/operator/interface/constrained_linear_iterator.h b/ugbase/lib_algebra/operator/interface/constrained_linear_iterator.h index 3e69b7aaf..b153238f6 100644 --- a/ugbase/lib_algebra/operator/interface/constrained_linear_iterator.h +++ b/ugbase/lib_algebra/operator/interface/constrained_linear_iterator.h @@ -54,6 +54,11 @@ namespace ug { * The handled constraints must implement the two methods: * adjust_correction(), * adjust_defect(). + * + * @note This class may have lost its use: + * Constraints are now usually implemented in such a way that + * that both defect and correction is always zero in constrained + * DoFs. */ template class ConstrainedLinearIterator : public TLinIt diff --git a/ugbase/lib_algebra/operator/linear_solver/bicgstab.h b/ugbase/lib_algebra/operator/linear_solver/bicgstab.h index 4b7dd7c1a..33877e755 100644 --- a/ugbase/lib_algebra/operator/linear_solver/bicgstab.h +++ b/ugbase/lib_algebra/operator/linear_solver/bicgstab.h @@ -114,7 +114,7 @@ class BiCGStab // check correct storage type in parallel #ifdef UG_PARALLEL if(!b.has_storage_type(PST_ADDITIVE) || !x.has_storage_type(PST_CONSISTENT)) - UG_THROW("BiCGStab: Inadequate storage format of Vectors."); + UG_THROW("BiCGStab: Inadequate parallel storage format of the vectors for the sol and the rhs."); #endif // build defect: r := b - A*x @@ -138,7 +138,7 @@ class BiCGStab // convert b to unique (should already be unique due to norm calculation) #ifdef UG_PARALLEL if(!r.change_storage_type(PST_UNIQUE)) - UG_THROW("BiCGStab: Cannot convert b to unique vector."); + UG_THROW("BiCGStab: Cannot convert b to a vector with the 'unique' parallel storage type."); #endif // needed variables @@ -147,6 +147,8 @@ class BiCGStab // restart flag (set to true at first run) bool bRestart = true; + write_debugXR(x, r, convergence_check()->step(), 'i'); + // Iteration loop while(!convergence_check()->iteration_ended()) { @@ -225,13 +227,16 @@ class BiCGStab // apply q = M^-1 * p ... if(preconditioner().valid()) { + enter_precond_debug_section(convergence_check()->step(), 'a'); if(!preconditioner()->apply(q, p)) { UG_LOG("BiCGStab: Cannot apply preconditioner. Aborting.\n"); + this->leave_vector_debug_writer_section(); return false; } - // ... or copy q = p + this->leave_vector_debug_writer_section(); } + // ... or copy q = p else { q = p; @@ -280,6 +285,8 @@ class BiCGStab // check convergence convergence_check()->update(s); + write_debugXR(x, s, convergence_check()->step(), 'a'); + // if finished: set output to last defect and exist loop if(convergence_check()->iteration_ended()) { @@ -289,13 +296,16 @@ class BiCGStab // apply q = M^-1 * s ... if(preconditioner().valid()) { + enter_precond_debug_section(convergence_check()->step(), 'b'); if(!preconditioner()->apply(q, s)) { UG_LOG("BiCGStab: Cannot apply preconditioner. Aborting.\n"); + this->leave_vector_debug_writer_section(); return false; } - // ... or set q:=s + this->leave_vector_debug_writer_section(); } + // ... or set q:=s else { q = s; @@ -352,6 +362,8 @@ class BiCGStab // check convergence convergence_check()->update(r); + write_debugXR(x, r, convergence_check()->step(), 'b'); + // check values if(omega == 0.0) { @@ -401,6 +413,23 @@ class BiCGStab convergence_check()->set_info(s); } + /// debugger output: solution and residual + void write_debugXR(vector_type &x, vector_type &r, int loopCnt, char phase) + { + if(!this->vector_debug_writer_valid()) return; + char ext[20]; sprintf(ext, "-%c_iter%03d", phase, loopCnt); + write_debug(r, std::string("BiCGStab_Residual") + ext + ".vec"); + write_debug(x, std::string("BiCGStab_Solution") + ext + ".vec"); + } + + /// debugger section for the preconditioner + void enter_precond_debug_section(int loopCnt, char phase) + { + if(!this->vector_debug_writer_valid()) return; + char ext[20]; sprintf(ext, "-%c_iter%03d", phase, loopCnt); + this->enter_vector_debug_writer_section(std::string("BiCGStab_Precond") + ext); + } + public: virtual std::string config_string() const { diff --git a/ugbase/lib_algebra/operator/linear_solver/cg.h b/ugbase/lib_algebra/operator/linear_solver/cg.h index bae68e1e5..fe0381a02 100644 --- a/ugbase/lib_algebra/operator/linear_solver/cg.h +++ b/ugbase/lib_algebra/operator/linear_solver/cg.h @@ -121,16 +121,21 @@ class CG SmartPtr spZ = x.clone_without_values(); vector_type& z = *spZ; SmartPtr spP = x.clone_without_values(); vector_type& p = *spP; + write_debugXR(x, r, convergence_check()->step()); + // Preconditioning if(preconditioner().valid()) { + enter_precond_debug_section(convergence_check()->step()); // apply z = M^-1 * s if(!preconditioner()->apply(z, r)) { UG_LOG("ERROR in 'CG::apply_return_defect': " "Cannot apply preconditioner. Aborting.\n"); + this->leave_vector_debug_writer_section(); return false; } + this->leave_vector_debug_writer_section(); } else z = r; @@ -187,6 +192,8 @@ class CG // Update r := r - alpha*t VecScaleAdd(r, 1.0, r, -alpha, q); + write_debugXR(x, r, convergence_check()->step()); + // Check convergence convergence_check()->update(r); if(convergence_check()->iteration_ended()) break; @@ -194,16 +201,19 @@ class CG // Preconditioning if(preconditioner().valid()) { + enter_precond_debug_section(convergence_check()->step()); // apply z = M^-1 * r if(!preconditioner()->apply(z, r)) { UG_LOG("ERROR in 'CG::apply_return_defect': " "Cannot apply preconditioner. Aborting.\n"); + this->leave_vector_debug_writer_section(); return false; } + this->leave_vector_debug_writer_section(); } else z = r; - + #ifdef UG_PARALLEL // make z consistent if(!z.change_storage_type(PST_CONSISTENT)) @@ -260,6 +270,23 @@ class CG convergence_check()->set_info(s); } + /// debugger output: solution and residual + void write_debugXR(vector_type &x, vector_type &r, int loopCnt) + { + if(!this->vector_debug_writer_valid()) return; + char ext[20]; sprintf(ext, "_iter%03d", loopCnt); + write_debug(r, std::string("CG_Residual") + ext + ".vec"); + write_debug(x, std::string("CG_Solution") + ext + ".vec"); + } + + /// debugger section for the preconditioner + void enter_precond_debug_section(int loopCnt) + { + if(!this->vector_debug_writer_valid()) return; + char ext[20]; sprintf(ext, "_iter%03d", loopCnt); + this->enter_vector_debug_writer_section(std::string("CG_Precond_") + ext); + } + protected: number VecProd(vector_type& a, vector_type& b) { diff --git a/ugbase/lib_algebra/operator/linear_solver/external_solvers/external_solvers.h b/ugbase/lib_algebra/operator/linear_solver/external_solvers/external_solvers.h index a01b49b8d..f5da88dea 100644 --- a/ugbase/lib_algebra/operator/linear_solver/external_solvers/external_solvers.h +++ b/ugbase/lib_algebra/operator/linear_solver/external_solvers/external_solvers.h @@ -87,6 +87,7 @@ class IExternalSolver public: // Constructor IExternalSolver() + : m_bUseConsistentInterfaces(false), m_bDisablePreprocessing(false) { m_size = 0; m_blockSize = 0; @@ -104,13 +105,19 @@ class IExternalSolver /// returns if parallel solving is supported virtual bool supports_parallel() const {return false;} + /// disable preprocessing (if underlying matrix has not changed) + void set_disable_preprocessing(bool bDisable) {m_bDisablePreprocessing = bDisable;} - public: + void enable_consistent_interfaces(bool enable){ m_bUseConsistentInterfaces = enable; } virtual void double_init(const CPUAlgebra::matrix_type &mat) = 0; void mat_preprocess(const matrix_type &A) { + // do not do a thing if preprocessing disabled + if (m_bDisablePreprocessing) + return; + if( A.num_rows() == 0 || A.num_cols() == 0) { m_size = 0; return; } STATIC_ASSERT(matrix_type::rows_sorted, Matrix_has_to_have_sorted_rows); @@ -119,13 +126,16 @@ class IExternalSolver #ifdef UG_PARALLEL // add slave rows to master rows (in indices which this is possible for) matrix_type A_tmp; A_tmp = A; - MatAddSlaveRowsToMasterRowOverlap0(A_tmp); - - // set zero on slaves - std::vector vIndex; - CollectUniqueElements(vIndex, A.layouts()->slave()); - SetDirichletRow(A_tmp, vIndex); - + if(m_bUseConsistentInterfaces){ + MatMakeConsistentOverlap0(A_tmp); + }else { + MatAddSlaveRowsToMasterRowOverlap0(A_tmp); + + // set zero on slaves + std::vector vIndex; + CollectUniqueElements(vIndex, A.layouts()->slave()); + SetDirichletRow(A_tmp, vIndex); + } // Even after this setting of Dirichlet rows, it is possible that there are // zero rows on a proc because of the distribution: // For example, if one has a horizontal grid interface between two SHADOW_RIM_COPY @@ -218,12 +228,17 @@ class IExternalSolver m_d.resize(m_size); #ifdef UG_PARALLEL + m_d.set_storage_type(PST_ADDITIVE); m_c.set_storage_type(PST_CONSISTENT); - // make defect unique SmartPtr spDtmp = d.clone(); - spDtmp->change_storage_type(PST_UNIQUE); + if(m_bUseConsistentInterfaces){ + spDtmp->change_storage_type(PST_CONSISTENT); + } else{ + spDtmp->change_storage_type(PST_UNIQUE); + } + get_vector(m_d, *spDtmp); #else @@ -239,6 +254,7 @@ class IExternalSolver #ifdef UG_PARALLEL // correction must always be consistent (but is unique by construction) + // (or regarded as such in the case of consistent interfaces) c.set_storage_type(PST_UNIQUE); c.change_storage_type(PST_CONSISTENT); #endif @@ -334,9 +350,13 @@ class IExternalSolver // Postprocess routine virtual bool postprocess() {return true;} - CPUAlgebra::vector_type m_c, m_d; - size_t m_size; - size_t m_blockSize; + bool m_bUseConsistentInterfaces; + CPUAlgebra::vector_type m_c, m_d; + size_t m_size; + size_t m_blockSize; + bool m_bDisablePreprocessing; + + }; } // namespace ug diff --git a/ugbase/lib_algebra/operator/linear_solver/linear_solver.h b/ugbase/lib_algebra/operator/linear_solver/linear_solver.h index 49ea8f26c..cb1455be4 100644 --- a/ugbase/lib_algebra/operator/linear_solver/linear_solver.h +++ b/ugbase/lib_algebra/operator/linear_solver/linear_solver.h @@ -102,8 +102,7 @@ class LinearSolver if(!preconditioner()->apply_update_defect(c, d)) { - UG_LOG("ERROR in 'LinearSolver::apply': Iterator " - "Operator applied incorrectly. Aborting.\n"); + UG_LOG("ERROR in 'LinearSolver': Could not apply preconditioner. Aborting.\n"); return false; } LS_PROFILE_END(LS_ApplyPrecond); @@ -111,14 +110,6 @@ class LinearSolver return true; } - void write_debugXCD(vector_type &x, vector_type &c, vector_type &d, int loopCnt, bool bWriteC) - { - char ext[20]; sprintf(ext, "_iter%03d", loopCnt); - write_debug(d, std::string("LS_Defect_") + ext + ".vec"); - if(bWriteC) write_debug(c, std::string("LS_Correction_") + ext + ".vec"); - write_debug(x, std::string("LS_Solution_") + ext + ".vec"); - } - /// solves the system and returns the last defect virtual bool apply_return_defect(vector_type& x, vector_type& b) { @@ -127,7 +118,7 @@ class LinearSolver #ifdef UG_PARALLEL if(!b.has_storage_type(PST_ADDITIVE) || !x.has_storage_type(PST_CONSISTENT)) - UG_THROW("LinearSolver::apply: Inadequate storage format of Vectors."); + UG_THROW("LinearSolver::apply: Inadequate parallel storage format of Vectors."); #endif // rename b as d (for convenience) @@ -159,7 +150,13 @@ class LinearSolver // Iteration loop while(!convergence_check()->iteration_ended()) { - if( !compute_correction(c, d) ) return false; + enter_precond_debug_section(loopCnt); + if( !compute_correction(c, d) ) + { + this->leave_vector_debug_writer_section(); + return false; + } + this->leave_vector_debug_writer_section(); // post-process the correction m_corr_post_process.apply (c); @@ -221,6 +218,24 @@ class LinearSolver } } + /// debugger output: solution, correction, defect + void write_debugXCD(vector_type &x, vector_type &c, vector_type &d, int loopCnt, bool bWriteC) + { + if(!this->vector_debug_writer_valid()) return; + char ext[20]; sprintf(ext, "_iter%03d", loopCnt); + write_debug(d, std::string("LS_Defect") + ext + ".vec"); + if(bWriteC) write_debug(c, std::string("LS_Correction") + ext + ".vec"); + write_debug(x, std::string("LS_Solution") + ext + ".vec"); + } + + /// debugger section for the preconditioner + void enter_precond_debug_section(int loopCnt) + { + if(!this->vector_debug_writer_valid()) return; + char ext[20]; sprintf(ext, "_iter%03d", loopCnt); + this->enter_vector_debug_writer_section(std::string("LS_Precond_") + ext); + } + protected: /// postprocessor for the correction in the iterations PProcessChain m_corr_post_process; diff --git a/ugbase/lib_algebra/operator/preconditioner/gauss_seidel.h b/ugbase/lib_algebra/operator/preconditioner/gauss_seidel.h index 4ffe5ec42..626286940 100644 --- a/ugbase/lib_algebra/operator/preconditioner/gauss_seidel.h +++ b/ugbase/lib_algebra/operator/preconditioner/gauss_seidel.h @@ -170,9 +170,20 @@ class GaussSeidelBase : public IPreconditioner } else if (m_bConsistentInterfaces) { - UG_COND_THROW(!d.has_storage_type(PST_ADDITIVE), "Additive or unique defect expected."); - step(m_A, c, d, m_relax); - c.set_storage_type(PST_ADDITIVE); + // make defect consistent + SmartPtr spDtmp = d.clone(); + spDtmp->change_storage_type(PST_CONSISTENT); + + THROW_IF_NOT_EQUAL_3(c.size(), spDtmp->size(), m_A.num_rows()); + step(m_A, c, *spDtmp, m_relax); + + // declare c unique to enforce that only master correction is used + // when it is made consistent below + c.set_storage_type(PST_UNIQUE); + + //UG_COND_THROW(!d.has_storage_type(PST_ADDITIVE), "Additive or unique defect expected."); + //step(m_A, c, d, m_relax); + //c.set_storage_type(PST_ADDITIVE); } else { diff --git a/ugbase/lib_algebra/operator/preconditioner/ilu.h b/ugbase/lib_algebra/operator/preconditioner/ilu.h index b104d1e5c..f35bb9af6 100644 --- a/ugbase/lib_algebra/operator/preconditioner/ilu.h +++ b/ugbase/lib_algebra/operator/preconditioner/ilu.h @@ -223,6 +223,8 @@ bool FactorizeILUSorted(Matrix_type &A, const number eps = 1e-50) // solve x = L^-1 b +// Returns true on success, or false on issues that lead to some changes in the solution +// (the solution is computed unless no exceptions are thrown) template bool invert_L(const Matrix_type &A, Vector_type &x, const Vector_type &b) { @@ -245,6 +247,8 @@ bool invert_L(const Matrix_type &A, Vector_type &x, const Vector_type &b) } // solve x = U^-1 * b +// Returns true on success, or false on issues that lead to some changes in the solution +// (the solution is computed unless no exceptions are thrown) template bool invert_U(const Matrix_type &A, Vector_type &x, const Vector_type &b, const number eps = 1e-8) @@ -254,8 +258,8 @@ bool invert_U(const Matrix_type &A, Vector_type &x, const Vector_type &b, typename Vector_type::value_type s; - size_t numNearZero=0; - + bool result = true; + // last row diagonal U entry might be close to zero with corresponding close to zero rhs // when solving Navier Stokes system, therefore handle separately if(x.size() > 0) @@ -269,27 +273,27 @@ bool invert_U(const Matrix_type &A, Vector_type &x, const Vector_type &b, // nearly zero due to round-off errors. In order to allow ill- // scaled matrices (i.e. small matrix entries row-wise) this // is compared to the rhs, that is small in this case as well. + //TODO: Note that this may happen for problems with naturally + // non-zero kernels, e.g. for the Stokes equation. One should + // probably suppress this message in those cases but set the + // rhs to 0. if (BlockNorm(A(i,i)) <= eps * BlockNorm(s)) { - if(numNearZero++<5) - { UG_LOG("ILU Warning: Near-zero diagonal entry " - "with norm "<=5) - { UG_LOG("...\nILU Warning: " << numNearZero << " ( out of " << x.size() << ") near-zero diagonal entries in last row of U.\n"); } - - return true; + return result; } @@ -372,7 +373,8 @@ class ILU : public IPreconditioner protected: using base_type::set_debug; using base_type::debug_writer; - // using base_type::write_debug; + using base_type::write_debug; + using base_type::print_debugger_message; public: // Constructor @@ -465,7 +467,11 @@ class ILU : public IPreconditioner matrix_type &mat = *pOp; PROFILE_BEGIN_GROUP(ILU_preprocess, "algebra ILU"); // Debug output of matrices - write_debug(mat, "ILU_prep_01_A_BeforeMakeUnique"); + #ifdef UG_PARALLEL + write_overlap_debug(mat, "ILU_prep_01_A_BeforeMakeUnique"); + #else + write_debug(mat, "ILU_PreProcess_orig_A"); + #endif m_ILU = mat; @@ -517,14 +523,21 @@ class ILU : public IPreconditioner m_h.resize(m_ILU.num_cols()); - write_debug(m_ILU, "ILU_prep_02_A_AfterMakeUnique"); + #ifdef UG_PARALLEL + write_overlap_debug(m_ILU, "ILU_prep_02_A_AfterMakeUnique"); + #endif // if using overlap we already sort in a different way if(m_bSort && !(m_useOverlap && sortSlaveToEnd)) calc_cuthill_mckee(); // Debug output of matrices - write_debug(m_ILU, "ILU_prep_03_A_BeforeFactorize"); + #ifdef UG_PARALLEL + write_overlap_debug(m_ILU, "ILU_prep_03_A_BeforeFactorize"); + #else + write_debug(m_ILU, "ILU_PreProcess_U_BeforeFactor"); + #endif + // Compute ILU Factorization if (m_beta!=0.0) FactorizeILUBeta(m_ILU, m_beta); @@ -533,7 +546,11 @@ class ILU : public IPreconditioner m_ILU.defragment(); // Debug output of matrices - write_debug(m_ILU, "ILU_prep_04_A_AfterFactorize"); + #ifdef UG_PARALLEL + write_overlap_debug(m_ILU, "ILU_prep_04_A_AfterFactorize"); + #else + write_debug(m_ILU, "ILU_PreProcess_U_AfterFactor"); + #endif // we're done return true; @@ -545,15 +562,19 @@ class ILU : public IPreconditioner if(!m_bSort || m_bSortIsIdentity) { // apply iterator: c = LU^{-1}*d - invert_L(m_ILU, tmp, d); // h := L^-1 d - invert_U(m_ILU, c, tmp, m_invEps); // c := U^-1 h = (LU)^-1 d + if(! invert_L(m_ILU, tmp, d)) // h := L^-1 d + print_debugger_message("ILU: There were issues at inverting L\n"); + if(! invert_U(m_ILU, c, tmp, m_invEps)) // c := U^-1 h = (LU)^-1 d + print_debugger_message("ILU: There were issues at inverting U\n"); } else { // we save one vector here by renaming SetVectorAsPermutation(tmp, d, m_newIndex); - invert_L(m_ILU, c, tmp); // c = L^{-1} d - invert_U(m_ILU, tmp, c, m_invEps); // tmp = (LU)^{-1} d + if(! invert_L(m_ILU, c, tmp)) // c = L^{-1} d + print_debugger_message("ILU: There were issues at inverting L (after permutation)\n"); + if(! invert_U(m_ILU, tmp, c, m_invEps)) // tmp = (LU)^{-1} d + print_debugger_message("ILU: There were issues at inverting U (after permutation)\n"); SetVectorAsPermutation(c, tmp, m_oldIndex); } } @@ -571,7 +592,7 @@ class ILU : public IPreconditioner // for debug output (only for application is written) static bool first = true; - if(first) write_debug(d, "ILU_step_1_d"); + if(first) write_overlap_debug(d, "ILU_step_1_d"); if(m_useOverlap){ for(size_t i = 0; i < d.size(); ++i) m_oD[i] = d[i]; @@ -580,7 +601,7 @@ class ILU : public IPreconditioner m_oD.set_storage_type(PST_ADDITIVE); m_oD.change_storage_type(PST_CONSISTENT); - if(first) write_debug(m_oD, "ILU_step_2_oD_consistent"); + if(first) write_overlap_debug(m_oD, "ILU_step_2_oD_consistent"); applyLU(m_oC, m_oD, m_h); @@ -590,10 +611,14 @@ class ILU : public IPreconditioner c.set_storage_type(PST_UNIQUE); } else if(m_useConsistentInterfaces){ - UG_COND_THROW(d.has_storage_type(PST_CONSISTENT), - "additive or unique defect expected"); - applyLU(c, d, m_h); - c.set_storage_type(PST_ADDITIVE); + // make defect consistent + SmartPtr spDtmp = d.clone(); + spDtmp->change_storage_type(PST_CONSISTENT); + applyLU(c, *spDtmp, m_h); + + // declare c unique to enforce that only master correction is used + // when it is made consistent below + c.set_storage_type(PST_UNIQUE); } else{ // make defect unique @@ -605,15 +630,17 @@ class ILU : public IPreconditioner } // write debug - if(first) write_debug(c, "ILU_step_3_c"); + if(first) write_overlap_debug(c, "ILU_step_3_c"); c.change_storage_type(PST_CONSISTENT); // write debug - if(first) {write_debug(c, "ILU_step_4_c_consistent"); first = false;} + if(first) {write_overlap_debug(c, "ILU_step_4_c_consistent"); first = false;} #else + write_debug(d, "ILU_step_d"); applyLU(c, d, m_h); + write_debug(c, "ILU_step_c"); #endif // we're done @@ -624,19 +651,17 @@ class ILU : public IPreconditioner virtual bool postprocess() {return true;} private: - template void write_debug(const T& t, std::string name) + #ifdef UG_PARALLEL + template void write_overlap_debug(const T& t, std::string name) { - #ifdef UG_PARALLEL - if(debug_writer().valid()){ - if(m_useOverlap && m_overlapWriter.valid() && t.layouts()->overlap_enabled()) - m_overlapWriter->write(t, name); - else - base_type::write_debug(t, name.c_str()); - } - #else - base_type::write_debug(t, name.c_str()); - #endif + if(debug_writer().valid()){ + if(m_useOverlap && m_overlapWriter.valid() && t.layouts()->overlap_enabled()) + m_overlapWriter->write(t, name); + else + write_debug(t, name.c_str()); + } } + #endif protected: /// storage for factorization diff --git a/ugbase/lib_algebra/operator/preconditioner/schur/schur.cpp b/ugbase/lib_algebra/operator/preconditioner/schur/schur.cpp index 8380e138a..c1d802e30 100644 --- a/ugbase/lib_algebra/operator/preconditioner/schur/schur.cpp +++ b/ugbase/lib_algebra/operator/preconditioner/schur/schur.cpp @@ -30,6 +30,7 @@ * GNU Lesser General Public License for more details. */ +#include "common/debug_id.h" #include "schur.h" namespace ug{ diff --git a/ugbase/lib_algebra/parallelization/parallel_vector.h b/ugbase/lib_algebra/parallelization/parallel_vector.h index 6e0bfa60c..487fbaa19 100644 --- a/ugbase/lib_algebra/parallelization/parallel_vector.h +++ b/ugbase/lib_algebra/parallelization/parallel_vector.h @@ -185,7 +185,7 @@ class ParallelVector : public TVector virtual this_type* virtual_clone_without_values() const; private: - // type of storage (i.e. consistent, additiv, additiv unique) + // type of storage (i.e. consistent, additive, additive unique) // holds or-combiation of constants enumerated in ug::ParallelStorageType. uint m_type; diff --git a/ugbase/lib_disc/dof_manager/dof_distribution.h b/ugbase/lib_disc/dof_manager/dof_distribution.h index e24bb1f63..04b716735 100644 --- a/ugbase/lib_disc/dof_manager/dof_distribution.h +++ b/ugbase/lib_disc/dof_manager/dof_distribution.h @@ -182,6 +182,27 @@ class DoFDistribution : public DoFDistributionInfoProvider // } } + template + void collect_associated + ( + std::vector& vAssElem, + GridObject* elem, + bool clearContainer = true + ) const + { + if (dynamic_cast(elem)) + collect_associated(vAssElem, dynamic_cast(elem), clearContainer); + else if (dynamic_cast(elem)) + collect_associated(vAssElem, dynamic_cast(elem), clearContainer); + else if (dynamic_cast(elem)) + collect_associated(vAssElem, dynamic_cast(elem), clearContainer); + else if (dynamic_cast(elem)) + collect_associated(vAssElem, dynamic_cast(elem), clearContainer); + else + UG_THROW("Element is neither Vertex nor Edge, Face or Volume. " + "Other elements not implemented."); + } + /// returns if the grid object is part of the dof distribution template bool is_contained(TGeomObj* obj) const{ diff --git a/ugbase/lib_disc/function_spaces/error_indicator.h b/ugbase/lib_disc/function_spaces/error_indicator.h index 7ed37c0b9..df96b51b7 100644 --- a/ugbase/lib_disc/function_spaces/error_indicator.h +++ b/ugbase/lib_disc/function_spaces/error_indicator.h @@ -1100,7 +1100,7 @@ void EvaluateResidualErrorP1(SmartPtr u, // evaluate L2-Norm of f and store element contributions in aaError /*SmartPtr > spIntegrand = make_sp(new UserDataIntegrand(f, u, time));*/ - UserDataIntegrand integrand(f, u, time); + UserDataIntegrand integrand(f, &(*u), time); Integrate(u->template begin(), u->template end(), aaPos, integrand, quadOrder, quadType, &aaError); diff --git a/ugbase/lib_disc/function_spaces/grid_function_util.h b/ugbase/lib_disc/function_spaces/grid_function_util.h index 514d07ca5..d7e063e9b 100644 --- a/ugbase/lib_disc/function_spaces/grid_function_util.h +++ b/ugbase/lib_disc/function_spaces/grid_function_util.h @@ -830,7 +830,6 @@ class GridFunctionDebugWriter: public IDebugWriter /// write vector virtual void write_vector(const vector_type& vec, const char* filename) { - // write to conn viewer if (bConnViewerOut) write_vector_to_conn_viewer(vec, filename); @@ -854,15 +853,9 @@ class GridFunctionDebugWriter: public IDebugWriter update_positions(); - std::string name = get_base_dir() + "/" + filename; - - if (!FileExists(get_base_dir())) { - UG_WARNING("GridFunctionDebugWriter::write_matrix: directory " - << get_base_dir() << "does not exist."); - UG_WARNING("GridFunctionDebugWriter::write_matrix: using cwd " - "as basedir."); - name = "./"; name.append(filename); - } + std::string name; + this->compose_file_path (name); + name += filename; if ( !FileTypeIs( filename, ".mat" ) ) { UG_THROW( "Only '.mat' format supported for matrices, but" @@ -931,15 +924,9 @@ class GridFunctionDebugWriter: public IDebugWriter PROFILE_FUNC_GROUP("debug"); update_positions(); - std::string name = get_base_dir() + "/" + filename; - - if (!FileExists(get_base_dir())) { - UG_WARNING("GridFunctionDebugWriter::write_vector_to_conn_viewer: directory " - << get_base_dir() << "does not exist."); - UG_WARNING("GridFunctionDebugWriter::write_vector_to_conn_viewer: using cwd " - "as basedir."); - name = "./"; name.append(filename); - } + std::string name; + this->compose_file_path (name); + name += filename; if ( !FileTypeIs( filename, ".vec" ) ) { UG_THROW( "Only '.vec' format supported for vectors, but" @@ -959,15 +946,10 @@ class GridFunctionDebugWriter: public IDebugWriter void write_vector_to_vtk(const vector_type& vec, const char* filename) { PROFILE_FUNC_GROUP("debug"); // check name - std::string name = get_base_dir() + "/" + filename; - - if (!FileExists(get_base_dir())) { - UG_WARNING("GridFunctionDebugWriter::write_vector_to_vtk: directory " - << get_base_dir() << "does not exist."); - UG_WARNING("GridFunctionDebugWriter::write_vector_to_vtk: using cwd " - "as basedir."); - name = "./"; name.append(filename); - } + + std::string name; + this->compose_file_path (name); + name += filename; typedef GridFunction TGridFunction; TGridFunction vtkFunc( diff --git a/ugbase/lib_disc/function_spaces/integrate.h b/ugbase/lib_disc/function_spaces/integrate.h index c258d3e42..7cb6f6665 100644 --- a/ugbase/lib_disc/function_spaces/integrate.h +++ b/ugbase/lib_disc/function_spaces/integrate.h @@ -479,7 +479,7 @@ class UserDataIntegrand SmartPtr > m_spData; // grid function - SmartPtr m_spGridFct; + TGridFunction* m_spGridFct; // time number m_time; @@ -487,7 +487,7 @@ class UserDataIntegrand public: /// constructor UserDataIntegrand(SmartPtr > spData, - SmartPtr spGridFct, + TGridFunction* spGridFct, number time) : m_spData(spData), m_spGridFct(spGridFct), m_time(time) { @@ -499,9 +499,8 @@ class UserDataIntegrand number time) : m_spData(spData), m_spGridFct(NULL), m_time(time) { - if(m_spData->requires_grid_fct()) - UG_THROW("UserDataIntegrand: Missing GridFunction, but " - " data requires grid function.") + UG_COND_THROW(m_spData->requires_grid_fct(), + "UserDataIntegrand: Missing GridFunction, but data requires grid function."); }; /// \copydoc IIntegrand::values @@ -856,14 +855,19 @@ class UserDataDistIntegrandSq template number Integral(SmartPtr > spData, - SmartPtr spGridFct, + TGridFunction& spGridFct, const char* subsets, number time, int quadOrder, std::string quadType) { - UserDataIntegrand spIntegrand(spData, spGridFct, time); - return IntegrateSubsets(spIntegrand, *spGridFct, subsets, quadOrder, quadType); + UserDataIntegrand spIntegrand(spData, &spGridFct, time); + return IntegrateSubsets(spIntegrand, spGridFct, subsets, quadOrder, quadType); } +template +number Integral(SmartPtr > spData, SmartPtr spGridFct, + const char* subsets, number time, int quadOrder, std::string quadType) +{ return Integral(spData, *spGridFct, subsets, time, quadOrder, quadType); } + template number Integral(SmartPtr > spData, SmartPtr spGridFct,const char* subsets,number time, int order) {return Integral(spData, spGridFct, subsets, time, order, "best");} @@ -929,7 +933,7 @@ number Integral(const char* luaFct, static const int dim = TGridFunction::dim; SmartPtr > sp = LuaUserDataFactory::create(luaFct); - return Integral(sp, spGridFct, subsets, time, quadOrder, quadType); + return Integral(sp, *spGridFct, subsets, time, quadOrder, quadType); } template @@ -1069,28 +1073,33 @@ class L2ErrorIntegrand */ template number L2Error(SmartPtr > spExactSol, - SmartPtr spGridFct, const char* cmp, + TGridFunction& gridFct, const char* cmp, number time, int quadOrder, const char* subsets) { // get function id of name - const size_t fct = spGridFct->fct_id_by_name(cmp); + const size_t fct = gridFct.fct_id_by_name(cmp); // check that function exists - if(fct >= spGridFct->num_fct()) - UG_THROW("L2Error: Function space does not contain" - " a function with name " << cmp << "."); + UG_COND_THROW(fct >= gridFct.num_fct(), + "L2Error: Function space does not contain a function with name " << cmp << "."); - L2ErrorIntegrand spIntegrand(spExactSol, *spGridFct, fct, time); - return sqrt(IntegrateSubsets(spIntegrand, *spGridFct, subsets, quadOrder)); + L2ErrorIntegrand spIntegrand(spExactSol, gridFct, fct, time); + return sqrt(IntegrateSubsets(spIntegrand, gridFct, subsets, quadOrder)); } +template +number L2Error(SmartPtr > spExactSol, + SmartPtr spGridFct, const char* cmp, + number time, int quadOrder, const char* subsets) +{ return L2Error(spExactSol, *spGridFct, cmp, time, quadOrder, subsets); } + template number L2Error(SmartPtr > spExactSol, SmartPtr spGridFct, const char* cmp, number time, int quadOrder) -{ - return L2Error(spExactSol, spGridFct, cmp, time, quadOrder, NULL); -} +{ return L2Error(spExactSol, *spGridFct, cmp, time, quadOrder, NULL); } + + #ifdef UG_FOR_LUA @@ -1372,7 +1381,6 @@ number GridFunctionDistance2(TGridFunction& spGridFct1, const char* cmp1, const int level1 = spGridFct1.dof_distribution()->grid_level().level(); const int level2 = spGridFct2.dof_distribution()->grid_level().level(); - // w/ weights if(level1 > level2){ TDistIntegrand spIntegrand(spGridFct1, fct1, spGridFct2, fct2, spWeights); @@ -1381,9 +1389,41 @@ number GridFunctionDistance2(TGridFunction& spGridFct1, const char* cmp1, TDistIntegrand spIntegrand(spGridFct2, fct2, spGridFct1, fct1, spWeights); return IntegrateSubsets(spIntegrand, spGridFct2, subsets, quadOrder); } +} +//! Computes (weighted) distance with shift for averages +template +number GridFunctionDistance2(TGridFunction& spGridFct1, const char* cmp1, + TGridFunction& spGridFct2, const char* cmp2, + int quadOrder, const char* subsets, + ConstSmartPtr spWeights, + number distAvg12) +{ +// get function id of name + const size_t fct1 = spGridFct1.fct_id_by_name(cmp1); + const size_t fct2 = spGridFct2.fct_id_by_name(cmp2); + +// check that function exists + if(fct1 >= spGridFct1.num_fct()) + UG_THROW("GridFunctionDistance: Function space does not contain" + " a function with name " << cmp1 << "."); + if(fct2 >= spGridFct2.num_fct()) + UG_THROW("GridFunctionDistance: Function space does not contain" + " a function with name " << cmp2 << "."); +// get top level of gridfunctions + const int level1 = spGridFct1.dof_distribution()->grid_level().level(); + const int level2 = spGridFct2.dof_distribution()->grid_level().level(); + + // w/ weights + if(level1 > level2){ + TDistIntegrand spIntegrand(spGridFct1, fct1, spGridFct2, fct2, spWeights, distAvg12); + return IntegrateSubsets(spIntegrand, spGridFct1, subsets, quadOrder); + }else{ + TDistIntegrand spIntegrand(spGridFct2, fct2, spGridFct1, fct1, spWeights, -distAvg12); + return IntegrateSubsets(spIntegrand, spGridFct2, subsets, quadOrder); + } } @@ -1510,8 +1550,7 @@ number L2Norm2(TGridFunction& u, const char* cmp, const size_t fct = u.fct_id_by_name(cmp); // check that function exists - if(fct >= u.num_fct()) - UG_THROW("L2Norm: Function space does not contain" + UG_COND_THROW(fct >= u.num_fct(), "L2Norm: Function space does not contain" " a function with name " << cmp << "."); L2Integrand integrandL2(u, fct, spWeight); @@ -1526,8 +1565,7 @@ number L2Norm2(TGridFunction& u, const char* cmp, const size_t fct = u.fct_id_by_name(cmp); // check that function exists - if(fct >= u.num_fct()) - UG_THROW("L2Norm: Function space does not contain" + UG_COND_THROW(fct >= u.num_fct(), "L2Norm: Function space does not contain" " a function with name " << cmp << "."); L2Integrand integrandL2(u, fct); @@ -1583,6 +1621,9 @@ class L2DistIntegrand ConstSmartPtr m_spWeight; + /// shift + double m_deltaFineCoarse; + public: /// constructor (1st is fine grid function) @@ -1590,13 +1631,13 @@ class L2DistIntegrand TGridFunction& coarseGridFct, size_t coarseCmp) : m_fineData(fineGridFct, fineCmp), m_fineTopLevel(fineGridFct.dof_distribution()->grid_level().level()), m_coarseData(coarseGridFct, coarseCmp), m_coarseTopLevel(coarseGridFct.dof_distribution()->grid_level().level()), - m_spMG(m_fineData.domain()->grid()), m_spWeight(make_sp(new ConstUserNumber(1.0))) + m_spMG(m_fineData.domain()->grid()), m_spWeight(make_sp(new ConstUserNumber(1.0))), + m_deltaFineCoarse(0.0) { - if(m_fineTopLevel < m_coarseTopLevel) - UG_THROW("L2DiffIntegrand: fine and top level inverted."); - - if(m_fineData.domain().get() != m_coarseData.domain().get()) - UG_THROW("L2DiffIntegrand: grid functions defined on different domains."); + UG_COND_THROW(m_fineTopLevel < m_coarseTopLevel, + "L2DiffIntegrand: fine and top level inverted."); + UG_COND_THROW(m_fineData.domain().get() != m_coarseData.domain().get(), + "L2DiffIntegrand: grid functions defined on different domains."); }; /// constructor (1st is fine grid function) @@ -1604,26 +1645,39 @@ class L2DistIntegrand TGridFunction& coarseGridFct, size_t coarseCmp, ConstSmartPtr spWeight) : m_fineData(fineGridFct, fineCmp), m_fineTopLevel(fineGridFct.dof_distribution()->grid_level().level()), m_coarseData(coarseGridFct, coarseCmp), m_coarseTopLevel(coarseGridFct.dof_distribution()->grid_level().level()), - m_spMG(m_fineData.domain()->grid()), m_spWeight(spWeight) + m_spMG(m_fineData.domain()->grid()), m_spWeight(spWeight), + m_deltaFineCoarse(0.0) { - if(m_fineTopLevel < m_coarseTopLevel) - UG_THROW("L2DiffIntegrand: fine and top level inverted."); + UG_COND_THROW(m_fineTopLevel < m_coarseTopLevel, + "L2DiffIntegrand: fine and top level inverted."); + UG_COND_THROW(m_fineData.domain().get() != m_coarseData.domain().get(), + "L2DiffIntegrand: grid functions defined on different domains."); + }; - if(m_fineData.domain().get() != m_coarseData.domain().get()) - UG_THROW("L2DiffIntegrand: grid functions defined on different domains."); + /// constructor (1st is fine grid function) + L2DistIntegrand(TGridFunction& fineGridFct, size_t fineCmp, + TGridFunction& coarseGridFct, size_t coarseCmp, ConstSmartPtr spWeight, number dist12) + : m_fineData(fineGridFct, fineCmp), m_fineTopLevel(fineGridFct.dof_distribution()->grid_level().level()), + m_coarseData(coarseGridFct, coarseCmp), m_coarseTopLevel(coarseGridFct.dof_distribution()->grid_level().level()), + m_spMG(m_fineData.domain()->grid()), m_spWeight(spWeight), + m_deltaFineCoarse(dist12) + { + UG_COND_THROW(m_fineTopLevel < m_coarseTopLevel, + "L2DiffIntegrand: fine and top level inverted."); + UG_COND_THROW(m_fineData.domain().get() != m_coarseData.domain().get(), + "L2DiffIntegrand: grid functions defined on different domains."); }; + virtual ~L2DistIntegrand() {} /// sets subset virtual void set_subset(int si) { - if(!m_fineData.is_def_in_subset(si)) - UG_THROW("L2DiffIntegrand: Grid function component" - <::set_subset(si); } @@ -1708,11 +1762,11 @@ class L2DistIntegrand } // get squared of difference - vValue[ip] = fineElemWeights[ip]*(coarseSolIP - fineSolIP)*(coarseSolIP - fineSolIP); + vValue[ip] = fineElemWeights[ip]*(fineSolIP - coarseSolIP -m_deltaFineCoarse)*(fineSolIP-coarseSolIP-m_deltaFineCoarse); } } - UG_CATCH_THROW("L2ErrorIntegrand::evaluate: trial space missing."); + UG_CATCH_THROW("L2DistIntegrand::evaluate: trial space missing."); }; }; @@ -1722,10 +1776,10 @@ template number L2Distance2(TGridFunction& spGridFct1, const char* cmp1, TGridFunction& spGridFct2, const char* cmp2, int quadOrder, const char* subsets, - ConstSmartPtr::weight_type> spWeight) + ConstSmartPtr::weight_type> spWeight, number avgDist12=0.0) { return GridFunctionDistance2, TGridFunction> - (spGridFct1, cmp1, spGridFct2, cmp2, quadOrder, subsets, spWeight); + (spGridFct1, cmp1, spGridFct2, cmp2, quadOrder, subsets, spWeight, avgDist12); } @@ -1803,8 +1857,8 @@ class H1SemiIntegrand /// sets subset virtual void set_subset(int si) { - if(!m_scalarData.is_def_in_subset(si)) - UG_THROW("H1Error: Grid function component" + + UG_COND_THROW(!m_scalarData.is_def_in_subset(si), "H1Error: Grid function component" <::set_subset(si); } @@ -1870,8 +1924,7 @@ class H1SemiIntegrand gridFct.dof_indices(pElem, m_scalarData.fct(), ind); // check multi indices - if(ind.size() != num_sh) - UG_THROW("H1SemiNormFuncIntegrand::evaluate: Wrong number of multi-)indices."); + UG_COND_THROW(ind.size() != num_sh, "H1SemiNormFuncIntegrand::evaluate: Wrong number of multi-)indices."); // loop all integration points std::vector > vLocGradient(num_sh); @@ -1933,8 +1986,7 @@ number H1SemiNorm2(TGridFunction& gridFct, const char* cmp, int quadOrder, const const size_t fct = gridFct.fct_id_by_name(cmp); // check that function exists - if(fct >= gridFct.num_fct()) - UG_THROW("H1SemiNorm: Function space does not contain" + UG_COND_THROW(fct >= gridFct.num_fct(), "H1SemiNorm: Function space does not contain" " a function with name " << cmp << "."); if (weights.invalid()) { H1SemiIntegrand integrand(gridFct, fct); @@ -2023,22 +2075,18 @@ class H1SemiDistIntegrand : public StdIntegrandgrid()), m_spWeight(spWeight) { - if(m_fineTopLevel < m_coarseTopLevel) - UG_THROW("H1SemiDiffIntegrand: fine and top level inverted."); - - if(m_fineData.domain().get() != m_coarseData.domain().get()) - UG_THROW("H1SemiDiffIntegrand: grid functions defined on different domains."); + UG_COND_THROW(m_fineTopLevel < m_coarseTopLevel, "H1SemiDiffIntegrand: fine and top level inverted."); + UG_COND_THROW(m_fineData.domain().get() != m_coarseData.domain().get(), "H1SemiDiffIntegrand: grid functions defined on different domains."); } virtual ~H1SemiDistIntegrand(){} /// sets subset virtual void set_subset(int si) { - if(!m_fineData.is_def_in_subset(si)) - UG_THROW("H1SemiDiffIntegrand: Grid function component" - <::set_subset(si); } @@ -2281,8 +2329,8 @@ class H1EnergyIntegrand /// sets subset virtual void set_subset(int si) { - if(!m_scalarData.is_def_in_subset(si)) - UG_THROW("H1EnergyIntegrand: Grid function component" + + UG_COND_THROW(!m_scalarData.is_def_in_subset(si), "H1EnergyIntegrand: Grid function component" <::set_subset(si); } @@ -2348,8 +2396,7 @@ class H1EnergyIntegrand gridFct.dof_indices(pElem, m_scalarData.fct(), ind); // check multi indices - if(ind.size() != num_sh) - UG_THROW("H1EnergyIntegrand::evaluate: Wrong number of multi-)indices."); + UG_COND_THROW(ind.size() != num_sh, "H1EnergyIntegrand::evaluate: Wrong number of multi-)indices."); // loop all integration points std::vector > vLocGradient(num_sh); @@ -2411,8 +2458,7 @@ number H1EnergyNorm2(TGridFunction& gridFct, const char* cmp, int quadOrder, con const size_t fct = gridFct.fct_id_by_name(cmp); // check that function exists - if(fct >= gridFct.num_fct()) - UG_THROW("H1SemiNorm: Function space does not contain" + UG_COND_THROW(fct >= gridFct.num_fct(), "H1SemiNorm: Function space does not contain" " a function with name " << cmp << "."); if (weights.invalid()) { H1EnergyIntegrand integrand(gridFct, fct); @@ -2780,7 +2826,7 @@ class H1NormIntegrand // compute global gradient MathVector approxGradIP; MathMatrix JTInv; - Inverse(JTInv, vJT[ip]); + RightInverse(JTInv, vJT[ip]); MatVecMult(approxGradIP, JTInv, locTmp); // get squared of difference @@ -2969,7 +3015,7 @@ class H1DistIntegrand // compute global gradient MathVector fineGradIP; MathMatrix fineJTInv; - Inverse(fineJTInv, vJT[ip]); + RightInverse(fineJTInv, vJT[ip]); MatVecMult(fineGradIP, fineJTInv, fineLocTmp); // compute global gradient @@ -3041,21 +3087,23 @@ class StdFuncIntegrand private: // grid function - SmartPtr m_spGridFct; + TGridFunction* m_pGridFct; // component of function const size_t m_fct; public: /// constructor - StdFuncIntegrand(SmartPtr spGridFct, size_t cmp) - : m_spGridFct(spGridFct), m_fct(cmp) + StdFuncIntegrand(TGridFunction* pGridFct, size_t cmp) + : m_pGridFct(pGridFct), m_fct(cmp) {}; + virtual ~StdFuncIntegrand(){} + /// sets subset virtual void set_subset(int si) { - if(!m_spGridFct->is_def_in_subset(m_fct, si)) + if(!m_pGridFct->is_def_in_subset(m_fct, si)) UG_THROW("L2ErrorIntegrand: Grid function component" <::set_subset(si); @@ -3074,7 +3122,7 @@ class StdFuncIntegrand // get reference object id (i.e. Triangle, Quadrilateral, Tetrahedron, ...) ReferenceObjectID roid = (ReferenceObjectID) pElem->reference_object_id(); - const LFEID m_id = m_spGridFct->local_finite_element_id(m_fct); + const LFEID m_id = m_pGridFct->local_finite_element_id(m_fct); try{ // get trial space @@ -3087,12 +3135,11 @@ class StdFuncIntegrand // get multiindices of element std::vector ind; // aux. index array - m_spGridFct->dof_indices(pElem, m_fct, ind); + m_pGridFct->dof_indices(pElem, m_fct, ind); // check multi indices - if(ind.size() != num_sh) - UG_THROW("StdFuncIntegrand::evaluate: Wrong number of" - " multi indices."); + UG_COND_THROW(ind.size() != num_sh, + "StdFuncIntegrand::evaluate: Wrong number of multi indices."); // loop all integration points for(size_t ip = 0; ip < numIP; ++ip) @@ -3104,7 +3151,7 @@ class StdFuncIntegrand { // get value at shape point (e.g. corner for P1 fct) // and add shape fct at ip * value at shape - const number valSH = DoFRef((*m_spGridFct), ind[sh]); + const number valSH = DoFRef((*m_pGridFct), ind[sh]); approxSolIP += valSH * rTrialSpace.shape(sh, vLocIP[ip]); } @@ -3120,7 +3167,7 @@ class StdFuncIntegrand template -number StdFuncIntegralOnVertex(SmartPtr spGridFct, +number StdFuncIntegralOnVertex(TGridFunction& gridFct, size_t fct, int si) { @@ -3133,8 +3180,8 @@ number StdFuncIntegralOnVertex(SmartPtr spGridFct, // note: this iterator is for the base elements, e.g. Face and not // for the special type, e.g. Triangle, Quadrilateral - const_iterator iter = spGridFct->template begin(si); - const_iterator iterEnd = spGridFct->template end(si); + const_iterator iter = gridFct.template begin(si); + const_iterator iterEnd = gridFct.template end(si); // iterate over all elements for(; iter != iterEnd; ++iter) @@ -3143,13 +3190,13 @@ number StdFuncIntegralOnVertex(SmartPtr spGridFct, grid_base_object* pElem = *iter; std::vector ind; // aux. index array - spGridFct->dof_indices(pElem, fct, ind); + gridFct.dof_indices(pElem, fct, ind); // compute approximated solution at integration point number value = 0.0; for(size_t sh = 0; sh < ind.size(); ++sh) { - value += DoFRef((*spGridFct), ind[sh]); + value += DoFRef(gridFct, ind[sh]); } // add to global sum @@ -3161,21 +3208,24 @@ number StdFuncIntegralOnVertex(SmartPtr spGridFct, return integral; } +template +number StdFuncIntegralOnVertex(SmartPtr spGridFct, size_t fct, int si) +{ return StdFuncIntegralOnVertex(*spGridFct, fct, si); } + template -number Integral(SmartPtr spGridFct, const char* cmp, +number Integral(TGridFunction& gridFct, const char* cmp, const char* subsets, int quadOrder) { // get function id of name - const size_t fct = spGridFct->fct_id_by_name(cmp); + const size_t fct = gridFct.fct_id_by_name(cmp); // check that function exists - if(fct >= spGridFct->num_fct()) - UG_THROW("L2Norm: Function space does not contain" + UG_COND_THROW(fct >= gridFct.num_fct(), "L2Norm: Function space does not contain" " a function with name " << cmp << "."); // read subsets - SubsetGroup ssGrp(spGridFct->domain()->subset_handler()); + SubsetGroup ssGrp(gridFct.domain()->subset_handler()); if(subsets != NULL) { ssGrp.add(TokenizeString(subsets)); @@ -3199,7 +3249,7 @@ number Integral(SmartPtr spGridFct, const char* cmp, { number value = 0; for(size_t s = 0; s < ssGrp.size(); ++s) - value += StdFuncIntegralOnVertex(spGridFct, fct, ssGrp[s]); + value += StdFuncIntegralOnVertex(gridFct, fct, ssGrp[s]); #ifdef UG_PARALLEL // sum over processes @@ -3213,10 +3263,16 @@ number Integral(SmartPtr spGridFct, const char* cmp, return value; } - StdFuncIntegrand spIntegrand(spGridFct, fct); - return IntegrateSubsets(spIntegrand, *spGridFct, subsets, quadOrder); + StdFuncIntegrand integrand(&gridFct, fct); + return IntegrateSubsets(integrand, gridFct, subsets, quadOrder); } +template +number Integral(SmartPtr spGridFct, const char* cmp, + const char* subsets, int quadOrder) +{ return Integral(*spGridFct, cmp, subsets, quadOrder); } + + template number Integral(SmartPtr spGridFct, const char* cmp, const char* subsets) @@ -3576,7 +3632,7 @@ number IntegralNormalComponentOnManifold( int quadOrder) { SmartPtr, TGridFunction::dim> > spIntegrand - = make_sp(new UserDataIntegrand, TGridFunction>(spData, spGridFct, time)); + = make_sp(new UserDataIntegrand, TGridFunction>(spData, &(*spGridFct), time)); return IntegralNormalComponentOnManifoldSubsets(spIntegrand, spGridFct, BndSubset, InnerSubset, quadOrder); } diff --git a/ugbase/lib_disc/function_spaces/metric_spaces.h b/ugbase/lib_disc/function_spaces/metric_spaces.h index 3bc341dce..05068b7fb 100644 --- a/ugbase/lib_disc/function_spaces/metric_spaces.h +++ b/ugbase/lib_disc/function_spaces/metric_spaces.h @@ -313,6 +313,81 @@ class L2ComponentSpace : }; +/** Evaluates difference between two grid functions in L2 norm */ +template +class L2QuotientSpace : + public IComponentSpace, + public IObjectWithWeights::weight_type > +{ +public: + typedef IComponentSpace base_type; + typedef typename L2DistIntegrand::weight_type weight_type; + typedef IObjectWithWeights weighted_obj_type; + + /// CTOR + L2QuotientSpace(const char *fctNames) + : base_type(fctNames), weighted_obj_type(make_sp(new ConstUserNumber(1.0))) {}; + + L2QuotientSpace(const char *fctNames, int order) + : base_type(fctNames, order), weighted_obj_type(make_sp(new ConstUserNumber(1.0))) {}; + + L2QuotientSpace(const char *fctNames, int order, double weight, const char* ssNames=0) + : base_type(fctNames, ssNames, order), weighted_obj_type(make_sp(new ConstUserNumber(weight))) {}; + + L2QuotientSpace(const char *fctNames, int order, ConstSmartPtr spWeight, const char* ssNames=0) + : base_type(fctNames, ssNames, order), weighted_obj_type(spWeight) {}; + + /// DTOR + ~L2QuotientSpace() {}; + + using IComponentSpace::norm; + using IComponentSpace::distance; + + /// for weighted norms + using weighted_obj_type::set_weight; + using weighted_obj_type::get_weight; + using weighted_obj_type::m_spWeight; + + /// \copydoc IComponentSpace::norm + double norm2(TGridFunction& u) + { + typedef ConstUserNumber MyConstUserData; + typedef SmartPtr > SPUserData; + + SPUserData spConst= make_sp(new MyConstUserData(1.0)); + number Meas = Integral(spConst, u, base_type::m_ssNames, 0.0, 1, "best"); + number uAvg = Integral(u, base_type::m_fctNames.c_str(), base_type::m_ssNames, base_type::m_quadorder); + + std::cerr << "Average:=" << uAvg <<"/" << Meas << " = " << uAvg/Meas << std::endl; + SPUserData spAvg = make_sp(new MyConstUserData(uAvg/Meas)); + double qnorm = L2Error(spAvg, u, base_type::m_fctNames.c_str(), 0.0, base_type::m_quadorder, base_type::m_ssNames); + + return qnorm*qnorm; + } + + /// \copydoc IComponentSpace::distance + double distance2(TGridFunction& uFine, TGridFunction& uCoarse) + { + typedef ConstUserNumber MyConstUserData; + typedef SmartPtr > SPUserData; + + SPUserData spConst= make_sp(new MyConstUserData(1.0)); + number Meas = Integral(spConst, uFine, base_type::m_ssNames, 0.0, 1, "best"); + number avgFine = Integral(uFine, base_type::m_fctNames.c_str(), base_type::m_ssNames, base_type::m_quadorder); + number avgCoarse = Integral(uCoarse, base_type::m_fctNames.c_str(), base_type::m_ssNames, base_type::m_quadorder); + + std::cerr << "Average:=(" << avgFine << "-" << avgCoarse<<")/" << Meas << " = " << (avgFine-avgCoarse)/Meas << std::endl; + return L2Distance2(uFine, base_type::m_fctNames.c_str(), + uCoarse, base_type::m_fctNames.c_str(), + base_type::m_quadorder, base_type::m_ssNames, weighted_obj_type::m_spWeight, + (avgFine-avgCoarse)/Meas); + } + + + +}; + + /** Evaluates distance between two grid functions in H1 semi-norm */ template class H1SemiComponentSpace @@ -519,11 +594,11 @@ class H1ComponentSpace : /// \copydoc IComponentSpace::norm double norm2(TGridFunction& uFine) - { return H1Norm2(uFine, base_type::m_fctNames.c_str(), base_type::m_quadorder); } + { return H1Norm2(uFine, base_type::m_fctNames.c_str(), base_type::m_quadorder, base_type::m_ssNames); } /// \copydoc IComponentSpace::norm double distance2(TGridFunction& uFine, TGridFunction& uCoarse) - { return H1Distance2(uFine, base_type::m_fctNames.c_str(), uCoarse, base_type::m_fctNames.c_str(), base_type::m_quadorder); } + { return H1Distance2(uFine, base_type::m_fctNames.c_str(), uCoarse, base_type::m_fctNames.c_str(), base_type::m_quadorder, base_type::m_ssNames); } }; diff --git a/ugbase/lib_disc/io/vtkoutput.cpp b/ugbase/lib_disc/io/vtkoutput.cpp index ce64ce8a6..16a290281 100644 --- a/ugbase/lib_disc/io/vtkoutput.cpp +++ b/ugbase/lib_disc/io/vtkoutput.cpp @@ -31,6 +31,9 @@ */ #include "vtkoutput.h" + +#include "common/util/os_info.h" // for GetPathSeparator + #include namespace ug{ @@ -182,9 +185,9 @@ void baseName(std::string& nameOut, const std::string& nameIn) // This will treat the two example cases above (on Unix systems). // A proper solution would use boost::filesystem and throw away the file extension // if present. However, this would require linking against a boost library. - size_t lo_slash = nameIn.find_last_of('/'); + size_t lo_pathSep = nameIn.rfind(GetPathSeparator()); size_t lo_dot = nameIn.find_last_of('.'); - if (lo_slash == std::string::npos || lo_slash < lo_dot) + if (lo_pathSep == std::string::npos || lo_pathSep < lo_dot) nameOut = nameIn.substr(0, lo_dot); else nameOut = nameIn; diff --git a/ugbase/lib_disc/operator/linear_operator/multi_grid_solver/mg_solver.h b/ugbase/lib_disc/operator/linear_operator/multi_grid_solver/mg_solver.h index a14f83c73..a7f793f32 100644 --- a/ugbase/lib_disc/operator/linear_operator/multi_grid_solver/mg_solver.h +++ b/ugbase/lib_disc/operator/linear_operator/multi_grid_solver/mg_solver.h @@ -248,6 +248,9 @@ class AssembledMultiGridCycle : virtual bool ignore_init_for_base_solver() const; /** \\ */ + /// reinit transfer operators + void force_reinit(); + /// Compute new correction c = B*d virtual bool apply(vector_type& c, const vector_type& d); @@ -442,6 +445,9 @@ class AssembledMultiGridCycle : /// missing coarse grid correction matrix_type RimCpl_Coarse_Fine; + + /// debugging output information (number of calls of the pre-, postsmoothers, base solver etc) + int n_pre_calls, n_post_calls, n_base_calls, n_restr_calls, n_prolong_calls; }; /// storage for all level @@ -519,8 +525,8 @@ class AssembledMultiGridCycle : * \param[in] spGF Level Vector to write for debug purpose * \param[in] name Filename */ - void write_debug(ConstSmartPtr spGF, std::string name); - void write_debug(const GF& rGF, std::string name); + inline void write_debug(ConstSmartPtr spGF, std::string name, int cycleNo = -1); + void write_debug(const GF& rGF, std::string name, int cycleNo = -1); /// writes debug output for a level matrix only on smooth path /** @@ -536,9 +542,15 @@ class AssembledMultiGridCycle : void write_debug(const matrix_type& mat, std::string name, const GF& rTo, const GF& rFrom); /// \} + + /// enters a new debugger section for smoothers, base solver etc + void enter_debug_writer_section(GridLevel& orig_gl, const char * sec_name, int lev, int cycleNo = -1, int callNo = -1); + + /// leaves the current debugger section + void leave_debug_writer_section(GridLevel& orig_gl); /// logs a level-data-struct to the terminal - void log_debug_data(int lvl, std::string name); + void log_debug_data(int lvl, int cycleNo, std::string name); /// Calls MGStats::set_defect (if available) with the given parameters void mg_stats_defect(GF& gf, int lvl, typename mg_stats_type::Stage stage); diff --git a/ugbase/lib_disc/operator/linear_operator/multi_grid_solver/mg_solver_impl.hpp b/ugbase/lib_disc/operator/linear_operator/multi_grid_solver/mg_solver_impl.hpp index f2382e817..2ccad9c48 100644 --- a/ugbase/lib_disc/operator/linear_operator/multi_grid_solver/mg_solver_impl.hpp +++ b/ugbase/lib_disc/operator/linear_operator/multi_grid_solver/mg_solver_impl.hpp @@ -192,7 +192,13 @@ apply(vector_type& rC, const vector_type& rD) // debug output write_debug(d, "Defect_In"); write_debug(*m_spSurfaceMat, "SurfaceStiffness", c, c); - + for(int lev = m_baseLev; lev <= m_topLev; ++lev) + { + LevData& ld = *m_vLevData[lev]; + ld.n_pre_calls = ld.n_post_calls = ld.n_base_calls + = ld.n_restr_calls = ld.n_prolong_calls = 0; + } + // project defect from surface to level GMG_PROFILE_BEGIN(GMG_Apply_CopyDefectFromSurface); try{ @@ -503,6 +509,14 @@ ignore_init_for_base_solver() const } +template +void AssembledMultiGridCycle:: +force_reinit() +{ + m_ApproxSpaceRevision.invalidate(); +} + + template void AssembledMultiGridCycle:: assemble_level_operator() @@ -1092,8 +1106,10 @@ init_projection() { LevData& lf = *m_vLevData[lev]; LevData& lc = *m_vLevData[lev-1]; + GridLevel gw_gl; enter_debug_writer_section(gw_gl, "ProjectionInit", lev); lf.Projection->set_levels(lc.st->grid_level(), lf.t->grid_level()); lf.Projection->init(); + leave_debug_writer_section(gw_gl); } UG_DLOG(LIB_DISC_MULTIGRID, 3, "gmg-stop init_projection\n"); @@ -1113,16 +1129,20 @@ init_smoother() UG_DLOG(LIB_DISC_MULTIGRID, 4, " init_smoother: initializing pre-smoother on lev "<init(ld.A, *ld.sc);} UG_CATCH_THROW("GMG::init: Cannot init pre-smoother for level "<init(ld.A, *ld.sc);} UG_CATCH_THROW("GMG::init: Cannot init post-smoother for level "<init(spGatheredBaseMat, *ld.t)) UG_THROW("GMG::init: Cannot init base solver on baselevel "<< m_baseLev); + leave_debug_writer_section(gw_gl); } else { @@ -1180,8 +1202,10 @@ init_base_solver() if(!m_pSurfaceSol) *ld.st = 0; + GridLevel gw_gl; enter_debug_writer_section(gw_gl, "BaseSolverInit", m_baseLev); if(!m_spBaseSolver->init(ld.A, *ld.st)) UG_THROW("GMG::init: Cannot init base solver on baselevel "<< m_baseLev); + leave_debug_writer_section(gw_gl); } UG_DLOG(LIB_DISC_MULTIGRID, 3, "gmg-stop init_base_solver\n"); @@ -1650,7 +1674,7 @@ presmooth_and_restriction(int lev) LevData& lc = *m_vLevData[lev-1]; UG_DLOG(LIB_DISC_MULTIGRID, 3, "gmg-start - presmooth on level "<apply(*lf.st, *lf.sd)) UG_THROW("GMG: Smoothing step "<do_restrict(*lc.sd, *spD); @@ -1764,6 +1792,8 @@ presmooth_and_restriction(int lev) for(size_t i = 0; i < m_vspRestrictionPostProcess.size(); ++i) m_vspRestrictionPostProcess[i]->post_process(lc.sd); GMG_PROFILE_END(); + leave_debug_writer_section(gw_gl); + lf.n_restr_calls++; UG_DLOG(LIB_DISC_MULTIGRID, 3, "gmg-stop - restriction on level "< m_LocalFullRefLevel) @@ -1801,9 +1831,10 @@ prolongation_and_postsmooth(int lev) lc.RimCpl_Fine_Coarse.matmul_minus(*lf.sd, *lc.sc); GMG_PROFILE_END(); } - log_debug_data(lev, "AfterCoarseGridDefect"); + log_debug_data(lev, lf.n_prolong_calls, "AfterCoarseGridDefect"); // PROLONGATE: + GridLevel gw_gl; enter_debug_writer_section(gw_gl, "Prolongation", lev, lf.n_prolong_calls); SmartPtr spT = lf.st; #ifdef UG_PARALLEL if( !lf.t->layouts()->vertical_slave().empty() || @@ -1849,6 +1880,8 @@ prolongation_and_postsmooth(int lev) for(size_t i = 0; i < m_vspProlongationPostProcess.size(); ++i) m_vspProlongationPostProcess[i]->post_process(lf.st); GMG_PROFILE_END(); + + leave_debug_writer_section(gw_gl); // add coarse grid correction: c := c + t GMG_PROFILE_BEGIN(GMG_AddCoarseGridCorrection); @@ -1857,7 +1890,7 @@ prolongation_and_postsmooth(int lev) UG_DLOG(LIB_DISC_MULTIGRID, 3, "gmg-stop - prolongation on level "<apply_sub(*lf.sd, *lf.st); if(nu == 0){ - log_debug_data(lev, "BeforePostSmooth"); + log_debug_data(lev, lf.n_prolong_calls, "BeforePostSmooth"); mg_stats_defect(*lf.sd, lev, mg_stats_type::BEFORE_POST_SMOOTH); } // a) Compute t = B*d with some iterator B + GridLevel gw_gl; enter_debug_writer_section(gw_gl, "PostSmoother", lev, lf.n_post_calls, nu); if(!lf.PostSmoother->apply(*lf.st, *lf.sd)) UG_THROW("GMG: Smoothing step "<apply(*ld.sc, *ld.sd)) UG_THROW("GMG::lmgc: Base solver on base level "<set(0.0); // compute coarse correction + GridLevel gw_gl; enter_debug_writer_section(gw_gl, "BaseSolver", lev, ld.n_base_calls); try{ if(!m_spBaseSolver->apply(*spGatheredBaseCorr, *ld.t)) UG_THROW("GMG::lmgc: Base solver on base level "< void AssembledMultiGridCycle:: -write_debug(ConstSmartPtr spGF, std::string name) +write_debug(ConstSmartPtr spGF, std::string name, int cycleNo) { - write_debug(*spGF, name); + write_debug(*spGF, name, cycleNo); } template void AssembledMultiGridCycle:: -write_debug(const GF& rGF, std::string name) +write_debug(const GF& rGF, std::string name, int cycleNo) { PROFILE_FUNC_GROUP("debug"); @@ -2098,7 +2141,9 @@ write_debug(const GF& rGF, std::string name) GridLevel gl = rGF.grid_level(); std::stringstream ss; ss << "GMG_" << name << GridLevelAppendix(gl); - ss << "_i" << std::setfill('0') << std::setw(3) << m_dbgIterCnt << ".vec"; + ss << "_i" << std::setfill('0') << std::setw(3) << m_dbgIterCnt; + if (cycleNo >= 0) ss << "_cycle" << std::setfill('0') << std::setw(3) << cycleNo; + ss << ".vec"; // write GridLevel currGL = m_spDebugWriter->grid_level(); @@ -2137,15 +2182,44 @@ write_debug(const matrix_type& mat, std::string name, const GridLevel& glTo, con m_spDebugWriter->set_grid_level(currGL); } +template +inline void AssembledMultiGridCycle:: +enter_debug_writer_section(GridLevel& dw_orig_gl, const char * sec_name, int lev, int cycleNo, int callNo) +{ + PROFILE_FUNC_GROUP("debug"); + + if(m_spDebugWriter.invalid()) return; + + dw_orig_gl = m_spDebugWriter->grid_level(); + m_spDebugWriter->set_grid_level(m_vLevData[lev]->sd->grid_level()); + + std::stringstream ss; + ss << "GMG_" << sec_name; + ss << "_i" << std::setfill('0') << std::setw(3) << m_dbgIterCnt; + if (cycleNo >= 0) ss << "_cycle" << std::setfill('0') << std::setw(3) << cycleNo; + ss << "_l" << lev; + if (callNo >= 0) ss << "_call" << std::setfill('0') << std::setw(3) << callNo; + m_spDebugWriter->enter_section(ss.str().c_str()); +} + +template +inline void AssembledMultiGridCycle:: +leave_debug_writer_section(GridLevel& dw_orig_gl) +{ + if(m_spDebugWriter.invalid()) return; + m_spDebugWriter->leave_section(); + m_spDebugWriter->set_grid_level(dw_orig_gl); +} + template void AssembledMultiGridCycle:: -log_debug_data(int lvl, std::string name) +log_debug_data(int lvl, int cycleNo, std::string name) { if(m_spDebugWriter.valid()){ std::string defName("Def_"); defName.append(name); std::string curName("Cor_"); curName.append(name); - write_debug(m_vLevData[lvl]->sd, defName); - write_debug(m_vLevData[lvl]->sc, curName); + write_debug(m_vLevData[lvl]->sd, defName, cycleNo); + write_debug(m_vLevData[lvl]->sc, curName, cycleNo); } const bool bEnableSerialNorm = false; diff --git a/ugbase/lib_disc/operator/linear_operator/nested_iteration/nested_iteration_impl.h b/ugbase/lib_disc/operator/linear_operator/nested_iteration/nested_iteration_impl.h index 653fc5de8..1ee671553 100644 --- a/ugbase/lib_disc/operator/linear_operator/nested_iteration/nested_iteration_impl.h +++ b/ugbase/lib_disc/operator/linear_operator/nested_iteration/nested_iteration_impl.h @@ -195,6 +195,8 @@ number NestedIterationSolver::coarsen_domain(const grid_functi SmartPtr spDD=u.dof_distribution(); m_spRefiner->mark(spDD->begin(), spDD->end(), RM_COARSEN); m_spRefiner->coarsen();*/ + + return 0; // dummy value } diff --git a/ugbase/lib_disc/operator/linear_operator/subspace_correction/sequential_subspace_correction.h b/ugbase/lib_disc/operator/linear_operator/subspace_correction/sequential_subspace_correction.h new file mode 100644 index 000000000..490a5c2a3 --- /dev/null +++ b/ugbase/lib_disc/operator/linear_operator/subspace_correction/sequential_subspace_correction.h @@ -0,0 +1,763 @@ +/* + * Copyright (c) 2018: G-CSC, Goethe University Frankfurt + * Authors: Arne Naegel + * + * This file is part of UG4. + * + * UG4 is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 3 (as published by the + * Free Software Foundation) with the following additional attribution + * requirements (according to LGPL/GPL v3 §7): + * + * (1) The following notice must be displayed in the Appropriate Legal Notices + * of covered and combined works: "Based on UG4 (www.ug4.org/license)". + * + * (2) The following notice must be displayed at a prominent place in the + * terminal output of covered works: "Based on UG4 (www.ug4.org/license)". + * + * (3) The following bibliography is recommended for citation and must be + * preserved in all covered files: + * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively + * parallel geometric multigrid solver on hierarchically distributed grids. + * Computing and visualization in science 16, 4 (2013), 151-164" + * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel + * flexible software system for simulating pde based models on high performance + * computers. Computing and visualization in science 16, 4 (2013), 165-179" + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ + +/* + * Remark: Based on implementation of element/component Seidel + */ + +#ifndef __H__UG__LIB_DISC__OPERATOR__LINEAR_OPERATOR__SEQUENTIAL_SSC__ +#define __H__UG__LIB_DISC__OPERATOR__LINEAR_OPERATOR__SEQUENTIAL_SSC__ + +#include "lib_algebra/operator/interface/preconditioner.h" + +#include +#include + +#ifdef UG_PARALLEL + #include "pcl/pcl_util.h" + #include "lib_algebra/parallelization/parallelization_util.h" + #include "lib_algebra/parallelization/parallelization.h" + #include "lib_algebra/parallelization/matrix_overlap.h" + #include "lib_algebra/parallelization/parallel_matrix_overlap_impl.h" +#endif + +namespace ug{ + + +//! Abstract definition for subspace V_k +template +class ILocalSubspace +{ +public: + /// Algebra type + typedef TAlgebra algebra_type; + + /// Vector type + typedef typename TAlgebra::vector_type vector_type; + + /// Matrix type + typedef typename TAlgebra::matrix_type matrix_type; + + typedef DenseVector< VariableArray1 > vector_type_local; + typedef DenseMatrix< VariableArray2 > matrix_type_local; + + /// virtual DTOR + virtual ~ILocalSubspace(){} + + /// Called once. + virtual bool preprocess(const vector_type &c) {return true;} + + /// Extract local data (based on group obj) + virtual void init(TGroupObj*, const vector_type &) = 0; + + /// Extract matrix (on local index set) + virtual void extract_matrix(const matrix_type &A) = 0; + + /// Extract rhs (on local index set) for parallel subspace correction + virtual void extract_rhs(const vector_type &d) = 0; + + /// Extract rhs (on local index set) for sequential subspace correction + virtual void extract_rhs(const vector_type &d, const matrix_type &A, const vector_type &c) = 0; + + /// u = u + omega*ck + virtual void update_solution(vector_type &u, double omega=1.0) = 0; + + virtual size_t size() { return 0; } +}; + + + +//! Abstract definition for subspace V_k +template +class LocalIndexSubspace : public ILocalSubspace +{ +public: + /// Algebra type + typedef TAlgebra algebra_type; + + /// Vector type + typedef typename TAlgebra::vector_type vector_type; + + /// Matrix type + typedef typename TAlgebra::matrix_type matrix_type; + + /// Local data + typedef std::vector index_vector; + typedef DenseVector< VariableArray1 > vector_type_local; + typedef DenseMatrix< VariableArray2 > matrix_type_local; + + /// virtual DTOR + virtual ~LocalIndexSubspace(){} + + /// Called once. + virtual bool preprocess(const vector_type &c) {return true;} + + /// Extract local data (based on group obj) + virtual void init(TGroupObj*, const vector_type &) = 0; + + /// Extract matrix (on local index set) + virtual void extract_matrix(const matrix_type &A) + { + typedef typename TAlgebra::matrix_type::const_row_iterator const_row_iterator; + const static int blockSize = TAlgebra::blockSize; + const size_t numIndex = this->size(); + + // fill local block matrix + bool bFound; + m_Aloc.resize(numIndex, numIndex); + m_Aloc = 0.0; + for (size_t j = 0; jsize(); + const static int blockSize = TAlgebra::blockSize; + + // compute s[j] := d[j] - sum_k A(j,k)*c[k] + // note: the loop over k is the whole matrix row (not only selected indices) + m_dloc.resize(numIndex); + for (size_t j = 0; jsize(); + + // compute s[j] := d[j] - sum_k A(j,k)*c[k] + // note: the loop over k is the whole matrix row (not only selected indices) + m_dloc.resize(numIndex); + for (size_t j = 0; jsize(); + + // solve block + m_uloc.resize(numIndex); + InverseMatMult(m_uloc, 1.0, m_Aloc, m_dloc); + for (size_t j=0;j +class LocalDoFSubspace : public ILocalSubspace +{ +public: + /// Algebra type + typedef TAlgebra algebra_type; + + /// Vector type + typedef typename TAlgebra::vector_type vector_type; + + /// Matrix type + typedef typename TAlgebra::matrix_type matrix_type; + + /// Local data + typedef std::vector index_vector; + typedef DenseVector< VariableArray1 > vector_type_local; + typedef DenseMatrix< VariableArray2 > matrix_type_local; + + /// virtual DTOR + virtual ~LocalDoFSubspace(){} + + /// Called once. + virtual bool preprocess(const vector_type &c) {return true;} + + /// Extract local data (based on group obj) + virtual void init(TGroupObj*, const vector_type &) = 0; + + /// Extract matrix (on local index set) + virtual void extract_matrix(const matrix_type &A) + { + typedef typename TAlgebra::matrix_type::const_row_iterator const_row_iterator; + // const static int blockSize = TAlgebra::blockSize; + const size_t numIndex = this->size(); + + // fill local block matrix + m_Aloc.resize(numIndex, numIndex); + m_Aloc = 0.0; + for (size_t j = 0; jsize(); + m_dloc.resize(numIndex); + + for (size_t j = 0; jsize(); + + // compute s[j] := d[j] - sum_k A(j,k)*c[k] + // note: the loop over k is the whole matrix row (not only selected indices) + // (code taken from component Gauss-Seidel) + m_dloc.resize(numIndex); + for (size_t j = 0; jsize(); + + // solve block + m_uloc.resize(numIndex); + InverseMatMult(m_uloc, 1.0, m_Aloc, m_dloc); + for (size_t j=0;j m_vInd; + + // Memory for local algebra + vector_type_local m_uloc; + vector_type_local m_dloc; + matrix_type_local m_Aloc; + +}; + + + +//! Collects indices on all elements with v \in Vtx(elem) +template +class VertexBasedSubspace : public LocalIndexSubspace +{ +public: + /// Algebra type + typedef TAlgebra algebra_type; + + /// Vector type + typedef typename TAlgebra::vector_type vector_type; + + /// Matrix type + typedef typename TAlgebra::matrix_type matrix_type; + + /// Base type + typedef LocalIndexSubspace base_type; + + /// CTOR + VertexBasedSubspace() {} + + /// virtual DTOR + virtual ~VertexBasedSubspace(){} + + /// Extract indices for local DoFs. + void init(Vertex *groupObj, const vector_type &cvec) + { + // We will modify index list of base class. + std::vector &vInd = base_type::m_vInd; + vInd.clear(); + + // Union of elements (associated with grouping object). + typedef GridFunction TGridFunction; + typedef typename GridFunction::element_type TElement; + const TGridFunction *c = dynamic_cast (&cvec); // Need a grid function here! + std::vector vElem; + c->collect_associated(vElem, groupObj); + + // Union of algebraic indices. + for(size_t i = 0; i < vElem.size(); ++i) + { + c->algebra_indices(vElem[i], vInd, false); + } + + // Remove dublicates. + if(vElem.size() > 1) + { + std::sort(vInd.begin(), vInd.end()); + vInd.erase(std::unique(vInd.begin(), vInd.end()), vInd.end()); + } + } + +}; + + +//! Collects indices on all elements with v \in Vtx(elem) +template +class VertexCenteredVankaSubspace : public LocalDoFSubspace +{ +public: + /// Algebra type + typedef TAlgebra algebra_type; + + /// Vector type + typedef typename TAlgebra::vector_type vector_type; + + /// Matrix type + typedef typename TAlgebra::matrix_type matrix_type; + + /// Base type + typedef LocalDoFSubspace base_type; + + /// CTOR + VertexCenteredVankaSubspace(const std::vector &vVtxCmp, const std::vector &vElemCmp) + : m_vVtxCmp(vVtxCmp), m_vElemCmp(vElemCmp) {} + + /// virtual DTOR + virtual ~VertexCenteredVankaSubspace(){} + + /// Extracts function IDs. + bool preprocess(const vector_type &cvec) + { + typedef GridFunction TGridFunction; + const TGridFunction *c = dynamic_cast (&cvec); // Need a grid function here! + UG_COND_THROW(c==NULL, "Requiring a grid function here!"); + + ConstSmartPtr ddinfo = + c->approx_space()->dof_distribution_info(); + UG_COND_THROW(ddinfo.invalid(), "Requiring valid ddinfo!"); + + // Vertex functions + m_vVtxFct.reserve(m_vVtxCmp.size()); + m_vVtxFct.clear(); + for(size_t i = 0; i < m_vVtxCmp.size(); ++i) + m_vVtxFct.push_back(ddinfo->fct_id_by_name(m_vVtxCmp[i].c_str())); + + // Element functions. + m_vElemFct.reserve(m_vElemCmp.size()); + m_vElemFct.clear(); + for(size_t i = 0; i < m_vElemCmp.size(); ++i) + m_vElemFct.push_back(ddinfo->fct_id_by_name(m_vElemCmp[i].c_str())); + + return true; + } + + /// Extract indices for local DoFs. + void init(Vertex *groupObj, const vector_type &cvec) + { + // We will modify index list of base class. + typename base_type::index_vector &vInd = base_type::m_vInd; + vInd.clear(); + + // Union of elements (associated with grouping object). + typedef GridFunction TGridFunction; + typedef typename GridFunction::element_type TElement; + const TGridFunction *c = dynamic_cast (&cvec); // Need a grid function here! + std::vector vElem; + c->collect_associated(vElem, groupObj); + + // Collect associated indices. + for(size_t i = 0; i < vElem.size(); ++i) + { + for(size_t f = 0; f < m_vElemFct.size(); ++f) + c->dof_indices(vElem[i], m_vElemFct[f], vInd, false, false); + } + + // Collect vertex indices. + for(size_t f = 0; f < m_vVtxFct.size(); ++f) + c->dof_indices(groupObj, m_vVtxFct[f], vInd, false, false); + + // Remove dublicates. + if(vElem.size() > 1) + { + std::sort(vInd.begin(), vInd.end()); + vInd.erase(std::unique(vInd.begin(), vInd.end()), vInd.end()); + } + + /* + UG_DLOG("VertexBasedVankaSubspace [for " << groupObj << "]:") + for (typename base_type::index_vector::iterator it = vInd.begin() ; it != vInd.end(); ++it) + { UG_DLOG(*it <<","); } + + UG_DLOG(std::endl); + */ + } + + + +protected: + std::vector m_vVtxCmp; // primary (vertex) components + std::vector m_vElemCmp; // secondary (element) components + + std::vector m_vVtxFct; // mapping to fct IDs + std::vector m_vElemFct; // mapping to fct IDs + +}; + + +/* +//! Correction for single subspace +template +class LocalSubspaceCorrection : + public ILinearIterator, + public DebugWritingObject +{ +public: + /// Algebra type + typedef TAlgebra algebra_type; + + /// Vector type + typedef typename TAlgebra::vector_type vector_type; + + /// Matrix type + typedef typename TAlgebra::matrix_type matrix_type; + + + typedef MatrixOperator TLinearOperator; + + virtual ~LocalSubspaceCorrection() {} + + /// returns the name of iterator + virtual const char* name() const = 0; + + /// returns if parallel solving is supported + bool supports_parallel() {return false}; + + /// initialize for operator J(u) and linearization point u + bool init(SmartPtr J, const vector_type& u) { + + }; + + /// initialize for linear operator L + virtual bool init(SmartPtr L) = 0; + + /// Compute new correction c = B*d (w/o updating the defect). + virtual bool apply(vector_type& c, const vector_type& d) + { + + } + + /// compute new correction c = B*d and update defect d := d - A*c + virtual bool apply_update_defect(vector_type& c, vector_type& d) + { + + } + +}; + + +*/ + + + +template +void ParallelSubspaceCorrectionLoop(const typename TAlgebra::matrix_type& A, + GridFunction& c, + const typename TAlgebra::vector_type& d, + number omega_relax, + ILocalSubspace &subspace, + typename GridFunction::template traits::const_iterator objIterBegin, + typename GridFunction::template traits::const_iterator objIterEnd +) +{ + // Loop over all grouping objects. + typedef typename GridFunction::template traits::const_iterator GroupObjIter; + for(GroupObjIter iter = objIterBegin; iter != objIterEnd; ++iter) + { + // Apply subspace correction (w.r.t. obj.) + TGroupObj* groupObj = *iter; + subspace.init(groupObj, c); + subspace.extract_matrix(A); + subspace.extract_rhs(d); // w/o updates in c => parallel + subspace.update_solution(c, omega_relax); + } +} + +template +void SequentialSubspaceCorrectionLoop(const typename TAlgebra::matrix_type& A, + GridFunction& c, + const typename TAlgebra::vector_type& d, + number omega_relax, + ILocalSubspace &subspace, + typename GridFunction::template traits::const_iterator objIterBegin, + typename GridFunction::template traits::const_iterator objIterEnd +) +{ + // Loop over all grouping objects. + typedef typename GridFunction::template traits::const_iterator GroupObjIter; + for(GroupObjIter iter = objIterBegin; iter != objIterEnd; ++iter) + { + // Apply subspace correction (w.r.t. obj.) + TGroupObj* groupObj = *iter; + subspace.init(groupObj, c); + subspace.extract_matrix(A); + subspace.extract_rhs(d,A,c); // w/ updates for c => sequential + subspace.update_solution(c, omega_relax); + } +} + +/// Sequential subspace correction preconditioner +template +class SequentialSubspaceCorrection : public IPreconditioner +{ +public: + /// Algebra type + typedef TAlgebra algebra_type; + + /// Vector type + typedef typename TAlgebra::vector_type vector_type; + + /// Matrix type + typedef typename TAlgebra::matrix_type matrix_type; + + /// Matrix Operator type + typedef typename IPreconditioner::matrix_operator_type matrix_operator_type; + + /// Base type + typedef IPreconditioner base_type; + +protected: + using base_type::set_debug; + using base_type::debug_writer; + using base_type::write_debug; + + typedef GridFunction grid_function_type; + +public: + /// default constructor + SequentialSubspaceCorrection() : m_relax(1.0), m_type("vertex") {}; + + /// constructor setting relaxation + SequentialSubspaceCorrection(number relax) : m_relax(relax), m_type("vertex") {}; + + + /// Clone + virtual SmartPtr > clone() + { + SmartPtr > + newInst(new SequentialSubspaceCorrection()); + newInst->set_debug(debug_writer()); + newInst->set_damp(this->damping()); + newInst->set_relax(m_relax); + newInst->set_type(m_type); + newInst->set_vertex_subspace(m_spVertexSubspace); + return newInst; + } + + /// Destructor + virtual ~SequentialSubspaceCorrection() + {}; + + /// returns if parallel solving is supported + virtual bool supports_parallel() const {return true;} + + /// set relaxation parameter + void set_relax(number omega){ m_relax=omega; } + + /// set type + void set_type(const std::string& type){ m_type=type; } + + /// set subspace + void set_vertex_subspace(SmartPtr > spVertexSubspace) + { m_spVertexSubspace = spVertexSubspace; } + + +protected: + /// Name of preconditioner + virtual const char* name() const + {return "SequentialSubspaceCorrection";} + + /// Preprocess routine + virtual bool preprocess(SmartPtr > pOp) + { + PROFILE_BEGIN_GROUP(SSC_preprocess, "algebra ssc"); + + // Creating overlap 1 matrix and vectors. + matrix_type *pA=NULL; +#ifdef UG_PARALLEL + if(pcl::NumProcs() > 1) + { + UG_ASSERT(0, "SequentialSubspaceCorrection not implemented in parallel. Need to think about this"); + // We should work with element overlap 1 here! + m_A = *pOp; + CreateOverlap(m_A); + m_oD.set_layouts(m_A.layouts()); + m_oC.set_layouts(m_A.layouts()); + m_oD.resize(m_A.num_rows(), false); + m_oC.resize(m_A.num_rows(), false); + pA = &m_A; + } + else +#endif + pA = &(*pOp); + + + + // Checking. + THROW_IF_NOT_EQUAL(pA->num_rows(), pA->num_cols()); + // UG_COND_THROW(CheckDiagonalInvertible(*pA) == false, name() << ": A has noninvertible diagonal"); + + return true; + } + + virtual bool step(SmartPtr > pOp, vector_type& c, const vector_type& d) + { + PROFILE_BEGIN_GROUP(SSC_step, "algebra ssc"); + + GridFunction* pC = dynamic_cast*>(&c); + UG_COND_THROW(pC == NULL, "SequentialSubspaceCorrection expects correction to be a GridFunction."); + + typedef typename GridFunction::element_type Element; + typedef typename GridFunction::side_type Side; + m_spVertexSubspace->preprocess(*pC); + + // Set all vector entries to zero. + pC->set(0.0); + +#ifdef UG_PARALLEL + if(pcl::NumProcs() > 1){ + UG_ASSERT(0, "SequentialSubspaceCorrection not implemented in parallel. Need to think about this"); + + // The key problem is that we are iterating over grid functions, not vectors !!! + } + else +#endif + { + matrix_type &A=*pOp; + if (m_type == "vertex") SequentialSubspaceCorrectionLoop(A, *pC, d, m_relax, + *m_spVertexSubspace, pC->template begin(), pC->template end()); + else UG_THROW("SequentialSubspaceCorrectionStep: wrong patch type '"<set_storage_type(PST_CONSISTENT); +#endif + + return true; + } + return false; + } + + /// Postprocess routine + virtual bool postprocess() {return true;} + + + + +protected: + number m_relax; + std::string m_type; + + SmartPtr > m_spVertexSubspace; + +#ifdef UG_PARALLEL + + /// matrix with overlap + matrix_type m_A; + + /// for overlaps only + vector_type m_oD; + vector_type m_oC; +#endif + + +}; + +} // end namespace ug + +#endif /* __H__UG__LIB_DISC__OPERATOR__LINEAR_OPERATOR__ELEMENT_GAUSS_SEIDEL__ */ diff --git a/ugbase/lib_disc/operator/non_linear_operator/newton_solver/newton.h b/ugbase/lib_disc/operator/non_linear_operator/newton_solver/newton.h index f35c5037c..cc5c59593 100644 --- a/ugbase/lib_disc/operator/non_linear_operator/newton_solver/newton.h +++ b/ugbase/lib_disc/operator/non_linear_operator/newton_solver/newton.h @@ -147,8 +147,8 @@ class NewtonSolver private: /// help functions for debug output /// \{ - void write_debug(const vector_type& vec, const char* filename); - void write_debug(const matrix_type& mat, const char* filename); + void write_debug(const vector_type& vec, std::string filename); + void write_debug(const matrix_type& mat, std::string filename); /// \} private: diff --git a/ugbase/lib_disc/operator/non_linear_operator/newton_solver/newton_impl.h b/ugbase/lib_disc/operator/non_linear_operator/newton_solver/newton_impl.h index e05d19bb5..f77b0d18d 100644 --- a/ugbase/lib_disc/operator/non_linear_operator/newton_solver/newton_impl.h +++ b/ugbase/lib_disc/operator/non_linear_operator/newton_solver/newton_impl.h @@ -175,14 +175,18 @@ bool NewtonSolver::apply(vector_type& u) NEWTON_PROFILE_END(); }UG_CATCH_THROW("NewtonSolver::apply: Computation of Start-Defect failed."); -// write start defect for debug +// loop counts (for the the convergence rate statistics etc.) int loopCnt = 0; m_lastNumSteps = 0; - char ext[20]; sprintf(ext, "_iter%03d", loopCnt); - std::string name("NEWTON_Defect"); - name.append(ext); - write_debug(*spD, name.c_str()); - write_debug(u, "NEWTON_StartSolution"); + +// write start defect for debug + char debug_name_ext[20]; + if (this->debug_writer_valid()) + { + sprintf(debug_name_ext, "_iter%03d", loopCnt); + write_debug(*spD, std::string("NEWTON_Defect") + debug_name_ext); + write_debug(u, "NEWTON_StartSolution"); + } // increase offset of output for linear solver const int stdLinOffset = m_spLinearSolver->standard_offset(); @@ -218,10 +222,12 @@ bool NewtonSolver::apply(vector_type& u) NEWTON_PROFILE_END(); }UG_CATCH_THROW("NewtonSolver::apply: Initialization of Jacobian failed."); - // Write Jacobian for debug - std::string matname("NEWTON_Jacobian"); - matname.append(ext); - write_debug(m_J->get_matrix(), matname.c_str()); + // Write Jacobian for debug and prepare the section for the lin. solver + if (this->debug_writer_valid()) + { + write_debug(m_J->get_matrix(), std::string("NEWTON_Jacobian") + debug_name_ext); + this->enter_debug_writer_section(std::string("NEWTON_LinSolver") + debug_name_ext); + } // Init Jacobi Inverse try{ @@ -247,6 +253,8 @@ bool NewtonSolver::apply(vector_type& u) NEWTON_PROFILE_END(); }UG_CATCH_THROW("NewtonSolver::apply: Application of Linear Solver failed."); + this->leave_debug_writer_section(); + // store convergence history const int numSteps = m_spLinearSolver->step(); if(loopCnt >= (int)m_vTotalLinSolverSteps.size()) m_vTotalLinSolverSteps.resize(loopCnt+1); @@ -287,7 +295,6 @@ bool NewtonSolver::apply(vector_type& u) // update counter loopCnt++; - sprintf(ext, "_iter%03d", loopCnt); // check convergence m_spConvCheck->update(*spD); @@ -295,12 +302,13 @@ bool NewtonSolver::apply(vector_type& u) m_vNonLinSolverRates[loopCnt-1] += m_spConvCheck->rate(); // write defect for debug - std::string name("NEWTON_Defect"); name.append(ext); - write_debug(*spD, name.c_str()); - std::string name2("NEWTON_Correction"); name2.append(ext); - write_debug(*spC, name2.c_str()); - std::string name3("NEWTON_Solution"); name3.append(ext); - write_debug(u, name3.c_str()); + if (this->debug_writer_valid()) + { + sprintf(debug_name_ext, "_iter%03d", loopCnt); + write_debug(*spD, std::string("NEWTON_Defect") + debug_name_ext); + write_debug(*spC, std::string("NEWTON_Correction") + debug_name_ext); + write_debug(u, std::string("NEWTON_Solution") + debug_name_ext); + } } // reset offset of output for linear solver to previous value @@ -398,29 +406,25 @@ void NewtonSolver::clear_average_convergence() } template -void NewtonSolver::write_debug(const vector_type& vec, const char* filename) +void NewtonSolver::write_debug(const vector_type& vec, std::string name) { -// add iter count to name - std::string name(filename); +// add call count to name char ext[20]; sprintf(ext, "_call%03d", m_dgbCall); - name.append(ext).append(".vec"); // write typedef DebugWritingObject base_writer_type; - base_writer_type::write_debug(vec, name.c_str()); + base_writer_type::write_debug(vec, name + ext); } template -void NewtonSolver::write_debug(const matrix_type& mat, const char* filename) +void NewtonSolver::write_debug(const matrix_type& mat, std::string name) { -// add iter count to name - std::string name(filename); +// add call count to name char ext[20]; sprintf(ext, "_call%03d", m_dgbCall); - name.append(ext).append(".mat"); // write typedef DebugWritingObject base_writer_type; - base_writer_type::write_debug(mat, name.c_str()); + base_writer_type::write_debug(mat, name + ext); } template diff --git a/ugbase/lib_disc/spatial_disc/constraints/dirichlet_boundary/lagrange_dirichlet_boundary.h b/ugbase/lib_disc/spatial_disc/constraints/dirichlet_boundary/lagrange_dirichlet_boundary.h index fb7e4d44e..035b82d46 100644 --- a/ugbase/lib_disc/spatial_disc/constraints/dirichlet_boundary/lagrange_dirichlet_boundary.h +++ b/ugbase/lib_disc/spatial_disc/constraints/dirichlet_boundary/lagrange_dirichlet_boundary.h @@ -45,6 +45,9 @@ #include #include + +// #define LAGRANGE_DIRICHLET_ADJ_TRANSFER_FIX + namespace ug{ template < typename TDomain, typename TAlgebra> @@ -89,6 +92,9 @@ class DirichletBoundary : m_bInvertSubsetSelection(false), m_bDirichletColumns(false), m_A(NULL) +#ifdef LAGRANGE_DIRICHLET_ADJ_TRANSFER_FIX + , m_bAdjustTransfers(true) +#endif {clear();} /// constructor with flag for Dirichlet-Columns. @@ -96,8 +102,22 @@ class DirichletBoundary : m_bInvertSubsetSelection(false), m_bDirichletColumns(DirichletColumns), m_A(NULL) +#ifdef LAGRANGE_DIRICHLET_ADJ_TRANSFER_FIX + , m_bAdjustTransfers(true) +#endif {clear();} +#ifdef LAGRANGE_DIRICHLET_ADJ_TRANSFER_FIX + /// constructor with flag for Dirichlet-Columns. + DirichletBoundary(bool DirichletColumns, bool bAdjustTransfers) + : m_bInvertSubsetSelection(false), + m_bDirichletColumns(DirichletColumns), + m_A(NULL), + m_bAdjustTransfers(bAdjustTransfers) + + {clear();} +#endif + /// destructor ~DirichletBoundary() {} @@ -478,6 +498,10 @@ class DirichletBoundary /// current position accessor typename domain_type::position_accessor_type m_aaPos; +#ifdef LAGRANGE_DIRICHLET_ADJ_TRANSFER_FIX + /// flag for setting dirichlet columns + bool m_bAdjustTransfers; +#endif }; } // end namespace ug diff --git a/ugbase/lib_disc/spatial_disc/constraints/dirichlet_boundary/lagrange_dirichlet_boundary_impl.h b/ugbase/lib_disc/spatial_disc/constraints/dirichlet_boundary/lagrange_dirichlet_boundary_impl.h index ce03a9dd8..6cab9605b 100644 --- a/ugbase/lib_disc/spatial_disc/constraints/dirichlet_boundary/lagrange_dirichlet_boundary_impl.h +++ b/ugbase/lib_disc/spatial_disc/constraints/dirichlet_boundary/lagrange_dirichlet_boundary_impl.h @@ -489,6 +489,13 @@ adjust_prolongation(matrix_type& P, int type, number time) { +#ifdef LAGRANGE_DIRICHLET_ADJ_TRANSFER_FIX + if (!m_bAdjustTransfers) + { + std::cerr << "Avoiding adjust_prolongation" << std::endl; + return; + } +#endif extract_data(); adjust_prolongation(m_mBNDNumberBndSegment, P, ddFine, ddCoarse, time); @@ -614,6 +621,13 @@ adjust_restriction(matrix_type& R, int type, number time) { +#ifdef LAGRANGE_DIRICHLET_ADJ_TRANSFER_FIX + if (!m_bAdjustTransfers) + { + std::cerr << "Avoiding adjust_restriction" << std::endl; + return; + } +#endif extract_data(); adjust_restriction(m_mBNDNumberBndSegment, R, ddCoarse, ddFine, time); diff --git a/ugbase/lib_disc/spatial_disc/disc_util/hfv1_geom.cpp b/ugbase/lib_disc/spatial_disc/disc_util/hfv1_geom.cpp index 3b6f66ddc..9ea0d2387 100644 --- a/ugbase/lib_disc/spatial_disc/disc_util/hfv1_geom.cpp +++ b/ugbase/lib_disc/spatial_disc/disc_util/hfv1_geom.cpp @@ -377,7 +377,7 @@ update(GridObject* elem, const MathVector* vCornerCoords, const ISubse const size_t natEdId2 = m_rRefElem.id(2, i, 1, jplus1); // corner of the face - const size_t cornerId = m_rRefElem.id(2,i, 0, jplus1); + const size_t cornerId = m_rRefElem.id(2, i, 0, jplus1); // nodes of hanging edges const size_t hangEdNodeId1 = m_vNatEdgeInfo[natEdId1].node_id(); @@ -473,8 +473,8 @@ update(GridObject* elem, const MathVector* vCornerCoords, const ISubse } // set center of elem as part of scvf - m_vSCVF[i].m_vGloPos[1] = m_gloMid[dim][0]; - m_vSCVF[i].m_vLocPos[1] = m_locMid[dim][0]; + m_vSCVF[i].m_vGloPos[dim > 1 ? 1 : 0] = m_gloMid[dim][0]; + m_vSCVF[i].m_vLocPos[dim > 1 ? 1 : 0] = m_locMid[dim][0]; // integration point AveragePositions(m_vSCVF[i].localIP, m_vSCVF[i].m_vLocPos, SCVF::m_numCorners); @@ -1053,8 +1053,8 @@ update(GridObject* pElem, const MathVector* vCornerCoords, const ISubs } // set center of elem as part of scvf - m_vSCVF[i].m_vGloPos[1] = m_gloMid[dim][0]; - m_vSCVF[i].m_vLocPos[1] = m_locMid[dim][0]; + m_vSCVF[i].m_vGloPos[dim > 1 ? 1 : 0] = m_gloMid[dim][0]; + m_vSCVF[i].m_vLocPos[dim > 1 ? 1 : 0] = m_locMid[dim][0]; // integration point AveragePositions(m_vSCVF[i].localIP, m_vSCVF[i].m_vLocPos, SCVF::m_numCorners); diff --git a/ugbase/lib_disc/spatial_disc/domain_disc.h b/ugbase/lib_disc/spatial_disc/domain_disc.h index 5b44939d3..9631dfb65 100644 --- a/ugbase/lib_disc/spatial_disc/domain_disc.h +++ b/ugbase/lib_disc/spatial_disc/domain_disc.h @@ -511,7 +511,7 @@ class DomainDiscretizationBase /// vector holding all registered elem discs std::vector*> m_vElemError; - // vector holding all registered constraints + /// vector holding all registered constraints std::vector > > m_vConstraint; /// current approximation space diff --git a/ugbase/lib_grid/CMakeLists.txt b/ugbase/lib_grid/CMakeLists.txt index 0cdf40363..b631deb48 100644 --- a/ugbase/lib_grid/CMakeLists.txt +++ b/ugbase/lib_grid/CMakeLists.txt @@ -86,6 +86,8 @@ set(srcAlgorithms algorithms/debug_util.cpp refinement/projectors/projection_handler.cpp refinement/projectors/smooth_projector.cpp refinement/projectors/subdivision_projector.cpp + refinement/projectors/neurite_projector.cpp + refinement/projectors/elliptic_cylinder_projector.cpp refinement/ref_mark_adjusters/local_mark_adjuster.cpp refinement/ref_mark_adjusters/horizontal_anisotropy_adjuster.cpp refinement/ref_mark_adjusters/mg_hnode_adjuster.cpp diff --git a/ugbase/lib_grid/algorithms/deg_layer_mngr_impl.h b/ugbase/lib_grid/algorithms/deg_layer_mngr_impl.h index bb8e83d19..7613c5759 100644 --- a/ugbase/lib_grid/algorithms/deg_layer_mngr_impl.h +++ b/ugbase/lib_grid/algorithms/deg_layer_mngr_impl.h @@ -34,6 +34,7 @@ * Implementation of the degenerated layer subset manager. */ // ug4 headers +#include "common/util/string_util.h" #include "lib_grid/grid/grid.h" namespace ug { diff --git a/ugbase/lib_grid/algorithms/geom_obj_util/anisotropy_util.h b/ugbase/lib_grid/algorithms/geom_obj_util/anisotropy_util.h index 899ee238e..cc49a7eb1 100644 --- a/ugbase/lib_grid/algorithms/geom_obj_util/anisotropy_util.h +++ b/ugbase/lib_grid/algorithms/geom_obj_util/anisotropy_util.h @@ -44,39 +44,118 @@ namespace ug { +enum AnisotropyState +{ + ISOTROPIC = 0, + QUAD_SHORTX, + QUAD_SHORTY, + PRISM_FLAT, + PRISM_LONG, + HEX_SHORTX, + HEX_SHORTY, + HEX_SHORTZ, + HEX_SHORTXY, + HEX_SHORTXZ, + HEX_SHORTYZ +}; + + template -bool is_anisotropic +AnisotropyState is_anisotropic +( + Edge* elem, + const TAAPos& aaPos, + number thresholdRatio +); + + +template +AnisotropyState is_anisotropic +( + Face* elem, + const TAAPos& aaPos, + number thresholdRatio +); + + +template +AnisotropyState is_anisotropic +( + Volume* elem, + const TAAPos& aaPos, + number thresholdRatio +); + + + +template +AnisotropyState close_sides_of_anisotropic_elem ( Edge* elem, Grid& grid, const TAAPos& aaPos, number thresholdRatio, - std::vector* nb = NULL + std::vector& sidesOut ); template -bool is_anisotropic +AnisotropyState close_sides_of_anisotropic_elem ( Face* elem, Grid& grid, const TAAPos& aaPos, number thresholdRatio, - std::vector* nb = NULL + std::vector& sidesOut ); template -static bool is_anisotropic +AnisotropyState close_sides_of_anisotropic_elem ( Volume* elem, Grid& grid, const TAAPos& aaPos, number thresholdRatio, - std::vector* nb = NULL + std::vector& sidesOut ); + +template +AnisotropyState long_edges_of_anisotropic_elem +( + Edge* elem, + Grid& grid, + const TAAPos& aaPos, + number thresholdRatio, + std::vector& longEdges +); + + +template +AnisotropyState long_edges_of_anisotropic_elem +( + Face* elem, + Grid& grid, + const TAAPos& aaPos, + number thresholdRatio, + std::vector& longEdges +); + + +template +AnisotropyState long_edges_of_anisotropic_elem +( + Volume* elem, + Grid& grid, + const TAAPos& aaPos, + number thresholdRatio, + std::vector& longEdges +); + + + } // namespace ug #include "anisotropy_util_impl.h" diff --git a/ugbase/lib_grid/algorithms/geom_obj_util/anisotropy_util_impl.h b/ugbase/lib_grid/algorithms/geom_obj_util/anisotropy_util_impl.h index d961952f2..7df6af8f0 100644 --- a/ugbase/lib_grid/algorithms/geom_obj_util/anisotropy_util_impl.h +++ b/ugbase/lib_grid/algorithms/geom_obj_util/anisotropy_util_impl.h @@ -39,202 +39,565 @@ namespace ug { template -bool is_anisotropic +AnisotropyState is_anisotropic +( + Edge* elem, + const TAAPos& aaPos, + number thresholdRatio +) +{ + return ISOTROPIC; +} + + + +template +static AnisotropyState is_anisotropic +( + Quadrilateral* q, + const TAAPos& aaPos, + number thresholdRatio +) +{ + // check whether elem is anisotropic + number sideLength02 = VertexDistance(q->vertex(0), q->vertex(1), aaPos) + + VertexDistance(q->vertex(2), q->vertex(3), aaPos); + + number sideLength13 = VertexDistance(q->vertex(1), q->vertex(2), aaPos) + + VertexDistance(q->vertex(3), q->vertex(0), aaPos); + + if (sideLength02 < thresholdRatio * sideLength13) + return QUAD_SHORTX; + if (sideLength13 < thresholdRatio * sideLength02) + return QUAD_SHORTY; + + return ISOTROPIC; +} + + + +template +AnisotropyState is_anisotropic +( + Face* elem, + const TAAPos& aaPos, + number thresholdRatio +) +{ + Quadrilateral* q = dynamic_cast(elem); + if (!q) + return ISOTROPIC; + + return is_anisotropic(q, aaPos, thresholdRatio); +} + + + + +template +static AnisotropyState is_anisotropic +( + Prism* p, + const TAAPos& aaPos, + number thresholdRatio +) +{ + // check whether elem is anisotropic + number length = VertexDistance(p->vertex(0), p->vertex(3), aaPos) + + VertexDistance(p->vertex(1), p->vertex(4), aaPos) + + VertexDistance(p->vertex(2), p->vertex(5), aaPos); + number width = VertexDistance(p->vertex(0), p->vertex(1), aaPos) + + VertexDistance(p->vertex(1), p->vertex(2), aaPos) + + VertexDistance(p->vertex(2), p->vertex(0), aaPos) + + VertexDistance(p->vertex(3), p->vertex(4), aaPos) + + VertexDistance(p->vertex(4), p->vertex(5), aaPos) + + VertexDistance(p->vertex(5), p->vertex(3), aaPos); + + number ratio = width ? 2.0*length/width : std::numeric_limits::max(); + + // flat case + if (ratio < thresholdRatio) + return PRISM_FLAT; + + // long case + if (ratio*thresholdRatio > 1) + return PRISM_LONG; + + return ISOTROPIC; +} + + + +template +static AnisotropyState is_anisotropic +( + Hexahedron* hex, + const TAAPos& aaPos, + number thresholdRatio +) +{ + number length1 = VertexDistance(hex->vertex(0), hex->vertex(1), aaPos) + + VertexDistance(hex->vertex(2), hex->vertex(3), aaPos) + + VertexDistance(hex->vertex(4), hex->vertex(5), aaPos) + + VertexDistance(hex->vertex(6), hex->vertex(7), aaPos); + number length2 = VertexDistance(hex->vertex(0), hex->vertex(3), aaPos) + + VertexDistance(hex->vertex(1), hex->vertex(2), aaPos) + + VertexDistance(hex->vertex(4), hex->vertex(7), aaPos) + + VertexDistance(hex->vertex(5), hex->vertex(6), aaPos); + number length3 = VertexDistance(hex->vertex(0), hex->vertex(4), aaPos) + + VertexDistance(hex->vertex(1), hex->vertex(5), aaPos) + + VertexDistance(hex->vertex(2), hex->vertex(6), aaPos) + + VertexDistance(hex->vertex(3), hex->vertex(7), aaPos); + + bool shortx = false; + bool shorty = false; + bool shortz = false; + + if (length1 < thresholdRatio * length2 || length1 < thresholdRatio * length3) + shortx = true; + + if (length2 < thresholdRatio * length1 || length2 < thresholdRatio * length3) + shorty = true; + + if (length3 < thresholdRatio * length1 || length3 < thresholdRatio * length2) + shortz = true; + + if (shortx) + { + if (shorty) + return HEX_SHORTXY; + if (shortz) + return HEX_SHORTXZ; + return HEX_SHORTX; + } + + if (shorty) + { + if (shortz) + return HEX_SHORTYZ; + return HEX_SHORTY; + } + + if (shortz) + return HEX_SHORTZ; + + return ISOTROPIC; +} + + + +template +AnisotropyState is_anisotropic +( + Volume* elem, + const TAAPos& aaPos, + number thresholdRatio +) +{ + // treat prism case + Prism* prism = dynamic_cast(elem); + if (prism) + return is_anisotropic(prism, aaPos, thresholdRatio); + + // treat hexahedron case + Hexahedron* hex = dynamic_cast(elem); + if (hex) + return is_anisotropic(hex, aaPos, thresholdRatio); + + // other cases do not exist + return ISOTROPIC; +} + + + +template +AnisotropyState close_sides_of_anisotropic_elem ( Edge* elem, Grid& grid, const TAAPos& aaPos, number thresholdRatio, - std::vector* nb + std::vector& sidesOut ) { - return false; + return ISOTROPIC; } - template -bool is_anisotropic +AnisotropyState close_sides_of_anisotropic_elem ( Face* elem, Grid& grid, const TAAPos& aaPos, number thresholdRatio, - std::vector* nb + std::vector& sidesOut ) { + // check whether this is a quadrilateral Quadrilateral* q = dynamic_cast(elem); if (!q) - return false; + return ISOTROPIC; - // check whether elem is anisotropic - number sideLength02 = VertexDistance(q->vertex(0), q->vertex(1), aaPos) - + VertexDistance(q->vertex(2), q->vertex(3), aaPos); + // check whether element is anisotropic (and which case) + AnisotropyState state = is_anisotropic(q, aaPos, thresholdRatio); + if (state == ISOTROPIC) + return state; - number sideLength13 = VertexDistance(q->vertex(1), q->vertex(2), aaPos) - + VertexDistance(q->vertex(3), q->vertex(0), aaPos); + if (state == QUAD_SHORTX) + { + Grid::SecureEdgeContainer assEd; + grid.associated_elements_sorted(assEd, q); + sidesOut.push_back(assEd[1]); + sidesOut.push_back(assEd[3]); - number ratio = 0.0; - if (sideLength02 > sideLength13) - ratio = sideLength13 / sideLength02; - else if (sideLength13 > sideLength02) - ratio = sideLength02 / sideLength13; - else - ratio = 1.0; + return state; + } - if (ratio >= thresholdRatio) - return false; + if (state == QUAD_SHORTY) + { + Grid::SecureEdgeContainer assEd; + grid.associated_elements_sorted(assEd, q); + sidesOut.push_back(assEd[0]); + sidesOut.push_back(assEd[2]); + return state; + } - // get elem neighbors at larger sides - typedef typename Grid::traits::secure_container elem_list; - typedef typename Grid::traits::secure_container side_list; + return state; +} - side_list sl; - EdgeDescriptor largeSide; - if (sideLength02 > sideLength13) - largeSide = elem->edge_desc(0); - else - largeSide = elem->edge_desc(1); - grid.associated_elements(sl, elem); - size_t slSz = sl.size(); - for (size_t s = 0; s < slSz; ++s) + +template +AnisotropyState close_sides_of_anisotropic_elem +( + Volume* elem, + Grid& grid, + const TAAPos& aaPos, + number thresholdRatio, + std::vector& sidesOut +) +{ + // treat prism case + Prism* prism = dynamic_cast(elem); + if (prism) + { + AnisotropyState state = is_anisotropic(prism, aaPos, thresholdRatio); + if (state == ISOTROPIC) + return state; + + // flat case + if (state == PRISM_FLAT) + { + Face* side = grid.get_side(prism, 0); + if (side) + sidesOut.push_back(side); + + side = grid.get_side(prism, 4); + if (side) + sidesOut.push_back(side); + } + + // long case + if (state == PRISM_LONG) + { + Face* side = grid.get_side(prism, 1); + if (side) + sidesOut.push_back(side); + + side = grid.get_side(prism, 2); + if (side) + sidesOut.push_back(side); + + side = grid.get_side(prism, 3); + if (side) + sidesOut.push_back(side); + } + + return state; + } + + + // treat heaxahedron case + Hexahedron* hex = dynamic_cast(elem); + if (hex) { - if (CompareVertices(sl[s], &largeSide)) + AnisotropyState state = is_anisotropic(hex, aaPos, thresholdRatio); + if (state == ISOTROPIC) + return state; + + + // short in x direction + if (state == HEX_SHORTX || state == HEX_SHORTXY || state == HEX_SHORTXZ) + { + Face* side = grid.get_side(hex, 2); + if (side) + sidesOut.push_back(side); + + side = grid.get_side(hex, 4); + if (side) + sidesOut.push_back(side); + } + + // short in y direction + if (state == HEX_SHORTY || state == HEX_SHORTXY || state == HEX_SHORTYZ) + { + Face* side = grid.get_side(hex, 1); + if (side) + sidesOut.push_back(side); + + side = grid.get_side(hex, 3); + if (side) + sidesOut.push_back(side); + } + + // short in z direction + if (state == HEX_SHORTZ || state == HEX_SHORTXZ || state == HEX_SHORTYZ) { - Edge* opp = GetOpposingSide(grid, elem, sl[s]); - if (nb) - { - nb->push_back(sl[s]); - nb->push_back(opp); - } - - return true; + Face* side = grid.get_side(hex, 0); + if (side) + sidesOut.push_back(side); + + side = grid.get_side(hex, 5); + if (side) + sidesOut.push_back(side); } + + return state; } - return true; + + // other elements cannot be anisotropic + return ISOTROPIC; } + + template -bool is_anisotropic +AnisotropyState long_edges_of_anisotropic_elem ( - Volume* elem, + Edge* elem, Grid& grid, const TAAPos& aaPos, number thresholdRatio, - std::vector* nb + std::vector& longEdges ) { - Prism* prism = dynamic_cast(elem); + return ISOTROPIC; +} + +template +AnisotropyState long_edges_of_anisotropic_elem +( + Face* elem, + Grid& grid, + const TAAPos& aaPos, + number thresholdRatio, + std::vector& longEdges +) +{ + return close_sides_of_anisotropic_elem(elem, grid, aaPos, thresholdRatio, longEdges); +} + + +template +AnisotropyState long_edges_of_anisotropic_elem +( + Volume* elem, + Grid& grid, + const TAAPos& aaPos, + number thresholdRatio, + std::vector& longEdges +) +{ // treat prism case + Prism* prism = dynamic_cast(elem); if (prism) { - // check whether elem is anisotropic - number length = VertexDistance(prism->vertex(0), prism->vertex(3), aaPos) - + VertexDistance(prism->vertex(1), prism->vertex(4), aaPos) - + VertexDistance(prism->vertex(2), prism->vertex(5), aaPos); - number width = VertexDistance(prism->vertex(0), prism->vertex(1), aaPos) - + VertexDistance(prism->vertex(1), prism->vertex(2), aaPos) - + VertexDistance(prism->vertex(2), prism->vertex(0), aaPos) - + VertexDistance(prism->vertex(3), prism->vertex(4), aaPos) - + VertexDistance(prism->vertex(4), prism->vertex(5), aaPos) - + VertexDistance(prism->vertex(5), prism->vertex(3), aaPos); + AnisotropyState state = is_anisotropic(prism, aaPos, thresholdRatio); + if (state == ISOTROPIC) + return state; - number ratio = width ? 2.0*length/width : std::numeric_limits::max(); - - if (ratio >= thresholdRatio) - return false; + // flat case + if (state == PRISM_FLAT) + { + Grid::SecureEdgeContainer assEd; + grid.associated_elements_sorted(assEd, prism); + UG_COND_THROW(assEd.size() < 9, "Prism needs to have 9 edges, but only " + << assEd.size() << " found."); - // get elem neighbors at larger sides - typedef typename Grid::traits::secure_container elem_list; - typedef typename Grid::traits::secure_container side_list; + longEdges.reserve(longEdges.size() + 6); + longEdges.push_back(assEd[0]); + longEdges.push_back(assEd[1]); + longEdges.push_back(assEd[2]); + longEdges.push_back(assEd[6]); + longEdges.push_back(assEd[7]); + longEdges.push_back(assEd[8]); - side_list sl; - FaceDescriptor largeSide = prism->face_desc(0); + return state; + } - grid.associated_elements(sl, prism); - size_t slSz = sl.size(); - for (size_t s = 0; s < slSz; ++s) + // long case + if (state == PRISM_LONG) { - if (CompareVertices(sl[s], &largeSide)) - { - Face* opp = GetOpposingSide(grid, elem, sl[s]); - if (nb) - { - nb->push_back(sl[s]); - nb->push_back(opp); - } - - return true; - } + Grid::SecureEdgeContainer assEd; + grid.associated_elements_sorted(assEd, prism); + + UG_COND_THROW(assEd.size() < 9, "Prism needs to have 9 edges, but only " + << assEd.size() << " found."); + + longEdges.reserve(longEdges.size() + 3); + longEdges.push_back(assEd[3]); + longEdges.push_back(assEd[4]); + longEdges.push_back(assEd[5]); + + return state; } - return true; + return state; } + + // treat heaxahedron case Hexahedron* hex = dynamic_cast(elem); - if (!hex) - return false; + if (hex) + { + AnisotropyState state = is_anisotropic(hex, aaPos, thresholdRatio); + if (state == ISOTROPIC) + return state; - // treat hexahedron case - // check whether elem is anisotropic - number length1 = VertexDistance(hex->vertex(0), hex->vertex(1), aaPos) - + VertexDistance(hex->vertex(2), hex->vertex(3), aaPos) - + VertexDistance(hex->vertex(4), hex->vertex(5), aaPos) - + VertexDistance(hex->vertex(6), hex->vertex(7), aaPos); - number length2 = VertexDistance(hex->vertex(0), hex->vertex(3), aaPos) - + VertexDistance(hex->vertex(1), hex->vertex(2), aaPos) - + VertexDistance(hex->vertex(4), hex->vertex(7), aaPos) - + VertexDistance(hex->vertex(5), hex->vertex(6), aaPos); - number length3 = VertexDistance(hex->vertex(0), hex->vertex(4), aaPos) - + VertexDistance(hex->vertex(1), hex->vertex(5), aaPos) - + VertexDistance(hex->vertex(2), hex->vertex(6), aaPos) - + VertexDistance(hex->vertex(3), hex->vertex(7), aaPos); + // short in x direction + if (state == HEX_SHORTX) + { + Grid::SecureEdgeContainer assEd; + grid.associated_elements_sorted(assEd, hex); + + UG_COND_THROW(assEd.size() < 12, "Hexahedron needs to have 12 edges, but only " + << assEd.size() << " found."); + + longEdges.reserve(longEdges.size() + 8); + longEdges.push_back(assEd[1]); + longEdges.push_back(assEd[3]); + longEdges.push_back(assEd[4]); + longEdges.push_back(assEd[5]); + longEdges.push_back(assEd[6]); + longEdges.push_back(assEd[7]); + longEdges.push_back(assEd[8]); + longEdges.push_back(assEd[11]); + + return state; + } - number ratio; - number largestLength = std::max(length1, std::max(length2, length3)); - number smallestLength = std::min(length1, std::min(length2, length3)); - if (largestLength > smallestLength) - ratio = smallestLength / largestLength; - else - ratio = 1.0; + // short in y direction + if (state == HEX_SHORTY) + { + Grid::SecureEdgeContainer assEd; + grid.associated_elements_sorted(assEd, hex); + + UG_COND_THROW(assEd.size() < 12, "Hexahedron needs to have 12 edges, but only " + << assEd.size() << " found."); + + longEdges.reserve(longEdges.size() + 8); + longEdges.push_back(assEd[0]); + longEdges.push_back(assEd[2]); + longEdges.push_back(assEd[4]); + longEdges.push_back(assEd[5]); + longEdges.push_back(assEd[6]); + longEdges.push_back(assEd[7]); + longEdges.push_back(assEd[8]); + longEdges.push_back(assEd[10]); + + return state; + } - if (ratio >= thresholdRatio) - return false; + // short in z direction + if (state == HEX_SHORTZ) + { + Grid::SecureEdgeContainer assEd; + grid.associated_elements_sorted(assEd, hex); + + UG_COND_THROW(assEd.size() < 12, "Hexahedron needs to have 12 edges, but only " + << assEd.size() << " found."); + + longEdges.reserve(longEdges.size() + 8); + longEdges.push_back(assEd[0]); + longEdges.push_back(assEd[1]); + longEdges.push_back(assEd[2]); + longEdges.push_back(assEd[3]); + longEdges.push_back(assEd[8]); + longEdges.push_back(assEd[9]); + longEdges.push_back(assEd[10]); + longEdges.push_back(assEd[11]); + + return state; + } - // get elem neighbors at larger sides - typedef typename Grid::traits::secure_container elem_list; - typedef typename Grid::traits::secure_container side_list; + // short in x and y direction + if (state == HEX_SHORTXY) + { + Grid::SecureEdgeContainer assEd; + grid.associated_elements_sorted(assEd, hex); - size_t smallestDim = length1 < length2 ? (length1 < length3 ? 1 : 3) : (length2 < length3 ? 2 : 3); - FaceDescriptor largeSide = hex->face_desc(3-smallestDim); + UG_COND_THROW(assEd.size() < 12, "Hexahedron needs to have 12 edges, but only " + << assEd.size() << " found."); - side_list sl; - grid.associated_elements(sl, hex); - size_t slSz = sl.size(); - for (size_t s = 0; s < slSz; ++s) - { - if (CompareVertices(sl[s], &largeSide)) + longEdges.reserve(longEdges.size() + 4); + longEdges.push_back(assEd[4]); + longEdges.push_back(assEd[5]); + longEdges.push_back(assEd[6]); + longEdges.push_back(assEd[7]); + + return state; + } + + // short in x and z direction + if (state == HEX_SHORTXZ) { - Face* opp = GetOpposingSide(grid, elem, sl[s]); - if (nb) - { - nb->push_back(sl[s]); - nb->push_back(opp); - } - - return true; + Grid::SecureEdgeContainer assEd; + grid.associated_elements_sorted(assEd, hex); + + UG_COND_THROW(assEd.size() < 12, "Hexahedron needs to have 12 edges, but only " + << assEd.size() << " found."); + + longEdges.reserve(longEdges.size() + 4); + longEdges.push_back(assEd[1]); + longEdges.push_back(assEd[3]); + longEdges.push_back(assEd[9]); + longEdges.push_back(assEd[11]); + + return state; } + + // short in y and z direction + if (state == HEX_SHORTYZ) + { + Grid::SecureEdgeContainer assEd; + grid.associated_elements_sorted(assEd, hex); + + UG_COND_THROW(assEd.size() < 12, "Hexahedron needs to have 12 edges, but only " + << assEd.size() << " found."); + + longEdges.reserve(longEdges.size() + 4); + longEdges.push_back(assEd[0]); + longEdges.push_back(assEd[2]); + longEdges.push_back(assEd[8]); + longEdges.push_back(assEd[10]); + + return state; + } + + return state; } - return true; + + // other elements cannot be anisotropic + return ISOTROPIC; } } // namespace ug diff --git a/ugbase/lib_grid/algorithms/orientation_util_impl.hpp b/ugbase/lib_grid/algorithms/orientation_util_impl.hpp index 2979fb544..e59ccdb4a 100644 --- a/ugbase/lib_grid/algorithms/orientation_util_impl.hpp +++ b/ugbase/lib_grid/algorithms/orientation_util_impl.hpp @@ -190,7 +190,7 @@ FixOrientation(Grid& grid, TVolIterator volsBegin, TVolIterator volsEnd, { int numFlips = 0; // iterate through all volumes - for(VolumeIterator iter = volsBegin; iter != volsEnd; ++iter){ + for(TVolIterator iter = volsBegin; iter != volsEnd; ++iter){ // check whether the orientation is fine if(!CheckOrientation(*iter, aaPosVRT)){ grid.flip_orientation(*iter); diff --git a/ugbase/lib_grid/algorithms/remeshing/resolve_intersections_impl.hpp b/ugbase/lib_grid/algorithms/remeshing/resolve_intersections_impl.hpp index 100a3514f..3ba43fadd 100644 --- a/ugbase/lib_grid/algorithms/remeshing/resolve_intersections_impl.hpp +++ b/ugbase/lib_grid/algorithms/remeshing/resolve_intersections_impl.hpp @@ -1372,7 +1372,6 @@ bool ResolveTriangleIntersections(Grid& grid, TriangleIterator trisBegin, // iterate over all triangles and perform intersecion with other triangles size_t triCounter = 0; - const size_t dbgTriInd(-1); for(TriangleIterator triIter1 = sel.begin(); triIter1 != sel.end(); ++triIter1, ++triCounter) { diff --git a/ugbase/lib_grid/algorithms/subdivision/subdivision_volumes.cpp b/ugbase/lib_grid/algorithms/subdivision/subdivision_volumes.cpp index c0943a3f7..536364eff 100644 --- a/ugbase/lib_grid/algorithms/subdivision/subdivision_volumes.cpp +++ b/ugbase/lib_grid/algorithms/subdivision/subdivision_volumes.cpp @@ -514,7 +514,7 @@ void ProjectHierarchyToLimitSubdivisionSurface(MultiGrid& mg) // Catch use of procedure for MultiGrids with just one level if(mg.num_levels() == 1) { - UG_THROW("Error in ProjectHierarchyToLimitSubdivisionVolume2d: " + UG_THROW("Error in ProjectHierarchyToLimitSubdivisionSurface: " "Procedure only to be used for MultiGrids with more than one level."); } @@ -570,7 +570,7 @@ void ProjectHierarchyToLimitSubdivisionVolume(MultiGrid& mg) // Catch use of procedure for MultiGrids with just one level if(mg.num_levels() == 1) { - UG_THROW("Error in ProjectHierarchyToLimitSubdivisionVolume3d: " + UG_THROW("Error in ProjectHierarchyToLimitSubdivisionVolume: " "Procedure only to be used for MultiGrids with more than one level."); } @@ -618,6 +618,413 @@ void ProjectHierarchyToLimitSubdivisionVolume(MultiGrid& mg) } +//////////////////////////////////////////////////////////////////////////////// +void CalculateSmoothManifoldPosInParentLevelButterflyScheme3d(MultiGrid& mg, MGSubsetHandler& markSH, + MGSubsetHandler& linearManifoldSH, + APosition& aSmoothBndPosOddVrt, + AInt& aNumManifoldEdges) +{ + /* + * Scheme reference: + * + * D. N. Zorin, Interpolating Subdivision for Meshes with Arbitrary Topology, + * SIGGRAPH '96 Proceedings of the 23rd annual conference on Computer graphics + * and interactive techniques, 1996. + */ + +// WARNING: Parallel implementation has to be fixed + #ifdef UG_PARALLEL + UG_LOG("WARNING: CalculateSmoothManifoldPosInParentLevelButterflyScheme3d: " + "Parallel implementation has to be fixed." << std::endl); + #endif + +// Catch use of procedure for MultiGrids with just one level + if(mg.num_levels() == 1) + { + UG_THROW("Error in CalculateSmoothManifoldPosInParentLevelButterflyScheme3d: " + "Procedure only to be used for MultiGrids with more than one level."); + } + +// Define attachment accessors + Grid::VertexAttachmentAccessor aaPos(mg, aPosition); + Grid::EdgeAttachmentAccessor aaSmoothBndPosOddVrt(mg, aSmoothBndPosOddVrt); + Grid::VertexAttachmentAccessor aaNumManifoldEdges(mg, aNumManifoldEdges); + + #ifdef UG_PARALLEL + DistributedGridManager& dgm = *mg.distributed_grid_manager(); + #endif + +// Declare centroid coordinate vector + typedef APosition::ValueType pos_type; + pos_type p; + pos_type q; + VecSet(p, 0); + VecSet(q, 0); + + /* + * Smoothing of odd vertices x + * + -1/16 1/8 -1/16 + \ / \ / + 1/2--x--1/2 + / \ / \ + -1/16 1/8 -1/16 + * + */ + +// Calculate smooth position for ODD vertices (EVEN vertices will be interpolated by this scheme) + for(EdgeIterator eIter = mg.begin(mg.top_level()-1); eIter != mg.end(mg.top_level()-1); ++eIter) + { + // Reset centroids + VecSet(p, 0); + VecSet(q, 0); + + Edge* e = *eIter; + + // Skip ghost edges + #ifdef UG_PARALLEL + if(dgm.is_ghost(e)) + continue; + #endif + + // In case of marked manifold edges, which do not belong to the user-specified linear boundary manifold subsets, + // and activated subdivision Butterfly refinement calculate subdivision surfaces smooth position + if(markSH.get_subset_index(e) != -1 && linearManifoldSH.get_subset_index(e) == -1) + { + // REGULAR CASE: both edge vertices are of valence 6 + if(aaNumManifoldEdges[e->vertex(0)] == 6 && aaNumManifoldEdges[e->vertex(1)] == 6) + { + // perform Butterfly subdivision on odd manifold vertices + // get the neighbored manifold triangles + std::vector associatedFaces; + std::vector associatedButterflyFaces; + std::vector associatedManifoldFaces; + std::vector associatedButterflyManifoldFaces; + + CollectAssociated(associatedFaces, mg, e); + + for(size_t i = 0; i < associatedFaces.size(); ++i) + { + // Only consider associated faces, which are marked as manifold faces + if(markSH.get_subset_index(associatedFaces[i]) != -1) + { + // Exclude ghost and horizontal slave manifold faces + #ifdef UG_PARALLEL + if(dgm.is_ghost(associatedFaces[i])) + continue; + + if(dgm.contains_status(associatedFaces[i], ES_H_SLAVE)) + continue; + #endif + + if(associatedManifoldFaces.size() < 2) + { + associatedManifoldFaces.push_back(associatedFaces[i]); + } + } + } + + // THROW, if more then 2 associated manifold faces have been found + if(associatedManifoldFaces.size() <= 2) + { + // Check, if all faces are triangles + for(size_t i = 0; i < associatedManifoldFaces.size(); ++i) + { + if(associatedManifoldFaces[i]->num_vertices() != 3) + { + UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevelButterflyScheme3d: " + "Non triangular faces included in grid: " << ElementDebugInfo(mg, associatedManifoldFaces[i])); + } + } + + // Summate centroid of face adjacent vertices (with corresponding weights 1/8) + for(size_t i = 0; i < associatedManifoldFaces.size(); ++i) + { + VecAdd(p, p, aaPos[GetConnectedVertex(e, associatedManifoldFaces[i])]); + } + + // Extend original "Loop's neighborhood diamond" to include 'BUTTERFLY VERTICES' (with corresponding weights -1/16) + for(size_t i = 0; i < associatedManifoldFaces.size(); ++i) + { + // Iterate over edges of original "Loop's neighborhood diamond" to extend to Butterfly neighborhood + for(size_t j = 0; j < associatedManifoldFaces[i]->num_edges(); ++j) + { + // Clear face container + associatedButterflyFaces.clear(); + associatedButterflyManifoldFaces.clear(); + + // Exclude edge e currently being edited + if(j != (size_t)GetEdgeIndex(associatedManifoldFaces[i], e)) + { + // Collect associated Butterfly face adjacent to edge j + GetNeighbours(associatedButterflyFaces, mg, associatedManifoldFaces[i], j); + + for(size_t k = 0; k < associatedButterflyFaces.size(); ++k) + { + // Only consider associated butterfly faces, which are marked as manifold faces + if(markSH.get_subset_index(associatedButterflyFaces[k]) != -1) + { + // Exclude ghost and horizontal slave manifold faces + #ifdef UG_PARALLEL + if(dgm.is_ghost(associatedButterflyFaces[k])) + continue; + + if(dgm.contains_status(associatedButterflyFaces[k], ES_H_SLAVE)) + continue; + #endif + + if(associatedButterflyFaces[k]->num_vertices() != 3) + { + UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevelButterflyScheme3d: " + "Non triangular faces included in grid: " << ElementDebugInfo(mg, associatedButterflyFaces[k])); + } + + associatedButterflyManifoldFaces.push_back(associatedButterflyFaces[k]); + } + } + + if(associatedButterflyManifoldFaces.size() != 1) + UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevelButterflyScheme3d: number of edge associated Butterfly Manifold faces != 1."); + + // Summate centroid of butterly face adjacent vertex + VecAdd(q, q, aaPos[GetConnectedVertex(mg.get_edge(associatedManifoldFaces[i]->edge_desc(j)), associatedButterflyManifoldFaces[0])]); + } + } + } + + // Exclude ghost and horizontal slaves of the parent edge vertices of the currently smoothed vertex + // to avoid multiple contributions to the centroid of the edge adjacent vertices + #ifdef UG_PARALLEL + if(dgm.is_ghost(e)) + { + continue; + } + + if(dgm.contains_status(e, ES_H_SLAVE)) + { + VecScaleAppend(aaSmoothBndPosOddVrt[e], 0.125, p, -1.0/16, q); + continue; + } + #endif + + VecScaleAppend(aaSmoothBndPosOddVrt[e], 0.5, aaPos[e->vertex(0)], 0.5, aaPos[e->vertex(1)], 0.125, p, -1.0/16, q); + } + else + UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevelButterflyScheme3d: numAssociatedManifoldFaces > 2."); + } + + // IRREGULAR CASE: at least one edge vertex irregular + if(aaNumManifoldEdges[e->vertex(0)] != 6 || aaNumManifoldEdges[e->vertex(1)] != 6) + { + // Number of centroids to calculate (1 or 2, depending on the valence of the vertices of e) + size_t numLoops; + + if((aaNumManifoldEdges[e->vertex(0)] != 6 && aaNumManifoldEdges[e->vertex(1)] == 6) || + (aaNumManifoldEdges[e->vertex(0)] == 6 && aaNumManifoldEdges[e->vertex(1)] != 6)) + { + numLoops = 1; + } + // case aaNumManifoldEdges[e->vertex(0)] != 6 && aaNumManifoldEdges[e->vertex(1)] != 6 + else + numLoops = 2; + + // Loop centroids to calculate + for(size_t n = 0; n < numLoops; ++n) + { + // Reset centroids + VecSet(p, 0); + VecSet(q, 0); + + Vertex* vrt; + Vertex* butterflyVertex; + + std::vector associatedFaces; + std::vector associatedManifoldFaces; + + // Determine smoothing case + if(aaNumManifoldEdges[e->vertex(0)] != 6 && aaNumManifoldEdges[e->vertex(1)] == 6) + { + vrt = e->vertex(0); + + // Push back e->vertex(1) as vertex s_0 + butterflyVertex = e->vertex(1); + } + else if (aaNumManifoldEdges[e->vertex(0)] == 6 && aaNumManifoldEdges[e->vertex(1)] != 6) + { + vrt = e->vertex(1); + + // Push back e->vertex(0) as vertex s_0 + butterflyVertex = e->vertex(0); + } + // case aaNumManifoldEdges[e->vertex(0)] != 6 && aaNumManifoldEdges[e->vertex(1)] != 6 + else + { + vrt = e->vertex(n); + + // Push back the other vertex as vertex s_0 + butterflyVertex = e->vertex(1 - (n % 2)); + } + + // perform Butterfly subdivision on odd manifold vertices + // get the neighbored manifold triangles + CollectAssociated(associatedFaces, mg, e); + + for(size_t i = 0; i < associatedFaces.size(); ++i) + { + // Only consider associated faces, which are marked as manifold faces + if(markSH.get_subset_index(associatedFaces[i]) != -1) + { + // Exclude ghost and horizontal slave manifold faces + #ifdef UG_PARALLEL + if(dgm.is_ghost(associatedFaces[i])) + continue; + + if(dgm.contains_status(associatedFaces[i], ES_H_SLAVE)) + continue; + #endif + + if(associatedManifoldFaces.size() < 2) + { + associatedManifoldFaces.push_back(associatedFaces[i]); + } + } + } + + if(associatedManifoldFaces.size() <= 2) + { + // Check, if all faces are triangles + for(size_t i = 0; i < associatedManifoldFaces.size(); ++i) + { + if(associatedManifoldFaces[i]->num_vertices() != 3) + { + UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevelButterflyScheme3d: " + "Non triangular faces included in grid: " << ElementDebugInfo(mg, associatedManifoldFaces[i])); + } + } + + // Start with one triangle of "Loop's neighborhood diamond" + Face* f = associatedManifoldFaces[0]; + size_t k = (size_t)aaNumManifoldEdges[vrt]; + + number butterflyWeight = 0.0; + std::vector butterflyWeights; + + if(k == 3) + { + butterflyWeights.push_back(5.0/12); + butterflyWeights.push_back(-1.0/12); + butterflyWeights.push_back(-1.0/12); + } + + if(k == 4) + { + butterflyWeights.push_back(3.0/8); + butterflyWeights.push_back(0.0); + butterflyWeights.push_back(-1.0/8); + butterflyWeights.push_back(0.0); + } + + // Special parallel treatment for s_0 in case e is ghost or horizontal slave + if(k != 3 && k != 4) + butterflyWeight = 1.0/k * 7.0/4; + else + butterflyWeight = butterflyWeights[0]; + + VecScaleAppend(p, butterflyWeight, aaPos[butterflyVertex]); + + // Ordered traversing of associated edges of currently considered irregular vertex vrt + for(size_t i = 1; i < k; ++i) + { + // Clear face containers + associatedFaces.clear(); + associatedManifoldFaces.clear(); + + // Get connecting edge of next butterfly vertex s_i in line to vrt + if(f->get_opposing_object(butterflyVertex).first != EDGE) + { + UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevelButterflyScheme3d: " + "Opposing object of butterfly vertex in manifold face is not an edge: " + << ElementDebugInfo(mg, f)) + } + + Edge* egdeOfNextFace = mg.get_edge(f, f->get_opposing_object(butterflyVertex).second); + + // Store butterfly vertex s_i + egdeOfNextFace->get_opposing_side(vrt, &butterflyVertex); + + // butterfly weight s_i + if(k != 3 && k != 4) + butterflyWeight = 1.0/k * (1.0/4 + cos(2*i*PI/k) + 1.0/2*cos(4*i*PI/k)); + else + butterflyWeight = butterflyWeights[i]; + + // Summate centroid of butterly vertices s_i + VecScaleAppend(q, butterflyWeight, aaPos[butterflyVertex]); + + // Get next face to traverse + GetNeighbours(associatedFaces, mg, f, GetEdgeIndex(f, egdeOfNextFace)); + + for(size_t j = 0; j < associatedFaces.size(); ++j) + { + // Only consider associated faces, which are marked as manifold faces + if(markSH.get_subset_index(associatedFaces[j]) != -1) + { + // Exclude ghost and horizontal slave manifold faces + #ifdef UG_PARALLEL + if(dgm.is_ghost(associatedFaces[j])) + continue; + + if(dgm.contains_status(associatedFaces[j], ES_H_SLAVE)) + continue; + #endif + + associatedManifoldFaces.push_back(associatedFaces[j]); + } + } + + if(associatedManifoldFaces.size() != 1) + UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevelButterflyScheme3d: number of edge associated Butterfly Manifold faces != 1."); + + // Store next face in line to traverse in next iteration + f = associatedManifoldFaces[0]; + } + + // Exclude ghost and horizontal slaves of the parent edge vertices of the currently smoothed vertex + // to avoid multiple contributions to the centroid of the edge adjacent vertices + #ifdef UG_PARALLEL + if(dgm.is_ghost(e)) + { + continue; + } + + if(dgm.contains_status(e, ES_H_SLAVE)) + { + VecScaleAppend(aaSmoothBndPosOddVrt[e], 1.0/numLoops, q); + continue; + } + #endif + + VecScaleAppend(aaSmoothBndPosOddVrt[e], 1.0/numLoops*3.0/4, aaPos[vrt], 1.0/numLoops, p, 1.0/numLoops, q); + } + else + UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevelButterflyScheme3d: numAssociatedManifoldFaces > 2."); + } + } + } + } + +// Manage vertex and edge attachment communication in parallel case -> COMMUNICATE aSmoothBndPosEvenVrt, aSmoothBndPosOddVrt + #ifdef UG_PARALLEL + // Reduce add operations: + // sum up h_slaves into h_masters + + // Copy operations: + // copy h_masters to h_slaves for consistency + AttachmentAllReduce(mg, aSmoothBndPosOddVrt, PCL_RO_SUM); + #endif +} + + //////////////////////////////////////////////////////////////////////////////// void CalculateSmoothManifoldPosInParentLevelLoopScheme2d(MultiGrid& mg, MGSubsetHandler& markSH, MGSubsetHandler& linearManifoldSH, @@ -625,10 +1032,17 @@ void CalculateSmoothManifoldPosInParentLevelLoopScheme2d(MultiGrid& mg, MGSubset APosition2& aSmoothBndPosOddVrt, AInt& aNumManifoldEdges) { + /* + * Scheme reference: + * + * C. Loop, Smooth subdivision surfaces based on triangles, + * master’s thesis, University of Utah, 1987. + */ + // Catch use of procedure for MultiGrids with just one level if(mg.num_levels() == 1) { - UG_THROW("Error in CalculateSmoothManifoldPosInParentLevel: " + UG_THROW("Error in CalculateSmoothManifoldPosInParentLevelLoopScheme2d: " "Procedure only to be used for MultiGrids with more than one level."); } @@ -775,7 +1189,7 @@ void CalculateSmoothManifoldPosInParentLevelLoopScheme2d(MultiGrid& mg, MGSubset { if(associatedManifoldFaces[i]->num_vertices() != 3) { - UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevel2d: " + UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevelLoopScheme2d: " "Non triangular faces included in grid: " << ElementDebugInfo(mg, associatedManifoldFaces[i])); } } @@ -804,7 +1218,7 @@ void CalculateSmoothManifoldPosInParentLevelLoopScheme2d(MultiGrid& mg, MGSubset VecScaleAppend(aaSmoothBndPosOddVrt[e], 0.375, aaPos[e->vertex(0)], 0.375, aaPos[e->vertex(1)], 0.125, p); } else - UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevel2d: numAssociatedManifoldFaces > 2."); + UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevelLoopScheme2d: numAssociatedManifoldFaces > 2."); } } @@ -828,10 +1242,17 @@ void CalculateSmoothManifoldPosInParentLevelLoopScheme3d(MultiGrid& mg, MGSubset APosition& aSmoothBndPosOddVrt, AInt& aNumManifoldEdges) { + /* + * Scheme reference: + * + * C. Loop, Smooth subdivision surfaces based on triangles, + * master’s thesis, University of Utah, 1987. + */ + // Catch use of procedure for MultiGrids with just one level if(mg.num_levels() == 1) { - UG_THROW("Error in CalculateSmoothManifoldPosInParentLevel: " + UG_THROW("Error in CalculateSmoothManifoldPosInParentLevelLoopScheme3d: " "Procedure only to be used for MultiGrids with more than one level."); } @@ -978,7 +1399,7 @@ void CalculateSmoothManifoldPosInParentLevelLoopScheme3d(MultiGrid& mg, MGSubset { if(associatedManifoldFaces[i]->num_vertices() != 3) { - UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevel3d: " + UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevelLoopScheme3d: " "Non triangular faces included in grid: " << ElementDebugInfo(mg, associatedManifoldFaces[i])); } } @@ -1007,7 +1428,7 @@ void CalculateSmoothManifoldPosInParentLevelLoopScheme3d(MultiGrid& mg, MGSubset VecScaleAppend(aaSmoothBndPosOddVrt[e], 0.375, aaPos[e->vertex(0)], 0.375, aaPos[e->vertex(1)], 0.125, p); } else - UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevel: numAssociatedManifoldFaces > 2."); + UG_THROW("ERROR in CalculateSmoothManifoldPosInParentLevelLoopScheme3d: numAssociatedManifoldFaces > 2."); } } @@ -1030,6 +1451,13 @@ void CalculateSmoothManifoldPosInTopLevelAveragingScheme2d(MultiGrid& mg, MGSubs APosition2& aSmoothBndPos_tri, APosition2& aSmoothBndPos_quad) { + /* + * Scheme reference: + * + * J. Warren and H. Weimer, Subdivision Methods for Geometric Design: A Constructive Approach, + * Morgan Kaufmann Publishers Inc., San Francisco, CA, USA, 1st ed., 2001. + */ + // Define attachment accessors Grid::VertexAttachmentAccessor aaPos(mg, aPosition2); Grid::VertexAttachmentAccessor aaSmoothBndPos_tri(mg, aSmoothBndPos_tri); @@ -1132,6 +1560,13 @@ void CalculateSmoothManifoldPosInTopLevelAveragingScheme3d(MultiGrid& mg, MGSubs APosition& aSmoothBndPos_tri, APosition& aSmoothBndPos_quad) { + /* + * Scheme reference: + * + * J. Warren and H. Weimer, Subdivision Methods for Geometric Design: A Constructive Approach, + * Morgan Kaufmann Publishers Inc., San Francisco, CA, USA, 1st ed., 2001. + */ + // Define attachment accessors Grid::VertexAttachmentAccessor aaPos(mg, aPosition); Grid::VertexAttachmentAccessor aaSmoothBndPos_tri(mg, aSmoothBndPos_tri); @@ -1234,6 +1669,15 @@ void CalculateSmoothVolumePosInTopLevel(MultiGrid& mg, MGSubsetHandler& markSH, APosition& aSmoothVolPos_prism, APosition& aSmoothVolPos_hex) { + /* + * Scheme references: + * + * S. Schaefer, J. Hakenberg, and J. Warren, Smooth subdivision of tetrahedral meshes, + * Proceedings of the 2004 Eurographics/ACM Symposium on Geometry Processing. + * + * J. Hakenberg, Smooth Subdivision for Mixed Volumetric Meshes, thesis, 2004 + */ + #ifdef UG_PARALLEL DistributedGridManager& dgm = *mg.distributed_grid_manager(); #endif @@ -1473,7 +1917,7 @@ void CalculateConstrainedSmoothVolumePosInTopLevel(MultiGrid& mg, MGSubsetHandle { if(GetVertexIndex(vol, oppVrt) == -1) { - UG_THROW("ERROR in CalculateSmoothVolumePosInTopLevel: identified opposing vertex actually not included in current volume."); + UG_THROW("ERROR in CalculateConstrainedSmoothVolumePosInTopLevel: identified opposing vertex actually not included in current volume."); } if(j != i && j != (size_t)GetVertexIndex(vol, oppVrt)) @@ -2129,6 +2573,132 @@ void ApplySmoothManifoldPosToTopLevelLoopScheme3d(MultiGrid& mg, MGSubsetHandler } +//////////////////////////////////////////////////////////////////////////////// +void ApplySmoothManifoldPosToTopLevelButterflyScheme3d(MultiGrid& mg, MGSubsetHandler& markSH, + MGSubsetHandler& linearManifoldSH) +{ +// Catch use of procedure for MultiGrids with just one level + if(mg.num_levels() == 1) + { + UG_THROW("Error in ApplySmoothManifoldPosToTopLevelButterflyScheme3d: " + "Procedure only to be used for MultiGrids with more than one level."); + } + + +/***************************************** + * + * (1) SETUP + * + *****************************************/ + +// Vertex attachments for associated number of manifold edges and smooth position +// (distinguish between volume and boundary smooth vertex positions +// and in case of boundary between EVEN and ODD smooth vertex positions) + AInt aNumManifoldEdges; + APosition aSmoothBndPosOddVrt; + +// attach previously declared vertex attachments with initial value 0 + mg.attach_to_vertices_dv(aNumManifoldEdges, 0); + mg.attach_to_edges_dv(aSmoothBndPosOddVrt, vector3(0, 0, 0)); + +// Define attachment accessors + Grid::VertexAttachmentAccessor aaPos(mg, aPosition); + Grid::EdgeAttachmentAccessor aaSmoothBndPosOddVrt(mg, aSmoothBndPosOddVrt); + +// Manage vertex attachment communication in parallel case: +// - Setup communication policy for the above attachment aPosition +// - Setup interface communicator +// - Setup distributed grid manager +// - Setup grid layout map + #ifdef UG_PARALLEL + // Attachment communication policies COPY + ComPol_CopyAttachment comPolCopyAPosition(mg, aPosition); + + // Interface communicators and distributed domain manager + pcl::InterfaceCommunicator com; + DistributedGridManager& dgm = *mg.distributed_grid_manager(); + GridLayoutMap& glm = dgm.grid_layout_map(); + #endif + + +/***************************************** + * + * (2) DETERMINE aNumManifoldEdges + * + *****************************************/ + + CalculateNumManifoldEdgesVertexAttachmentInParentLevel(mg, markSH, aNumManifoldEdges); + + +/***************************************** + * + * (3) CALCULATE aSmoothBndPosEvenVrt, + * aSmoothBndPosOddVrt + * + *****************************************/ + +// Calculate aSmoothBndPosOddVrt + CalculateSmoothManifoldPosInParentLevelButterflyScheme3d(mg, markSH, linearManifoldSH, aSmoothBndPosOddVrt, aNumManifoldEdges); + + +/***************************************** + * + * (4) APPLY + * + *****************************************/ + +// Loop all vertices of top_level + for(VertexIterator vrtIter = mg.begin(mg.top_level()); vrtIter != mg.end(mg.top_level()); ++vrtIter) + { + Vertex* vrt = *vrtIter; + + // Catch vertices without parent + if(mg.get_parent(vrt) == NULL) + continue; + + // In case of marked manifold vertices, which do not belong to the user-specified linear boundary manifold subsets, + // and activated Loop scheme refinement apply subdivision surfaces smoothing, else linear refinement + if(markSH.get_subset_index(vrt) != -1 && linearManifoldSH.get_subset_index(vrt) == -1) + { + // ODD VERTEX + if(mg.get_parent(vrt)->reference_object_id() == ROID_EDGE) + { + // Get parent edge + Edge* parentEdge = static_cast(mg.get_parent(vrt)); + + aaPos[vrt] = aaSmoothBndPosOddVrt[parentEdge]; + } + } + } + + +/***************************************** + * + * (5) COMMUNICATE VERTICALLY + * AFTER SUBDIVISION SURFACES + * + *****************************************/ + +// Communicate aPosition in parallel case + #ifdef UG_PARALLEL + // copy ghosts = VMASTER to v_slaves + com.exchange_data(glm, INT_V_MASTER, INT_V_SLAVE, comPolCopyAPosition); + com.communicate(); + #endif + + +/***************************************** + * + * (6) CLEAN UP + * + *****************************************/ + +// detach vertex attachments + mg.detach_from_vertices(aNumManifoldEdges); + mg.detach_from_edges(aSmoothBndPosOddVrt); +} + + //////////////////////////////////////////////////////////////////////////////// void ApplySmoothManifoldPosToTopLevelAveragingScheme2d(MultiGrid& mg, MGSubsetHandler& markSH, MGSubsetHandler& linearManifoldSH) @@ -2562,7 +3132,7 @@ void ApplySmoothSubdivisionSurfacesToTopLevel3d(MultiGrid& mg, MGSubsetHandler& // Catch use of procedure for MultiGrids with just one level if(mg.num_levels() == 1) { - UG_THROW("Error in ApplySmoothSubdivisionToTopLevel: " + UG_THROW("Error in ApplySmoothSubdivisionSurfacesToTopLevel3d: " "Procedure only to be used for MultiGrids with more than one level."); } @@ -2581,10 +3151,12 @@ void ApplySmoothSubdivisionSurfacesToTopLevel3d(MultiGrid& mg, MGSubsetHandler& ApplySmoothManifoldPosToTopLevelLoopScheme3d(mg, markSH, linearManifoldSH); else if(g_boundaryRefinementRule == SUBDIV_SURF_AVERAGING_SCHEME) ApplySmoothManifoldPosToTopLevelAveragingScheme3d(mg, markSH, linearManifoldSH); + else if(g_boundaryRefinementRule == SUBDIV_SURF_BUTTERFLY_SCHEME) + ApplySmoothManifoldPosToTopLevelButterflyScheme3d(mg, markSH, linearManifoldSH); else if(g_boundaryRefinementRule == SUBDIV_VOL){} else if(g_boundaryRefinementRule == LINEAR){} else - UG_THROW("ERROR in ApplySubdivisionSurfacesToTopLevel: Unknown boundary refinement rule. Known rules are 'subdiv_surf_loop_scheme', 'subdiv_surf_averaging_scheme' or 'linear'."); + UG_THROW("ERROR in ApplySmoothSubdivisionSurfacesToTopLevel3d: Unknown boundary refinement rule. Known rules are 'subdiv_surf_loop_scheme', 'subdiv_surf_averaging_scheme', 'subdiv_surf_butterfly_scheme' or 'linear'."); } @@ -2626,10 +3198,12 @@ void ApplySmoothSubdivisionVolumesToTopLevel(MultiGrid& mg, MGSubsetHandler& sh, ApplySmoothManifoldPosToTopLevelLoopScheme3d(mg, markSH, linearManifoldSH); else if(g_boundaryRefinementRule == SUBDIV_SURF_AVERAGING_SCHEME) ApplySmoothManifoldPosToTopLevelAveragingScheme3d(mg, markSH, linearManifoldSH); + else if(g_boundaryRefinementRule == SUBDIV_SURF_BUTTERFLY_SCHEME) + ApplySmoothManifoldPosToTopLevelButterflyScheme3d(mg, markSH, linearManifoldSH); else if(g_boundaryRefinementRule == SUBDIV_VOL){} else if(g_boundaryRefinementRule == LINEAR){} else - UG_THROW("ERROR in ApplySubdivisionVolumesToTopLevel: Unknown boundary refinement rule. Known rules are 'subdiv_surf_loop_scheme', 'subdiv_surf_averaging_scheme', 'linear' or 'subdiv_vol'."); + UG_THROW("ERROR in ApplySubdivisionVolumesToTopLevel: Unknown boundary refinement rule. Known rules are 'subdiv_surf_loop_scheme', 'subdiv_surf_averaging_scheme', 'subdiv_surf_butterfly_scheme', 'linear' or 'subdiv_vol'."); /***************************************** diff --git a/ugbase/lib_grid/algorithms/subdivision/subdivision_volumes.h b/ugbase/lib_grid/algorithms/subdivision/subdivision_volumes.h index f9bbaf022..9f6d16959 100644 --- a/ugbase/lib_grid/algorithms/subdivision/subdivision_volumes.h +++ b/ugbase/lib_grid/algorithms/subdivision/subdivision_volumes.h @@ -66,6 +66,7 @@ enum GlobalBoundaryRefinementRule LINEAR, SUBDIV_SURF_LOOP_SCHEME, SUBDIV_SURF_AVERAGING_SCHEME, + SUBDIV_SURF_BUTTERFLY_SCHEME, SUBDIV_VOL }; @@ -178,6 +179,22 @@ void CalculateSmoothManifoldPosInParentLevelLoopScheme3d(MultiGrid& mg, MGSubset AInt& aNumManifoldEdges); +/// Parent level vertex smoothing function for subdivision surfaces refinement (Butterfly scheme) +/** This function calculates the smoothed positions of all parent level vertices + * determined by the subdivision surfaces refinement. + * + * @param mg reference to MultiGrid + * @param markSH reference to SubsetHandler markSH containing marked (inner) boundary manifold + * @param linearManifoldSH reference to user-specified linearManifoldSubsets SubsetHandler + * @param aSmoothBndPosOddVrt reference to aSmoothBndPosOddVrt + * @param aNumManifoldEdges reference to aNumManifoldEdges +**/ +void CalculateSmoothManifoldPosInParentLevelButterflyScheme3d(MultiGrid& mg, MGSubsetHandler& markSH, + MGSubsetHandler& linearManifoldSH, + APosition& aSmoothBndPosOddVrt, + AInt& aNumManifoldEdges); + + /// Toplevel vertex smoothing function for subdivision surfaces refinement (Averaging scheme) /** This function calculates the smoothed positions of all toplevel vertices * determined by the subdivision surfaces refinement. @@ -291,6 +308,19 @@ void ApplySmoothManifoldPosToTopLevelLoopScheme3d(MultiGrid& mg, MGSubsetHandler MGSubsetHandler& linearManifoldSH); +/// Toplevel vertex repositioning function for subdivision surfaces refinement (Butterfly scheme) +/** This function repositions all toplevel manifold vertices to their smoothed positions + * determined by the subdivision surfaces refinement. + * + * @param mg reference to MultiGrid + * @param markSH reference to SubsetHandler markSH containing marked (inner) boundary manifold + * @param linearManifoldSH reference to user-specified linearManifoldSubsets SubsetHandler + * @param aSmoothBndPosOddVrt reference to aSmoothBndPosOddVrt +**/ +void ApplySmoothManifoldPosToTopLevelButterflyScheme3d(MultiGrid& mg, MGSubsetHandler& markSH, + MGSubsetHandler& linearManifoldSH); + + /// Toplevel vertex repositioning function for subdivision surfaces refinement (Averaging scheme) /** This function repositions all toplevel manifold vertices to their smoothed positions * determined by the subdivision surfaces refinement. diff --git a/ugbase/lib_grid/file_io/file_io_ugx_impl.hpp b/ugbase/lib_grid/file_io/file_io_ugx_impl.hpp index bc3f7cf98..6cd96985f 100644 --- a/ugbase/lib_grid/file_io/file_io_ugx_impl.hpp +++ b/ugbase/lib_grid/file_io/file_io_ugx_impl.hpp @@ -273,6 +273,7 @@ create_constrained_vertex_node(ConstrainedVertexIterator vrtsBegin, // write the vertices to a temporary stream stringstream ss; + ss.precision(18); for(ConstrainedVertexIterator iter = vrtsBegin; iter != vrtsEnd; ++iter) { for(int i = 0; i < numCoords; ++i) diff --git a/ugbase/lib_grid/refinement/hanging_node_refiner_base.cpp b/ugbase/lib_grid/refinement/hanging_node_refiner_base.cpp index 3d329a1b0..91fbe422e 100644 --- a/ugbase/lib_grid/refinement/hanging_node_refiner_base.cpp +++ b/ugbase/lib_grid/refinement/hanging_node_refiner_base.cpp @@ -769,7 +769,7 @@ void HangingNodeRefinerBase::perform_refinement() // a normal edge may have previously been created by replacing a // constrained or constraining edge. Those edges won't be considered here // FIXME: This is not correct at least for constrained edges! Remove marked_to_normal()! - if((!refinement_is_allowed(e)) || marked_to_normal(e)){ + if(!refinement_is_allowed(e)){ continue; } diff --git a/ugbase/lib_grid/refinement/projectors/elliptic_cylinder_projector.cpp b/ugbase/lib_grid/refinement/projectors/elliptic_cylinder_projector.cpp new file mode 100644 index 000000000..e8bc3334d --- /dev/null +++ b/ugbase/lib_grid/refinement/projectors/elliptic_cylinder_projector.cpp @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2016: G-CSC, Goethe University Frankfurt + * Author: Markus Breit + * + * This file is part of UG4. + * + * UG4 is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 3 (as published by the + * Free Software Foundation) with the following additional attribution + * requirements (according to LGPL/GPL v3 §7): + * + * (1) The following notice must be displayed in the Appropriate Legal Notices + * of covered and combined works: "Based on UG4 (www.ug4.org/license)". + * + * (2) The following notice must be displayed at a prominent place in the + * terminal output of covered works: "Based on UG4 (www.ug4.org/license)". + * + * (3) The following bibliography is recommended for citation and must be + * preserved in all covered files: + * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively + * parallel geometric multigrid solver on hierarchically distributed grids. + * Computing and visualization in science 16, 4 (2013), 151-164" + * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel + * flexible software system for simulating pde based models on high performance + * computers. Computing and visualization in science 16, 4 (2013), 165-179" + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ + +#include "elliptic_cylinder_projector.h" + +#include "common/math/misc/math_util.h" +#include "refinement_projector.h" + + +namespace ug { + + +EllipticCylinderProjector::EllipticCylinderProjector() +: m_center(0, 0, 0), + m_cylinder_axis(0, 0, 1), + m_ellipse_axis1(1, 0, 0), + m_ellipse_axis2(0, 1, 0), + m_radius(-1), + m_influenceRadius(-1), + m_ellipseNormal(0, 0, 1), + m_a(1.0), + m_b(1.0) +{} + + +EllipticCylinderProjector::EllipticCylinderProjector +( + const vector3& center, + const vector3& cylAxis, + const vector3& ellipseAxis1, + const vector3& ellipseAxis2 +) +: m_center(center), + m_cylinder_axis(cylAxis), + m_ellipse_axis1(ellipseAxis1), + m_ellipse_axis2(ellipseAxis2), + m_radius(-1), + m_influenceRadius(-1), + m_a(VecLength(ellipseAxis1)), + m_b(VecLength(ellipseAxis2)) +{ + VecCross(m_ellipseNormal, m_ellipse_axis1, m_ellipse_axis2); +} + + +EllipticCylinderProjector::EllipticCylinderProjector +( + const vector3& center, + const vector3& cylAxis, + const vector3& ellipseAxis1, + const vector3& ellipseAxis2, + number radius +) +: m_center(center), + m_cylinder_axis(cylAxis), + m_ellipse_axis1(ellipseAxis1), + m_ellipse_axis2(ellipseAxis2), + m_radius(radius), + m_influenceRadius(-1), + m_a(VecLength(ellipseAxis1)), + m_b(VecLength(ellipseAxis2)) +{ + UG_LOGN("Constructor5"); + VecCross(m_ellipseNormal, m_ellipse_axis1, m_ellipse_axis2); +} + + +EllipticCylinderProjector::EllipticCylinderProjector +( + const vector3& center, + const vector3& cylAxis, + const vector3& ellipseAxis1, + const vector3& ellipseAxis2, + number radius, + number influenceRadius +) +: m_center(center), + m_cylinder_axis(cylAxis), + m_ellipse_axis1(ellipseAxis1), + m_ellipse_axis2(ellipseAxis2), + m_radius(radius), + m_influenceRadius(influenceRadius), + m_a(VecLength(ellipseAxis1)), + m_b(VecLength(ellipseAxis2)) +{ + VecCross(m_ellipseNormal, m_ellipse_axis1, m_ellipse_axis2); +} + + +EllipticCylinderProjector::EllipticCylinderProjector +( + SPIGeometry3d geometry, + const vector3& center, + const vector3& cylAxis, + const vector3& ellipseAxis1, + const vector3& ellipseAxis2, + number radius, + number influenceRadius +) +: RefinementProjector(geometry), + m_center(center), + m_cylinder_axis(cylAxis), + m_ellipse_axis1(ellipseAxis1), + m_ellipse_axis2(ellipseAxis2), + m_radius(radius), + m_influenceRadius(influenceRadius), + m_a(VecLength(ellipseAxis1)), + m_b(VecLength(ellipseAxis2)) +{ + VecCross(m_ellipseNormal, m_ellipse_axis1, m_ellipse_axis2); +} + + +EllipticCylinderProjector::~EllipticCylinderProjector() +{} + + + +void EllipticCylinderProjector::set_center(const vector3& center) +{ + m_center = center; +} + +const vector3& EllipticCylinderProjector::center() const +{ + return m_center; +} + + +void EllipticCylinderProjector::set_cylinder_axis(const vector3& axis) +{ + m_cylinder_axis = axis; +} + +const vector3& EllipticCylinderProjector::cylinder_axis() const +{ + return m_cylinder_axis; +} + + +void EllipticCylinderProjector::set_ellipse_axis1(const vector3& axis) +{ + m_ellipse_axis1 = axis; + VecCross(m_ellipseNormal, m_ellipse_axis1, m_ellipse_axis2); + m_a = VecLength(axis); +} + +const vector3& EllipticCylinderProjector::ellipse_axis1() const +{ + return m_ellipse_axis1; +} + + +void EllipticCylinderProjector::set_ellipse_axis2(const vector3& axis) +{ + m_ellipse_axis2 = axis; + VecCross(m_ellipseNormal, m_ellipse_axis1, m_ellipse_axis2); + m_b = VecLength(axis); +} + +const vector3& EllipticCylinderProjector::ellipse_axis2() const +{ + return m_ellipse_axis2; +} + + +void EllipticCylinderProjector::set_radius(number radius) +{ + m_radius = radius; +} + +number EllipticCylinderProjector::radius() const +{ + return m_radius; +} + + +void EllipticCylinderProjector::set_influence_radius(number influenceRadius) +{ + m_influenceRadius = influenceRadius; +} + +number EllipticCylinderProjector::influence_radius() const +{ + return m_influenceRadius; +} + + + +number EllipticCylinderProjector::new_vertex(Vertex* vrt, Edge* parent) +{ + return perform_projection(vrt, parent); +} + +number EllipticCylinderProjector::new_vertex(Vertex* vrt, Face* parent) +{ + return perform_projection(vrt, parent); +} + +number EllipticCylinderProjector::new_vertex(Vertex* vrt, Volume* parent) +{ + return perform_projection(vrt, parent); +} + + + +number EllipticCylinderProjector::radial_ellipse_coord(const vector3& v) +{ + // project coordinates along cylinder axis to ellipse plane + vector3 proj2Ellipse; + number dummy; + RayPlaneIntersection(proj2Ellipse, dummy, v, m_cylinder_axis, m_center, m_ellipseNormal); + + // get x and y coordinates + const number x = VecDot(m_ellipse_axis1, proj2Ellipse) / m_a; + const number y = VecDot(m_ellipse_axis2, proj2Ellipse) / m_b; + + return sqrt(x*x/(m_a*m_a) + y*y/(m_b*m_b)); +} + + +number EllipticCylinderProjector::scale_point_to_radius(vector3& vIO, number r) +{ + // project coordinates along cylinder axis to ellipse plane + vector3 proj2Ellipse; + number dummy; + RayPlaneIntersection(proj2Ellipse, dummy, vIO, m_cylinder_axis, m_center, m_ellipseNormal); + + VecSubtract(vIO, vIO, proj2Ellipse); + + // current radius + const number x = VecDot(m_ellipse_axis1, proj2Ellipse) / m_a; + const number y = VecDot(m_ellipse_axis2, proj2Ellipse) / m_b; + const number rcur = sqrt(x*x/(m_a*m_a) + y*y/(m_b*m_b)); + + // scale and undo projection + if (rcur > SMALL * r) + VecScaleAdd(vIO, 1.0, vIO, r / rcur, proj2Ellipse); + else + // if current position is in the center of the cylinder, leave it there + VecAdd(vIO, vIO, m_center); + + return rcur; +} + + +template +number EllipticCylinderProjector::perform_projection(Vertex* vrt, TElem* parent) +{ + // General method: + // The "radius" of all parent vertices is averaged + // and used for the radius of the projected child. + // The (Cartesian) coordinates of all parents are averaged. + // The resulting coordinates are scaled around the axis + // within the ellipse plane to reach the previously determined radius. + // TODO: It might be better to also average arc lengths and axial coords of parents + // and project the child to averaged radius, arc length and axial coords. + + // average parent vertex positions and radii + typename TElem::ConstVertexArray vrts = parent->vertices(); + const size_t numVrts = parent->num_vertices(); + + if (numVrts == 0) + { + set_pos(vrt, vector3(0, 0, 0)); + return 1; + } + + number avgR = 0; + vector3 proj(0, 0, 0); + for (size_t i = 0; i < numVrts; ++i) + { + const vector3& p = pos(vrts[i]); + avgR += radial_ellipse_coord(p); + proj += p; + } + avgR /= numVrts; + VecScale(proj, proj, 1.0 / numVrts); + + // move averaged position to new radius + const number curR = scale_point_to_radius(proj, avgR); + set_pos(vrt, proj); + + if (m_influenceRadius > 0) + { + if (m_radius > m_influenceRadius) + { + const number dist = m_radius - m_influenceRadius; + return clip((curR - m_influenceRadius) / dist, 0, 1); + } + else if (m_radius >= 0) + { + const number dist = m_influenceRadius - m_radius; + if (dist > 0) + return clip(1 - (curR - m_radius) / dist, 0, 1); + return curR < m_radius ? 1 : 0; + } + else + return clip(1 - curR / m_influenceRadius, 0, 1); + } + + return 1; +} + + +} // namespace ug diff --git a/ugbase/lib_grid/refinement/projectors/elliptic_cylinder_projector.h b/ugbase/lib_grid/refinement/projectors/elliptic_cylinder_projector.h new file mode 100644 index 000000000..e51f9f753 --- /dev/null +++ b/ugbase/lib_grid/refinement/projectors/elliptic_cylinder_projector.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2016: G-CSC, Goethe University Frankfurt + * Author: Markus Breit + * + * This file is part of UG4. + * + * UG4 is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 3 (as published by the + * Free Software Foundation) with the following additional attribution + * requirements (according to LGPL/GPL v3 §7): + * + * (1) The following notice must be displayed in the Appropriate Legal Notices + * of covered and combined works: "Based on UG4 (www.ug4.org/license)". + * + * (2) The following notice must be displayed at a prominent place in the + * terminal output of covered works: "Based on UG4 (www.ug4.org/license)". + * + * (3) The following bibliography is recommended for citation and must be + * preserved in all covered files: + * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively + * parallel geometric multigrid solver on hierarchically distributed grids. + * Computing and visualization in science 16, 4 (2013), 151-164" + * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel + * flexible software system for simulating pde based models on high performance + * computers. Computing and visualization in science 16, 4 (2013), 165-179" + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ + +#ifndef UG__LIB_GRID__REFINEMENT__PROJECTORS__ELLIPTIC_CYLINDER_PROJECTOR_H +#define UG__LIB_GRID__REFINEMENT__PROJECTORS__ELLIPTIC_CYLINDER_PROJECTOR_H + +#include "common/math/math_vector_matrix/math_vector_functions.h" // VecLength etc. +#include "refinement_projector.h" + + +namespace ug { +/// Projects new vertices onto a cylinder with an elliptic base. +/** For projection during refinement the radius property is ignored. Instead + * the distance to the center of a newly inserted vertex is calculated + * as the average distance of the vertices of the parent element to the center. + * The radius property thus defaults to -1. + * + * You may still specify a radius. This radius can be used for auto-fitting of + * the center and for reprojecting a set of vertices onto the sphere. + */ +class EllipticCylinderProjector +: public RefinementProjector +{ + public: + EllipticCylinderProjector(); + + EllipticCylinderProjector + ( + const vector3& center, + const vector3& cylAxis, + const vector3& ellipseAxis1, + const vector3& ellipseAxis2 + ); + + EllipticCylinderProjector + ( + const vector3& center, + const vector3& cylAxis, + const vector3& ellipseAxis1, + const vector3& ellipseAxis2, + number radius + ); + + EllipticCylinderProjector + ( + const vector3& center, + const vector3& cylAxis, + const vector3& ellipseAxis1, + const vector3& ellipseAxis2, + number radius, + number influenceRadius + ); + + EllipticCylinderProjector + ( + SPIGeometry3d geometry, + const vector3& center, + const vector3& cylAxis, + const vector3& ellipseAxis1, + const vector3& ellipseAxis2, + number radius, + number influenceRadius + ); + + virtual ~EllipticCylinderProjector(); + + void set_center(const vector3& center); + const vector3& center() const; + + void set_cylinder_axis(const vector3& axis); + const vector3& cylinder_axis() const; + + void set_ellipse_axis1(const vector3& axis); + const vector3& ellipse_axis1() const; + + void set_ellipse_axis2(const vector3& axis); + const vector3& ellipse_axis2() const; + + void set_radius(number radius); + number radius() const; + + void set_influence_radius(number influenceRadius); + number influence_radius() const; + + + /// called when a new vertex was created from an old edge + virtual number new_vertex(Vertex* vrt, Edge* parent); + + /// called when a new vertex was created from an old face + virtual number new_vertex(Vertex* vrt, Face* parent); + + /// called when a new vertex was created from an old volume + virtual number new_vertex(Vertex* vrt, Volume* parent); + + + private: + number radial_ellipse_coord(const vector3& v); + number scale_point_to_radius(vector3& vIO, number r); + + template + number perform_projection(Vertex* vrt, TElem* parent); + + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) + { + ar & make_nvp("center", m_center); + ar & make_nvp("cylAxis", m_cylinder_axis); + ar & make_nvp("ellipseAxis1", m_ellipse_axis1); + ar & make_nvp("ellipseAxis2", m_ellipse_axis2); + ar & make_nvp("radius", m_radius); + ar & make_nvp("influence radius", m_influenceRadius); + UG_EMPTY_BASE_CLASS_SERIALIZATION(EllipticCylinderProjector, RefinementProjector); + + // this is only needed during load, but does not hurt during save either + VecCross(m_ellipseNormal, m_ellipse_axis1, m_ellipse_axis2); + m_a = VecLength(m_ellipse_axis1); + m_b = VecLength(m_ellipse_axis2); + } + + + private: + vector3 m_center; + vector3 m_cylinder_axis; + vector3 m_ellipse_axis1; + vector3 m_ellipse_axis2; + number m_radius; + number m_influenceRadius; + + vector3 m_ellipseNormal; + number m_a; + number m_b; +}; + +} // namespace ug + +#endif // UG__LIB_GRID__REFINEMENT__PROJECTORS__ELLIPTIC_CYLINDER_PROJECTOR_H diff --git a/ugbase/lib_grid/refinement/projectors/neurite_projector.cpp b/ugbase/lib_grid/refinement/projectors/neurite_projector.cpp new file mode 100644 index 000000000..c058851bb --- /dev/null +++ b/ugbase/lib_grid/refinement/projectors/neurite_projector.cpp @@ -0,0 +1,1136 @@ +/* + * Copyright (c) 2016: G-CSC, Goethe University Frankfurt + * Author: Markus Breit + * + * This file is part of UG4. + * + * UG4 is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 3 (as published by the + * Free Software Foundation) with the following additional attribution + * requirements (according to LGPL/GPL v3 §7): + * + * (1) The following notice must be displayed in the Appropriate Legal Notices + * of covered and combined works: "Based on UG4 (www.ug4.org/license)". + * + * (2) The following notice must be displayed at a prominent place in the + * terminal output of covered works: "Based on UG4 (www.ug4.org/license)". + * + * (3) The following bibliography is recommended for citation and must be + * preserved in all covered files: + * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively + * parallel geometric multigrid solver on hierarchically distributed grids. + * Computing and visualization in science 16, 4 (2013), 151-164" + * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel + * flexible software system for simulating pde based models on high performance + * computers. Computing and visualization in science 16, 4 (2013), 165-179" + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * Created on: 2016-12-19 + */ + +#include "neurite_projector.h" +#include "common/error.h" +#include "lib_grid/global_attachments.h" +#include "lib_grid/algorithms/debug_util.h" // ElementDebugInfo + +#include +#include + +namespace ug { + +static number VecProd(const vector3& a, const vector3& b) +{ + return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; +} + +static number VecNormSquared(const vector3& a) +{ + return a[0]*a[0] + a[1]*a[1] + a[2]*a[2]; +} + +static number VecNorm(const vector3& a) +{ + return sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]); +} + + +NeuriteProjector::NeuriteProjector() +//: m_quadOrder(80) +{ + typedef Attachment NPSurfParam; + if (!GlobalAttachments::is_declared("npSurfParams")) + GlobalAttachments::declare_attachment("npSurfParams", true); + + // If branching points extend 5r in each direction and we integrate over twice that size + // we add around quadOrder/2 Gaussians with sigma=r over a total length of 20r. + // So we should use about quadOrder=80 to ensure a smooth surface + // that does not look like a pearl necklace. + prepare_quadrature(); +} + + +NeuriteProjector::NeuriteProjector(SPIGeometry3d geometry) +: RefinementProjector(geometry) + //m_quadOrder(80) +{ + typedef Attachment NPSurfParam; + if (!GlobalAttachments::is_declared("npSurfParams")) + GlobalAttachments::declare_attachment("npSurfParams", true); + + attach_surf_params(); + prepare_quadrature(); +} + + +NeuriteProjector::~NeuriteProjector() +{} + + +void NeuriteProjector::set_geometry(SPIGeometry3d geometry) +{ + // call base class method + RefinementProjector::set_geometry(geometry); + + attach_surf_params(); +} + + + +number NeuriteProjector::new_vertex(Vertex* vrt, Edge* parent) +{ + return push_into_place(vrt, parent); +} + + +number NeuriteProjector::new_vertex(Vertex* vrt, Face* parent) +{ + return push_into_place(vrt, parent); +} + + +number NeuriteProjector::new_vertex(Vertex* vrt, Volume* parent) +{ + return push_into_place(vrt, parent); +} + + +void NeuriteProjector::direction_at_grid_object(vector3& dirOut, GridObject* o) const +{ + // treat vertex separately as it is no vertex group + if (o->base_object_id() == VERTEX) + { + Vertex* v = dynamic_cast(o); + UG_COND_THROW(!v, "Non-vertex with VERTEX base object id.") + + const SurfaceParams& sp = m_aaSurfParams[v]; + uint32 nid = sp.neuriteID; + float t = sp.axial; + + const Section& sec = get_section((uint32) nid, t); + + number te = sec.endParam; + const number* s = &sec.splineParamsX[0]; + number& v0 = dirOut[0]; + v0 = -3.0*s[0]*(te-t) - 2.0*s[1]; + v0 = v0*(te-t) - s[2]; + + s = &sec.splineParamsY[0]; + number& v1 = dirOut[1]; + v1 = -3.0*s[0]*(te-t) - 2.0*s[1]; + v1 = v1*(te-t) - s[2]; + + s = &sec.splineParamsZ[0]; + number& v2 = dirOut[2]; + v2 = -3.0*s[0]*(te-t) - 2.0*s[1]; + v2 = v2*(te-t) - s[2]; + + return; + } + + IVertexGroup* vrtGrp = dynamic_cast(o); + UG_COND_THROW(!vrtGrp, "Non-vertex element which is not a vertex group."); + + size_t nid; + float t, dummy1, dummy2; + average_params(nid, t, dummy1, dummy2, vrtGrp); + + const Section& sec = get_section((uint32)nid, t); + + number te = sec.endParam; + const number* s = &sec.splineParamsX[0]; + number& v0 = dirOut[0]; + v0 = -3.0*s[0]*(te-t) - 2.0*s[1]; + v0 = v0*(te-t) - s[2]; + + s = &sec.splineParamsY[0]; + number& v1 = dirOut[1]; + v1 = -3.0*s[0]*(te-t) - 2.0*s[1]; + v1 = v1*(te-t) - s[2]; + + s = &sec.splineParamsZ[0]; + number& v2 = dirOut[2]; + v2 = -3.0*s[0]*(te-t) - 2.0*s[1]; + v2 = v2*(te-t) - s[2]; +} + + + +void NeuriteProjector::attach_surf_params() +{ + Grid& grid = this->geometry()->grid(); + + UG_COND_THROW(!GlobalAttachments::is_declared("npSurfParams"), + "GlobalAttachment 'npSurfParams' not declared."); + m_aSurfParams = GlobalAttachments::attachment >("npSurfParams"); + + // make sure surfaceParams attachment is attached + UG_COND_THROW(!grid.has_vertex_attachment(m_aSurfParams), + "No surface parameter attachment for neurite projector attached to grid."); + + m_aaSurfParams.access(grid, m_aSurfParams); + + // handle attachment values also on higher grid levels (if required) + MultiGrid* mg = dynamic_cast(&grid); + if (mg) + { + SmartPtr spMG(mg); + + // never destroy the grid from here - we did not create it + ++(*spMG.refcount_ptr()); + + m_cah.set_attachment(m_aSurfParams); + m_cah.set_grid(spMG); + } +} + + +void NeuriteProjector::debug_neurites() const +{ + UG_LOGN("BPs of neurites in projector:") + // print out branching region data + for (size_t n = 0; n < m_vNeurites.size(); ++n) + { + UG_LOGN("Neurite " << n); + const NeuriteProjector::Neurite& neurite = m_vNeurites[n]; + const std::vector vBR = neurite.vBR; + + for (size_t b = 0; b < vBR.size(); ++b) + { + UG_LOGN(" BR " << b << ": " << vBR[b].tstart << ".." << vBR[b].tend); + SmartPtr bp = vBR[b].bp; + + UG_LOGN(" associated BP data:") + size_t bpSz = bp->vNid.size(); + if (bpSz != bp->vRegions.size()) + { + UG_LOGN( "Size mismatch: vNid " << bpSz << ", vRegions " << bp->vRegions.size()); + } + else + { + for (size_t i = 0; i < bpSz; ++i) + { + UG_LOGN(" " << bp->vNid[i] << " (" << bp->vRegions[i]->tstart + << ", " << bp->vRegions[i]->tend << ")"); + } + } + } + } +} + + +void NeuriteProjector::add_neurite(const Neurite& n) +{ + m_vNeurites.push_back(n); +} + + +const NeuriteProjector::Neurite& NeuriteProjector::neurite(size_t nid) const +{ + UG_COND_THROW(nid >= m_vNeurites.size(), + "Requested neurite index " << nid << " that is not present in the neurite."); + return m_vNeurites[nid]; +} + + +Grid::VertexAttachmentAccessor >& +NeuriteProjector::surface_params_accessor() +{ + return m_aaSurfParams; +} + + +const Grid::VertexAttachmentAccessor >& +NeuriteProjector::surface_params_accessor() const +{ + return m_aaSurfParams; +} + + +const std::vector >& NeuriteProjector::quadrature_points() const +{ + return m_qPoints; +}; + + + +const NeuriteProjector::Section& +NeuriteProjector::get_section(uint32 nid, float t) const +{ + Section cmpSec(t); + const std::vector
& vSections = m_vNeurites[nid].vSec; + std::vector
::const_iterator itSec = + std::lower_bound(vSections.begin(), vSections.end(), cmpSec, CompareSections()); + + UG_COND_THROW(itSec == vSections.end(), + "Could not find section for parameter t = " << t << " in neurite " << nid << "."); + + return *itSec; +} + + + +static bool cmpQPairs(const std::pair& a, const std::pair& b) +{return a.first < b.first;} + +void NeuriteProjector::prepare_quadrature() +{ + /* + GaussLegendre gl(m_quadOrder); + size_t nPts = gl.size(); + m_qPoints.resize(nPts); + for (size_t i = 0; i < nPts; ++i) + { + m_qPoints[i].first = gl.point(i)[0]; + m_qPoints[i].second = gl.weight(i); + } + */ + m_qPoints.resize(40); + m_qPoints[0].first = 4.8061379124697458903340327798768835266e-1; + m_qPoints[1].first = 5.1938620875302541096659672201231164734e-1; + m_qPoints[2].first = 4.4195796466237239575827435779598794312e-1; + m_qPoints[3].first = 5.5804203533762760424172564220401205688e-1; + m_qPoints[4].first = 4.0365120964931445014224157396742505259e-1; + m_qPoints[5].first = 5.9634879035068554985775842603257494741e-1; + m_qPoints[6].first = 3.6592390749637315942940782759570190829e-1; + m_qPoints[7].first = 6.3407609250362684057059217240429809171e-1; + m_qPoints[8].first = 3.2900295458712076349625375941040284497e-1; + m_qPoints[9].first = 6.7099704541287923650374624058959715503e-1; + m_qPoints[10].first = 2.9311039781419749923756012709814315851e-1; + m_qPoints[11].first = 7.0688960218580250076243987290185684149e-1; + m_qPoints[12].first = 2.584620991569106435457167128775884977e-1; + m_qPoints[13].first = 7.415379008430893564542832871224115023e-1; + m_qPoints[14].first = 2.2526643745243589896203434723524101488e-1; + m_qPoints[15].first = 7.7473356254756410103796565276475898512e-1; + m_qPoints[16].first = 1.9372305516600988102369377488465256131e-1; + m_qPoints[17].first = 8.0627694483399011897630622511534743869e-1; + m_qPoints[18].first = 1.6402165769291022581032274251925294501e-1; + m_qPoints[19].first = 8.3597834230708977418967725748074705499e-1; + m_qPoints[20].first = 1.3634087240503644835950177412253472572e-1; + m_qPoints[21].first = 8.6365912759496355164049822587746527428e-1; + m_qPoints[22].first = 1.1084717428674030615251422724675257599e-1; + m_qPoints[23].first = 8.8915282571325969384748577275324742401e-1; + m_qPoints[24].first = 8.7693884583344168401839884666950613046e-2; + m_qPoints[25].first = 9.1230611541665583159816011533304938695e-1; + m_qPoints[26].first = 6.7020248393870248089609095822690018215e-2; + m_qPoints[27].first = 9.3297975160612975191039090417730998179e-1; + m_qPoints[28].first = 4.8950596515562851635873334565753448208e-2; + m_qPoints[29].first = 9.5104940348443714836412666543424655179e-1; + m_qPoints[30].first = 3.3593595860661733319573916577397141783e-2; + m_qPoints[31].first = 9.6640640413933826668042608342260285822e-1; + m_qPoints[32].first = 2.1041590393104172097729500273620357453e-2; + m_qPoints[33].first = 9.7895840960689582790227049972637964255e-1; + m_qPoints[34].first = 1.1370025008112868668314858143548096511e-2; + m_qPoints[35].first = 9.8862997499188713133168514185645190349e-1; + m_qPoints[36].first = 4.6368806502714967734728238893139225189e-3; + m_qPoints[37].first = 9.9536311934972850322652717611068607748e-1; + m_qPoints[38].first = 8.8114514472039982518864878970675383211e-4; + m_qPoints[39].first = 9.9911885485527960017481135121029324617e-1; + + m_qPoints[0].second = 3.8752973989212405631861981479163163482e-2; + m_qPoints[1].second = 3.8752973989212405631861981479163163482e-2; + m_qPoints[2].second = 3.8519909082123982794153767141905124262e-2; + m_qPoints[3].second = 3.8519909082123982794153767141905124262e-2; + m_qPoints[4].second = 3.8055180950313121185779037961247411506e-2; + m_qPoints[5].second = 3.8055180950313121185779037961247411506e-2; + m_qPoints[6].second = 3.7361584528984132100094668130662336596e-2; + m_qPoints[7].second = 3.7361584528984132100094668130662336596e-2; + m_qPoints[8].second = 3.6443291197902029530255341721258917929e-2; + m_qPoints[9].second = 3.6443291197902029530255341721258917929e-2; + m_qPoints[10].second = 3.530582369564338984774181542764341618e-2; + m_qPoints[11].second = 3.530582369564338984774181542764341618e-2; + m_qPoints[12].second = 3.3956022907616951912845054115961992992e-2; + m_qPoints[13].second = 3.3956022907616951912845054115961992992e-2; + m_qPoints[14].second = 3.2402006728300519037277264783376365016e-2; + m_qPoints[15].second = 3.2402006728300519037277264783376365016e-2; + m_qPoints[16].second = 3.0653121246464469583268998204199297951e-2; + m_qPoints[17].second = 3.0653121246464469583268998204199297951e-2; + m_qPoints[18].second = 2.87198845496957756833088654552129928e-2; + m_qPoints[19].second = 2.87198845496957756833088654552129928e-2; + m_qPoints[20].second = 2.6613923491968412177498239886130252278e-2; + m_qPoints[21].second = 2.6613923491968412177498239886130252278e-2; + m_qPoints[22].second = 2.4347903817536116030717080224073194034e-2; + m_qPoints[23].second = 2.4347903817536116030717080224073194034e-2; + m_qPoints[24].second = 2.1935454092836635995837343020857747906e-2; + m_qPoints[25].second = 2.1935454092836635995837343020857747906e-2; + m_qPoints[26].second = 1.9391083987236008819986015645223081127e-2; + m_qPoints[27].second = 1.9391083987236008819986015645223081127e-2; + m_qPoints[28].second = 1.6730097641273923696339091543205424489e-2; + m_qPoints[29].second = 1.6730097641273923696339091543205424489e-2; + m_qPoints[30].second = 1.3968503490011700549244578753860538651e-2; + m_qPoints[31].second = 1.3968503490011700549244578753860538651e-2; + m_qPoints[32].second = 1.1122924597083478630752162092104286604e-2; + m_qPoints[33].second = 1.1122924597083478630752162092104286604e-2; + m_qPoints[34].second = 8.2105291909539443564317424411819636462e-3; + m_qPoints[35].second = 8.2105291909539443564317424411819636462e-3; + m_qPoints[36].second = 5.2491422655764068073710855336398261884e-3; + m_qPoints[37].second = 5.2491422655764068073710855336398261884e-3; + m_qPoints[38].second = 2.2606385492665956292358664390926663639e-3; + + std::sort(m_qPoints.begin(), m_qPoints.end(), cmpQPairs); +} + +static void compute_section_end_points +( + vector3& pos0Out, + vector3& pos1Out, + std::vector::const_iterator secIt +) +{ + number te = secIt->endParam; + const number* s = &secIt->splineParamsX[0]; + number& p0 = pos0Out[0]; + p0 = s[0]*te + s[1]; + p0 = p0*te + s[2]; + p0 = p0*te + s[3]; + pos1Out[0] = s[3]; + + s = &secIt->splineParamsY[0]; + number& p1 = pos0Out[1]; + p1 = s[0]*te + s[1]; + p1 = p1*te + s[2]; + p1 = p1*te + s[3]; + pos1Out[1] = s[3]; + + s = &secIt->splineParamsZ[0]; + number& p2 = pos0Out[2]; + p2 = s[0]*te + s[1]; + p2 = p2*te + s[2]; + p2 = p2*te + s[3]; + pos1Out[2] = s[3]; +} + + +static void compute_position_and_velocity_in_section +( + vector3& posAxOut, + vector3& velOut, + number& radiusOut, + std::vector::const_iterator secIt, + float t +) +{ + number te = secIt->endParam; + const number* s = &secIt->splineParamsX[0]; + number& p0 = posAxOut[0]; + number& v0 = velOut[0]; + p0 = s[0]*(te-t) + s[1]; v0 = -3.0*s[0]*(te-t) - 2.0*s[1]; + p0 = p0*(te-t) + s[2]; v0 = v0*(te-t) - s[2]; + p0 = p0*(te-t) + s[3]; + + s = &secIt->splineParamsY[0]; + number& p1 = posAxOut[1]; + number& v1 = velOut[1]; + p1 = s[0]*(te-t) + s[1]; v1 = -3.0*s[0]*(te-t) - 2.0*s[1]; + p1 = p1*(te-t) + s[2]; v1 = v1*(te-t) - s[2]; + p1 = p1*(te-t) + s[3]; + + s = &secIt->splineParamsZ[0]; + number& p2 = posAxOut[2]; + number& v2 = velOut[2]; + p2 = s[0]*(te-t) + s[1]; v2 = -3.0*s[0]*(te-t) - 2.0*s[1]; + p2 = p2*(te-t) + s[2]; v2 = v2*(te-t) - s[2]; + p2 = p2*(te-t) + s[3]; + + s = &secIt->splineParamsR[0]; + radiusOut = s[0]*(te-t) + s[1]; + radiusOut = radiusOut*(te-t) + s[2]; + radiusOut = radiusOut*(te-t) + s[3]; +} + + +static inline void compute_ONB +( + vector3& firstOut, + vector3& secondOut, + vector3& thirdOut, + const vector3& firstIn, + const vector3& refDir +) +{ + VecNormalize(firstOut, firstIn); + number fac = VecProd(refDir, firstOut); + VecScaleAdd(secondOut, 1.0, refDir, -fac, firstOut); + VecNormalize(secondOut, secondOut); + + VecCross(thirdOut, firstOut, secondOut); +} + + +// computes the polar angle phi of a vector +// given a rotational axis direction and two additional +// directions, together being a orthoNORMAL basis +static inline number compute_angle +( + const vector3& axis, + const vector3& secondAxis, + const vector3& thirdAxis, + vector3& posFromAxis +) +{ + // eliminate component in axis direction + VecScaleAdd(posFromAxis, 1.0, posFromAxis, -VecProd(posFromAxis, axis), axis); + + // compute coord vector (components 2 and 3) w.r.t. ONB + vector2 relCoord; + relCoord[0] = VecProd(posFromAxis, secondAxis); + VecScaleAdd(posFromAxis, 1.0, posFromAxis, -relCoord[0], secondAxis); + relCoord[1] = VecProd(posFromAxis, thirdAxis); + VecNormalize(relCoord, relCoord); + + // get angle from coord vector + number angle; + if (fabs(relCoord[0]) < 1e-8) + angle = relCoord[1] < 0 ? 1.5*PI : 0.5*PI; + else + angle = relCoord[0] < 0 ? PI - atan(-relCoord[1]/relCoord[0]) : atan(relCoord[1]/relCoord[0]); + + // angle should be in [0,2*PI) + if (angle < 0) angle += 2.0*PI; + + return angle; +} + + +void NeuriteProjector::average_params +( + size_t& neuriteID, + float& t, + float& angle, + float& radius, + const IVertexGroup* parent +) const +{ + // branching vertices need to belong to parent branch + // AND parent branch neurite index must be smaller than child's index + + // find neurite ID parent belongs to + neuriteID = 0; + size_t nVrt = parent->num_vertices(); + for (size_t i = 0; i < nVrt; ++i) + { + const SurfaceParams& surfParams = m_aaSurfParams[parent->vertex(i)]; + size_t nid = (size_t) surfParams.neuriteID; + neuriteID = std::max(neuriteID, nid); + } + + // average t, phi, r + t = 0.0; + vector2 v(0.0); + radius = 0.0; + for (size_t i = 0; i < nVrt; ++i) + { + const SurfaceParams& surfParams = m_aaSurfParams[parent->vertex(i)]; + size_t nid = (size_t) surfParams.neuriteID; + if (nid == neuriteID) + { + t += surfParams.axial; + radius += surfParams.radial; + if (surfParams.radial > 0) + { + const float phi = surfParams.angular; + VecAdd(v, v, vector2(cos(phi), sin(phi))); + } + } + else + { + // Here we are on a branching vertex that belongs to the parent. + // We could just use t=0 here, but this is imprecise as t = 0 + // is in the middle of the parent neurite and also axially set off, + // so we calculate the actual t and phi params here. + std::vector
::const_iterator secIt = m_vNeurites[neuriteID].vSec.begin(); + vector3 pos0, pos1; + compute_section_end_points(pos0, pos1, secIt); + const vector3& vrtPos = this->pos(parent->vertex(i)); + VecSubtract(pos1, pos1, pos0); + VecSubtract(pos0, vrtPos, pos0); + number axPos = VecProd(pos0, pos1) / VecNormSquared(pos1) * secIt->endParam; + + vector3 vel; + number surfRadius; + compute_position_and_velocity_in_section(pos1, vel, surfRadius, secIt, axPos); + const vector3& refDir = m_vNeurites[neuriteID].refDir; + vector3 posRefDir, thirdDir; + compute_ONB(vel, posRefDir, thirdDir, vel, refDir); + vector3 posFromAxis; + VecSubtract(posFromAxis, vrtPos, pos1); + number phi = compute_angle(vel, posRefDir, thirdDir, posFromAxis); + + t += axPos; + VecAdd(v, v, vector2(cos(phi), sin(phi))); + radius += VecNorm(posFromAxis) / surfRadius; + } + } + t /= nVrt; + + if (fabs(v[0]) < 1e-8) + { + if (fabs(v[1]) < 1e-8) + angle = 0.0; // center angle is 0.0 by default + else + angle = v[1] < 0 ? 1.5*PI : 0.5*PI; + } + else + angle = v[0] < 0 ? PI - atan(-v[1]/v[0]) : atan(v[1]/v[0]); + + if (angle < 0) angle += 2.0*PI; + + radius /= nVrt; +} + + +void NeuriteProjector::average_pos_from_parent(vector3& posOut, const IVertexGroup* parent) const +{ + posOut = 0.0; + size_t nVrt = parent->num_vertices(); + for (size_t i = 0; i < nVrt; ++i) + posOut += this->pos(parent->vertex(i)); + posOut /= (number) nVrt; +} + + +vector3 NeuriteProjector::position(Vertex* vrt) const +{ + return this->pos(vrt); +} + + + +static void bp_defect_and_gradient +( + number& defectOut, + vector3& gradientOut, + const std::vector& bpList, + const vector3& x, + number rad, + const NeuriteProjector* np +) +{ + // get integration rule points and weights + const std::vector >& qPoints = np->quadrature_points(); + size_t nPts = qPoints.size(); + + // evaluate integrand at points + defectOut = 0.0; + gradientOut = 0.0; + size_t nParts = bpList.size(); + for (size_t i = 0; i < nParts; ++i) + { + const NeuriteProjector::BPProjectionHelper& helper = bpList[i]; + const number& start = helper.start; + const number& end = helper.end; + std::vector::const_iterator secIt = helper.sec_start; + + // iterate IPs + for (size_t j = 0; j < nPts; ++j) + { + // transform point coords to current context + float t = (float) (start + qPoints[j].first*(end-start)); + while (secIt->endParam < t) ++secIt; // this should be safe as last section must end with 1.0 + + vector3 posAx, vel, blub; + number radius; + compute_position_and_velocity_in_section(posAx, vel, radius, secIt, t); + + number radInv = 1.0 / radius; + VecSubtract(posAx, posAx, x); + number posNormSq = VecNormSquared(posAx); + number velNorm = sqrt(VecNormSquared(vel)); + number integrandVal = radInv*exp(-posNormSq*radInv*radInv) * velNorm; + number gradIntegrandVal = 2.0*radInv*radInv * integrandVal; + VecScale(blub, posAx, gradIntegrandVal); + + number w = qPoints[j].second; + defectOut += integrandVal * w * (end-start); + VecScaleAdd(gradientOut, 1.0, gradientOut, w * (end-start), blub); +/* + UG_LOGN(" Ival: " << integrandVal << ", iExp: " << -posNormSq*radInv*radInv + << ", velNorm: " << velNorm << ", weight: " << w + << ", t: " << t << ", intvl: " << end-start + << ", posAx: " << posAx); +*/ + } + } + + defectOut -= sqrt(PI)*exp(-rad*rad); +} + + +/* +static void pos_on_neurite_spine +( + vector3& posOut, + const NeuriteProjector::Neurite& neurite, + size_t neuriteID, + float& t +) +{ + const std::vector& vSections = neurite.vSec; + + // find correct section + NeuriteProjector::Section cmpSec(t); + std::vector::const_iterator it = + std::lower_bound(vSections.begin(), vSections.end(), cmpSec, NeuriteProjector::CompareSections()); + + UG_COND_THROW(it == vSections.end(), + "Could not find section for parameter t = " << t << " in neurite " << neuriteID << "."); + + // calculate correct axial position + // and velocity dir + vector3 vel_dummy; + number radius_dummy; + compute_position_and_velocity_in_section(posOut, vel_dummy, radius_dummy, it, t); +} +*/ + + +static void newton_for_bp_projection +( + vector3& posOut, + const std::vector& vProjHelp, + number rad, + const NeuriteProjector* np +) +{ + vector3 posStart = posOut; + // calculate start defect and gradient + number def; + number def_init; + vector3 grad; + bp_defect_and_gradient(def, grad, vProjHelp, posOut, rad, np); + def_init = fabs(def); + + // perform some kind of Newton search for the correct position + size_t maxIt = 100; + number minDef = 1e-8*sqrt(PI)*exp(-1.0); + size_t iter = 0; + while (fabs(def) > minDef && fabs(def) > 1e-8 * def_init && ++iter <= maxIt) + { +//UG_LOGN(iter << " " << def << " " << grad << " " << posOut); + vector3 posTest; + vector3 gradTest; + number defTest; + VecScaleAdd(posTest, 1.0, posOut, -def / VecNormSquared(grad), grad); + bp_defect_and_gradient(defTest, gradTest, vProjHelp, posTest, rad, np); + + // line search + number lambda = 0.5; + number bestDef = defTest; + vector3 bestGrad = gradTest; + vector3 bestPos = posTest; + while (fabs(defTest) >= fabs(def) && lambda > 0.001) + { +//UG_LOGN(" line search with lambda = " << lambda); + VecScaleAdd(posTest, 1.0, posOut, -lambda*def / VecNormSquared(grad), grad); + bp_defect_and_gradient(defTest, gradTest, vProjHelp, posTest, rad, np); + if (fabs(defTest) < fabs(bestDef)) + { + bestDef = defTest; + bestGrad = gradTest; + bestPos = posTest; + } + lambda *= 0.5; + } + def = bestDef; + grad = gradTest; + posOut = bestPos; + } + UG_COND_THROW(def != def, + "Newton iteration did not converge for branching point projection at " << posStart << ". Defect is NaN.") + UG_COND_THROW(fabs(def) > minDef && fabs(def) > 1e-8 * def_init, + "Newton iteration did not converge for branching point projection at " << posStart << ".") +} + + +static void pos_in_neurite +( + vector3& posOut, + const NeuriteProjector::Neurite& neurite, + size_t neuriteID, + float& t, + float& angle, + float& rad +) +{ + const std::vector& vSections = neurite.vSec; + + // find correct section + NeuriteProjector::Section cmpSec(t); + std::vector::const_iterator it = + std::lower_bound(vSections.begin(), vSections.end(), cmpSec, NeuriteProjector::CompareSections()); + + UG_COND_THROW(it == vSections.end(), + "Could not find section for parameter t = " << t << " in neurite " << neuriteID << "."); + + // calculate correct axial position + // and velocity dir + vector3 posAx, vel; + number radius; + compute_position_and_velocity_in_section(posAx, vel, radius, it, t); + + // calculate orthonormal basis vectors + vector3 projRefDir, thirdDir; + compute_ONB(vel, projRefDir, thirdDir, vel, neurite.refDir); + + // calculate new position + VecScaleAdd(posOut, 1.0, posAx, rad*radius*cos(angle), projRefDir, rad*radius*sin(angle), thirdDir); +} + +/* does not work as well as expected +static void bp_newton_start_pos +( + vector3& initOut, + const std::vector& vProjHelp, + size_t neuriteID, + float angle, + const IVertexGroup* parent, + const NeuriteProjector* np +) +{ + // If t1, t2 are the axial positions of the (edge) parent vertices, + // then construct the spline vectors v1, v2 corresponding to these + // axial coordinates. Then construct the spline vector v3 corresponding + // to t3 = 0.5*(t1+t2) and calculate: + // q = ||v3 - 0.5*(v1+v2)|| / ||v1 - v2|| + // Then if q is smaller than some threshold value, the underlying + // spline is quasi-linear on the parent, so we can use the averaged + // vertex positions as initial solution. + // Otherwise, we take the neurite surface position of t3. + // Or, even better, some weighted version of this which coincides + // with the first case if its condition applies. + size_t nVrt = parent->num_vertices(); + float t_avg = 0.0; + vector3 pos_avg(0.0); + std::vector vAxParam(nVrt, 0.0); + std::vector vAxPos(nVrt, 0.0); + for (size_t i = 0; i < nVrt; ++i) + { + float& t = vAxParam[i]; + const NeuriteProjector::SurfaceParams& surfParams = np->surface_params_accessor()[parent->vertex(i)]; + size_t nid = (size_t) surfParams.neuriteID; + if (nid == neuriteID) + { + t = surfParams.axial; + t_avg += t; + } + else + { + // Here we are on a branching vertex that belongs to the parent. + // We could just use t=0 here, but this is imprecise as t = 0 + // is in the middle of the parent neurite and also axially set off, + // so we calculate the actual t param here (approx.). + vector3 pos0, pos1; + std::vector::const_iterator secIt = np->neurite(neuriteID).vSec.begin(); + compute_section_end_points(pos0, pos1, secIt); + const vector3& vrtPos = np->position(parent->vertex(i)); + VecSubtract(pos1, pos1, pos0); + VecSubtract(pos0, vrtPos, pos0); + number axPos = VecProd(pos0, pos1) / VecNormSquared(pos1) * secIt->endParam; + + t = axPos; + t_avg += t; + } + + vector3& spine_pos = vAxPos[i]; + pos_on_neurite_spine(spine_pos, np->neurite(neuriteID), neuriteID, t); + VecAdd(pos_avg, pos_avg, spine_pos); + } + t_avg /= nVrt; + VecScale(pos_avg, pos_avg, 1.0 / (number) nVrt); + + vector3 spine_pos; + pos_on_neurite_spine(spine_pos, np->neurite(neuriteID), neuriteID, t_avg); + + number distSq = VecDistanceSq(spine_pos, pos_avg); + number hSq = 0.0; + if (nVrt == 2) + hSq = VecDistanceSq(vAxPos[0], vAxPos[1]); + else if (nVrt == 3) + { + hSq = std::max(VecDistanceSq(vAxPos[0], vAxPos[1]), VecDistanceSq(vAxPos[1], vAxPos[2])); + hSq = std::max(hSq, VecDistanceSq(vAxPos[2], vAxPos[0])); + } + else if (nVrt == 4) + { + hSq = std::max(VecDistanceSq(vAxPos[0], vAxPos[1]), VecDistanceSq(vAxPos[1], vAxPos[2])); + hSq = std::max(hSq, VecDistanceSq(vAxPos[2], vAxPos[3])); + hSq = std::max(hSq, VecDistanceSq(vAxPos[3], vAxPos[0])); + } + else + UG_THROW("Number of parent vertices other than 2, 3 or 4 not supported."); + + number qSq; + if (hSq <= 1e-8*distSq) + qSq = 0.0; + else + qSq = distSq / hSq; + number w1 = qSq*qSq / (qSq*qSq + 1e-4); // in [0,1] with w1 = 0.5 for q = 0.1; + + vector3 pos_test_1, pos_test_2; + pos_on_surface_neurite(pos_test_1, np->neurite(neuriteID), neuriteID, t_avg, angle); + np->average_pos_from_parent(pos_test_2, parent); + VecScaleAdd(initOut, w1, pos_test_1, 1.0-w1, pos_test_2); +} +*/ + + + +static void pos_in_bp +( + vector3& posOut, + const NeuriteProjector::Neurite& neurite, + size_t neuriteID, + float& t, + float& angle, + float& rad, + std::vector::const_iterator& it, + const IVertexGroup* parent, + const NeuriteProjector* np +) +{ + const std::vector& vSections = neurite.vSec; + + // preparations for Newton method: + // save integration start and end positions of all BRs of this BP, + // also save a section iterator to start from + SmartPtr bp = it->bp; + size_t nParts = bp->vRegions.size(); + std::vector vProjHelp(nParts); + const std::vector* secs; + for (size_t i = 0; i < nParts; ++i) + { + number& start = vProjHelp[i].start; + number& end = vProjHelp[i].end; + std::vector::const_iterator& sec_start = vProjHelp[i].sec_start; + + size_t nid = bp->vNid[i]; + const NeuriteProjector::BranchingRegion* br = bp->vRegions[i]; + + UG_COND_THROW(!br, "Requested branching region not accessible."); + + start = br->tstart; + end = br->tend; + secs = &np->neurite(nid).vSec; + + number dt = end - start; + start = std::max(start - 0.5*dt, 0.0); + end = std::min(end + 0.5*dt, 1.0); + + if (start == 0.0) + sec_start = secs->begin(); + else + { + NeuriteProjector::Section cmpSec(start); + sec_start = std::lower_bound(secs->begin(), secs->end(), cmpSec, NeuriteProjector::CompareSections()); + UG_COND_THROW(sec_start == secs->end(), + "Could not find section for start parameter t = " << start << "."); + } + } + + // determine suitable start position for Newton iteration + pos_in_neurite(posOut, np->neurite(neuriteID), neuriteID, t, angle, rad); + //bp_newton_start_pos(posOut, vProjHelp, neuriteID, angle, parent, np); + + // perform Newton iteration + try {newton_for_bp_projection(posOut, vProjHelp, rad, np);} + UG_CATCH_THROW("Newton iteration for neurite projection at branching point failed."); + + + // FIXME: This might not be completely correct; at least, + // for two or more refinements, there are ripples + // in refined branching points. + // This becomes worse with every refinement + // resulting in self-intersections. + // update the surface param info to the new position + // In order not to have to compute zeros of a 5th order polynomial, + // we make a linearity approximation here: + // s(t) = s(t0) + v(t0)*(t-t0); + // v(t) = v(t0) + // Then from v(t) * (s(t)-pos) = 0, we get: + // t = t0 + v(t0) * (pos - s(t0)) / (v(t0)*v(t0)) + vector3 s, v; + number radius; + std::vector::const_iterator secIt = vSections.begin(); + while (t > secIt->endParam) + ++secIt; + compute_position_and_velocity_in_section(s, v, radius, secIt, t); + VecSubtract(s, posOut, s); + t += VecProd(v,s) / VecProd(v,v); + + // and now angle + vector3 projRefDir; + vector3 thirdDir; + compute_ONB(v, projRefDir, thirdDir, v, neurite.refDir); + vector3 copyS = s; + angle = compute_angle(v, projRefDir, thirdDir, copyS); + + // and radius + rad = VecNorm(s) / radius; + + + // if we have ended up outside of the BP, then use the usual positioning + if (t > it->tend) + { + vector3 posAx; + number radius; + compute_position_and_velocity_in_section(posAx, v, radius, secIt, t); + VecScaleAdd(posOut, 1.0, posAx, rad*radius*cos(angle), projRefDir, rad*radius*sin(angle), thirdDir); + } +} + + +static void pos_on_surface_tip +( + vector3& posOut, + const NeuriteProjector::Neurite& neurite, + const IVertexGroup* parent, + const NeuriteProjector* np, + number rad +) +{ + const std::vector& vSections = neurite.vSec; + + // initial position: regular refinement + np->average_pos_from_parent(posOut, parent); + + // project to half-sphere with given radius over tip center + // TODO: One might think about something more sophisticated, + // as the current approach ensures only continuity of the radius + // at tips, but not differentiability. + const NeuriteProjector::Section& sec = vSections[vSections.size()-1]; + vector3 tipCenter(sec.splineParamsX[3], sec.splineParamsY[3], sec.splineParamsZ[3]); + number radius = sec.splineParamsR[3]; + + vector3 radialVec; + VecSubtract(radialVec, posOut, tipCenter); + number radNorm = sqrt(VecProd(radialVec, radialVec)); + + // only project if we are not in the center of the half sphere + if (radNorm >= 1e-6*rad*radius) + { + VecScale(radialVec, radialVec, rad*radius/sqrt(VecProd(radialVec, radialVec))); + VecAdd(posOut, tipCenter, radialVec); + } +} + + + +number NeuriteProjector::push_into_place(Vertex* vrt, const IVertexGroup* parent) +{ + // average axial and angular params from parent; + // also decide on neuriteID + size_t neuriteID; + float t; + float angle; + float rad; + average_params(neuriteID, t, angle, rad, parent); + UG_COND_THROW(neuriteID >= m_vNeurites.size(), "Requested neurite ID which is not present."); + + const Neurite& neurite = m_vNeurites[neuriteID]; + const std::vector& vBR = neurite.vBR; + + // vector for new position + vector3 pos(0.0); + + // FOUR CASES can occur: + // 1. We are at a branching point. + // 2. We are well inside a regular piece of neurite. + // 3. We are at the tip of a neurite. + // 4. We are at the soma. + + // case 1: branching point? + BranchingRegion cmpBR(t); + std::vector::const_iterator it = + std::upper_bound(vBR.begin(), vBR.end(), cmpBR, CompareBranchingRegionEnds()); + + if (it != vBR.end() && it->tstart < t) + pos_in_bp(pos, neurite, neuriteID, t, angle, rad, it, parent, this); + + // case 2: normal neurite position + else if (t <= 1.0) + pos_in_neurite(pos, neurite, neuriteID, t, angle, rad); + + // case 3: tip of neurite + else + pos_on_surface_tip(pos, neurite, parent, this, rad); + + // case 4: soma + // TODO: implement! + + + // save new surface params for new vertex + m_aaSurfParams[vrt].neuriteID = neuriteID; + m_aaSurfParams[vrt].axial = t; + m_aaSurfParams[vrt].angular = angle; + m_aaSurfParams[vrt].radial = rad; + + // set position + set_pos(vrt, pos); + + return 1.0; +} + +// -------------------------------------------------------- // +// DO NOT CHANGE below this line! Needed for serialization. // + +std::ostream& operator<<(std::ostream &os, const NeuriteProjector::SurfaceParams& surfParams) +{ + using std::ostringstream; + ostringstream strs; + strs << surfParams.neuriteID << " "; + strs << surfParams.axial << " "; + strs << surfParams.angular << " "; + strs << surfParams.radial; + os << strs.str(); + return os; +} + +std::istream& operator>>(std::istream& in, NeuriteProjector::SurfaceParams& surfParams) +{ + std::string temp; + using boost::lexical_cast; + + in >> temp; + surfParams.neuriteID = lexical_cast(temp); + temp.clear(); + + in >> temp; + surfParams.axial = (lexical_cast(temp)); + temp.clear(); + + in >> temp; + surfParams.angular = lexical_cast(temp); + temp.clear(); + + in >> temp; + surfParams.radial = lexical_cast(temp); + temp.clear(); + + return in; +} + + +} // namespace ug diff --git a/ugbase/lib_grid/refinement/projectors/neurite_projector.h b/ugbase/lib_grid/refinement/projectors/neurite_projector.h new file mode 100644 index 000000000..89221ef95 --- /dev/null +++ b/ugbase/lib_grid/refinement/projectors/neurite_projector.h @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2016: G-CSC, Goethe University Frankfurt + * Author: Markus Breit + * + * This file is part of UG4. + * + * UG4 is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 3 (as published by the + * Free Software Foundation) with the following additional attribution + * requirements (according to LGPL/GPL v3 §7): + * + * (1) The following notice must be displayed in the Appropriate Legal Notices + * of covered and combined works: "Based on UG4 (www.ug4.org/license)". + * + * (2) The following notice must be displayed at a prominent place in the + * terminal output of covered works: "Based on UG4 (www.ug4.org/license)". + * + * (3) The following bibliography is recommended for citation and must be + * preserved in all covered files: + * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively + * parallel geometric multigrid solver on hierarchically distributed grids. + * Computing and visualization in science 16, 4 (2013), 151-164" + * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel + * flexible software system for simulating pde based models on high performance + * computers. Computing and visualization in science 16, 4 (2013), 165-179" + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * Created on: 2016-12-19 + */ + +#ifndef UG__LIB_GRID__REFINEMENT__PROJECTORS__NEURITE_PROJECTOR_H +#define UG__LIB_GRID__REFINEMENT__PROJECTORS__NEURITE_PROJECTOR_H + +#include "common/types.h" +#include "lib_grid/refinement/projectors/refinement_projector.h" +#include "lib_grid/tools/copy_attachment_handler.h" +//#include "lib_disc/quadrature/gauss_legendre/gauss_legendre.h" + +#include // for separate load/save methods + +namespace ug { + +class NeuriteProjector +: public RefinementProjector +{ + public: + NeuriteProjector(); + NeuriteProjector(SPIGeometry3d geometry); + + virtual ~NeuriteProjector(); + + virtual void set_geometry(SPIGeometry3d geometry); + + /// called when a new vertex was created from an old edge. + virtual number new_vertex(Vertex* vrt, Edge* parent); + + /// called when a new vertex was created from an old face. + virtual number new_vertex(Vertex* vrt, Face* parent); + + /// called when a new vertex was created from an old face. + virtual number new_vertex(Vertex* vrt, Volume* parent); + + /// spline direction at some grid object + void direction_at_grid_object(vector3& dirOut, GridObject* o) const; + + protected: + void attach_surf_params(); + + public: + struct Section + { + Section() {} // constructor for serialization + Section(number _endParam) // constructor for search with CompareSections + : endParam(_endParam) {} + + number endParam; + + number splineParamsX[4]; + number splineParamsY[4]; + number splineParamsZ[4]; + number splineParamsR[4]; + + template + void serialize(Archive& ar, const unsigned int version) + { + ar & endParam; + for (size_t i = 0; i < 4; ++i) + { + ar & splineParamsX[i]; + ar & splineParamsY[i]; + ar & splineParamsZ[i]; + ar & splineParamsR[i]; + } + } + }; + + + struct BranchingPoint; + struct BranchingRegion + { + BranchingRegion() : tstart(0.0), tend(0.0), bp(SPNULL) {} // constructor for serialization + BranchingRegion(number _tend) // constructor for search with CompareBranchingPointEnds + : tstart(_tend), tend(_tend) {} + + /// the parameter range for this branching point + number tstart; + number tend; + + /// pointer to whole branching point + SmartPtr bp; + + template + void save(Archive & ar, const unsigned int version) const + { + ar << tstart; + ar << tend; + + // the following won't work: + //bool owns_bp = bp->vRegions[0] == this; + // pointers are not identical if constructed outside of projector + // but this is unsafe: + bool owns_bp = tstart == bp->vRegions[0]->tstart && tend == bp->vRegions[0]->tend; + ar << owns_bp; + if (owns_bp) + ar << *bp; + } + + template + void load(Archive & ar, const unsigned int version) + { + // invoke serialization of the base class + ar >> tstart; + ar >> tend; + + bool owns_bp; + ar >> owns_bp; + if (owns_bp) + { + bp = make_sp(new BranchingPoint()); + ar >> *bp; + } + } + + BOOST_SERIALIZATION_SPLIT_MEMBER(); + }; + + + struct BranchingPoint + { + std::vector vNid; + std::vector vRegions; + + template + void serialize(Archive& ar, const unsigned int version) + { + // please note: We could simply use + // ar & vNid + // but this somehow takes up a huge number of template recursions + size_t sz = vNid.size(); // this does not hurt in the load-case + ar & sz; + vNid.resize(sz); // this does not hurt in the save-case + for (size_t i = 0; i < sz; ++i) + ar & vNid[i]; + } + }; + + + struct Neurite + { + vector3 refDir; + std::vector
vSec; + std::vector vBR; + + template + void serialize(Archive& ar, const unsigned int version) + { + ar & refDir; + + size_t sz = vSec.size(); + ar & sz; + vSec.resize(sz); + for (size_t i = 0; i < sz; ++i) + ar & vSec[i]; + + sz = vBR.size(); + ar & sz; + vBR.resize(sz); + for (size_t i = 0; i < sz; ++i) + ar & vBR[i]; + } + }; + + struct SurfaceParams + { + uint32 neuriteID; + float axial; + float angular; + float radial; + }; + + + public: + struct CompareSections + { + bool operator()(const Section& a, const Section& b) + {return a.endParam < b.endParam;} + }; + struct CompareBranchingRegionEnds + { + bool operator()(const BranchingRegion& a, const BranchingRegion& b) + {return a.tend < b.tend;} + }; + + // helper struct for branching point calculations + struct BPProjectionHelper + { + number start; + number end; + std::vector
::const_iterator sec_start; + }; + + void debug_neurites() const; + + public: + void add_neurite(const Neurite& n); + const Neurite& neurite(size_t nid) const; + + Grid::VertexAttachmentAccessor >& surface_params_accessor(); + const Grid::VertexAttachmentAccessor >& surface_params_accessor() const; + + const std::vector >& quadrature_points() const; + + void average_pos_from_parent(vector3& posOut, const IVertexGroup* parent) const; + + vector3 position(Vertex* vrt) const; + + protected: + const Section& get_section(uint32 nid, float t) const; + + void prepare_quadrature(); + + void average_params + ( + size_t& neuriteID, + float& t, + float& angle, + float& radius, + const IVertexGroup* parent + ) const; + + number push_into_place(Vertex* vrt, const IVertexGroup* parent); + + private: + Attachment m_aSurfParams; + Grid::VertexAttachmentAccessor > m_aaSurfParams; + + /// handles propagation of surface param attachment to children on higher levels + CopyAttachmentHandler > m_cah; + + /** + * @brief storage for cubic splines: + * Vector of sorted vectors of sections; each inner vector represents a single neurite. + * Any complete neurite is parameterized by t in [0,1]. Each section in the neurite vector + * consists of the parameter t at which the section ends and the sixteen coefficients + * describing the spline in each dimension (monomial basis {(t_(i+1) - t)^i}_i). + **/ + std::vector m_vNeurites; + + /// for quadrature when projecting within branching points + //size_t m_quadOrder; + + std::vector > m_qPoints; + + + + friend class boost::serialization::access; + + template + void save(Archive & ar, const unsigned int version) const + { + UG_EMPTY_BASE_CLASS_SERIALIZATION(NeuriteProjector, RefinementProjector); + + // only write if data is to be written + if(ArchiveInfo::TYPE == AT_DATA) + { + size_t sz = m_vNeurites.size(); + ar << sz; + for (size_t i = 0; i < sz; ++i) + ar << m_vNeurites[i]; + } + + // do not do anything otherwise + else if (ArchiveInfo::TYPE == AT_GUI) + {} + + //ar << m_quadOrder; + } + + template + void load(Archive & ar, const unsigned int version) + { + UG_EMPTY_BASE_CLASS_SERIALIZATION(NeuriteProjector, RefinementProjector); + + size_t sz; + ar >> sz; + m_vNeurites.resize(sz); + for (size_t i = 0; i < sz; ++i) + ar >> m_vNeurites[i]; + + //ar >> m_quadOrder; + + // reconstruct uninitialized pointers in branching points/ranges + size_t nNeurites = m_vNeurites.size(); + for (size_t n = 0; n < nNeurites; ++n) + { + Neurite& neurite = m_vNeurites[n]; + size_t nBR = neurite.vBR.size(); + for (size_t b = 0; b < nBR; ++b) + { + BranchingRegion& br = neurite.vBR[b]; + SmartPtr bp = br.bp; + if (bp.valid() && !bp->vRegions.size()) + { + size_t nReg = bp->vNid.size(); + bp->vRegions.resize(nReg); + bp->vRegions[0] = &br; + for (size_t r = 1; r < nReg; ++r) + { + UG_ASSERT(m_vNeurites[bp->vNid[r]].vBR.size(), + "No branching region in neurite where there should be one.") + bp->vRegions[r] = &m_vNeurites[bp->vNid[r]].vBR[0]; + bp->vRegions[r]->bp = bp; + } + } + } + } + + //debug_neurites(); + } + + BOOST_SERIALIZATION_SPLIT_MEMBER(); +}; + +// DO NOT CHANGE! Needed for serialization! // +std::ostream& operator<<(std::ostream &os, const NeuriteProjector::SurfaceParams& surfParams); +std::istream& operator>>(std::istream& in, NeuriteProjector::SurfaceParams& surfParams); +DECLARE_ATTACHMENT_INFO_TRAITS(Attachment, "NeuriteProjectorSurfaceParams"); + +} // namespace ug + + +#endif // UG__LIB_GRID__REFINEMENT__PROJECTORS__NEURITE_PROJECTOR_H diff --git a/ugbase/lib_grid/refinement/projectors/projectors.h b/ugbase/lib_grid/refinement/projectors/projectors.h index 6a4553f8b..354b946c7 100644 --- a/ugbase/lib_grid/refinement/projectors/projectors.h +++ b/ugbase/lib_grid/refinement/projectors/projectors.h @@ -40,12 +40,14 @@ #include "refinement_projector.h" #include "cylinder_cut_projector.h" #include "cylinder_projector.h" +#include "elliptic_cylinder_projector.h" #include "plane_cut_projector.h" #include "projection_handler.h" #include "raster_layers_projector.h" #include "smooth_projector.h" #include "sphere_projector.h" #include "subdivision_projector.h" +#include "neurite_projector.h" namespace boost { namespace mpl { @@ -61,11 +63,13 @@ namespace tmp { // in this list, since they are only usable in specialized algorithms. typedef vector< pair >, - pair >, + pair >, + pair >, pair >, pair >, pair >, - pair > + pair >, + pair > > ProjectorTypes; } diff --git a/ugbase/lib_grid/tools/subset_group.h b/ugbase/lib_grid/tools/subset_group.h index 3276ae466..97679383e 100644 --- a/ugbase/lib_grid/tools/subset_group.h +++ b/ugbase/lib_grid/tools/subset_group.h @@ -90,7 +90,7 @@ class SubsetGroup /// adds all subsets of another subset to the group void add(const SubsetGroup& ssGroup); - /// select all subsets of underlying subset + /// select all subsets of underlying subset handler void add_all(); /// removes a subset from this group diff --git a/ugbase/lib_grid/tools/surface_view_impl.hpp b/ugbase/lib_grid/tools/surface_view_impl.hpp index db26b94be..31bcaa804 100644 --- a/ugbase/lib_grid/tools/surface_view_impl.hpp +++ b/ugbase/lib_grid/tools/surface_view_impl.hpp @@ -567,6 +567,38 @@ collect_associated(std::vector& vAssElem, vAssElem.push_back(vCoarseElem[i]); } } + +// alternative implementation taking into account possible SHADOW_RIM_COPY elem. +#if 0 + // collect associated on this level + if (is_contained(elem, gl, ALL)) + { + std::vector vCoarseElem; + CollectAssociated(vCoarseElem, *m_pMG, elem, true); + for (size_t i = 0; i < vCoarseElem.size(); ++i) + if (is_contained(vCoarseElem[i], gl, ALL)) + vAssElem.push_back(vCoarseElem[i]); + } + + // if at border of a grid level, there may be connections of a "shadow-rim-copy" element + // to surface elements on the finer level. These must be taken into account. + if (is_contained(elem, gl, SHADOW_RIM_COPY)) + { + if (m_pMG->num_children(elem) > 0) + { + TElem* child = m_pMG->get_child(elem, 0); + if (is_contained(child, gl, SURFACE_RIM)) + { + // get connected elements + std::vector vFineElem; + CollectAssociated(vFineElem, *m_pMG, child, true); + for (size_t i = 0; i < vFineElem.size(); ++i) + if (is_contained(vFineElem[i], gl, ALL)) + vAssElem.push_back(vFineElem[i]); + } + } + } +#endif } diff --git a/ugbase/pcl/parallel_archive.h b/ugbase/pcl/parallel_archive.h index 7e226c0f5..657425f1c 100644 --- a/ugbase/pcl/parallel_archive.h +++ b/ugbase/pcl/parallel_archive.h @@ -154,7 +154,7 @@ class ParallelArchive ParallelArchive(std::string filename, pcl::ProcessCommunicator pc = pcl::ProcessCommunicator(pcl::PCD_WORLD)) : m_filename(filename), m_pc(pc) { - m_bWritten=false; + m_bWritten = false; m_bUnsafe = false; } @@ -168,7 +168,13 @@ class ParallelArchive { if(!m_bWritten) { - UG_COND_THROW(m_bUnsafe, "use ParallelArchive::write when using add_raw.") + if (m_bUnsafe) + { + UG_LOGN("ParallelArchive: Cannot write from destructor when using add_raw.\n" + "Use the write() method when using add_raw."); + return; + } + write(); } } diff --git a/ugbase/ug_shell/completion.cpp b/ugbase/ug_shell/completion.cpp index 5244cf18b..8d605794c 100644 --- a/ugbase/ug_shell/completion.cpp +++ b/ugbase/ug_shell/completion.cpp @@ -43,6 +43,7 @@ #include "bridge/bridge.h" #include "bindings/lua/info_commands.h" #include "bindings/lua/lua_stack_check.h" +#include "common/log.h" #include "registry/registry.h" #include "registry/class_helper.h" @@ -66,9 +67,9 @@ int iOtherCompletitionsLength; const char **pOtherCompletitions=NULL; -void print(std::string s) +void print(const std::string& s) { - print(s.c_str()); + printf("%s", s.c_str()); } /// \addtogroup ugbase_ugshell