diff --git a/.cmake/mito-config.cmake.in b/.cmake/mito-config.cmake.in index 124606f58..427eebac1 100644 --- a/.cmake/mito-config.cmake.in +++ b/.cmake/mito-config.cmake.in @@ -7,8 +7,8 @@ include(${CMAKE_CURRENT_LIST_DIR}/mito-targets.cmake) check_required_components(mito) -# request c++20 -set(CMAKE_CXX_STANDARD 20) +# request c++23 +set(CMAKE_CXX_STANDARD 23) # adjust the include path list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) diff --git a/.cmake/mito_benchmarks_mito_lib.cmake b/.cmake/mito_benchmarks_mito_lib.cmake index 54372f4d6..033a672ae 100644 --- a/.cmake/mito_benchmarks_mito_lib.cmake +++ b/.cmake/mito_benchmarks_mito_lib.cmake @@ -20,5 +20,9 @@ mito_benchmark_driver(benchmarks/mito.lib/integration/integration.cc) # fields mito_benchmark_driver(benchmarks/mito.lib/fields/laplacian.cc) +if(WITH_PETSC) + # poisson boundary value problem + mito_benchmark_driver(benchmarks/mito.lib/pdes/poisson.cc) +endif() # end of file diff --git a/.cmake/mito_init.cmake b/.cmake/mito_init.cmake index 31d5997a3..4dd6251bd 100644 --- a/.cmake/mito_init.cmake +++ b/.cmake/mito_init.cmake @@ -30,12 +30,12 @@ function(mito_cxxInit) # set C++ standard include(CheckCXXCompilerFlag) - # request c++20 - set(CMAKE_CXX_STANDARD 20 PARENT_SCOPE) + # request c++23 + set(CMAKE_CXX_STANDARD 23 PARENT_SCOPE) set(CMAKE_CXX_STANDARD_REQUIRED True PARENT_SCOPE) # additional compilation flags - set(CMAKE_CXX_FLAGS "-fdiagnostics-color=always -Wall -Wextra -pedantic -Werror" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "-fdiagnostics-color=always -Wall -Wextra -pedantic -Werror -Wno-psabi" PARENT_SCOPE) # all done endfunction(mito_cxxInit) diff --git a/.cmake/mito_sources.cmake b/.cmake/mito_sources.cmake index 3095b5fea..6fa1c85f9 100644 --- a/.cmake/mito_sources.cmake +++ b/.cmake/mito_sources.cmake @@ -10,7 +10,8 @@ set(MITO_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/lib/mito/version.cc) # the mito petsc backend if (WITH_PETSC) set(MITO_SOURCES ${MITO_SOURCES} - lib/mito/solvers/backend/petsc/PETScKrylovSolver.cc +lib/mito/matrix_solvers/backend/petsc/PETScLinearSystem.cc +lib/mito/matrix_solvers/backend/petsc/PETScKrylovSolver.cc ) endif() diff --git a/.cmake/mito_tests_mito_lib.cmake b/.cmake/mito_tests_mito_lib.cmake index b3a8b9a84..88ab480e6 100644 --- a/.cmake/mito_tests_mito_lib.cmake +++ b/.cmake/mito_tests_mito_lib.cmake @@ -10,9 +10,11 @@ mito_test_driver(tests/mito.lib/functions/derivative_constants.cc) mito_test_driver(tests/mito.lib/functions/derivative_chain_rule.cc) mito_test_driver(tests/mito.lib/functions/derivative_higher_order.cc) mito_test_driver(tests/mito.lib/functions/derivative_sum.cc) +mito_test_driver(tests/mito.lib/functions/derivative_subscript.cc) mito_test_driver(tests/mito.lib/functions/derivative_product.cc) mito_test_driver(tests/mito.lib/functions/derivative_inverse.cc) mito_test_driver(tests/mito.lib/functions/partial_derivatives.cc) +mito_test_driver(tests/mito.lib/functions/tensor_derivatives.cc) #  geometry mito_test_driver(tests/mito.lib/geometry/coordinates.cc) @@ -35,9 +37,20 @@ mito_test_driver(tests/mito.lib/geometry/euclidean_metric_space.cc) mito_test_driver(tests/mito.lib/geometry/polar_metric_space.cc) mito_test_driver(tests/mito.lib/geometry/spherical_metric_space.cc) -# discretization -mito_test_driver(tests/mito.lib/discretization/quadrature_field.cc) -mito_test_driver(tests/mito.lib/discretization/nodal_field.cc) +# constraints +mito_test_driver(tests/mito.lib/constraints/dirichlet.cc) + +# discrete +mito_test_driver(tests/mito.lib/discrete/quadrature_field.cc) +mito_test_driver(tests/mito.lib/discrete/mesh_field.cc) + +# fem +mito_test_driver(tests/mito.lib/fem/block_grad_grad.cc) +mito_test_driver(tests/mito.lib/fem/block_mass.cc) +mito_test_driver(tests/mito.lib/fem/shape_functions_triangle_construction.cc) +mito_test_driver(tests/mito.lib/fem/shape_functions_triangle_p1.cc) +mito_test_driver(tests/mito.lib/fem/shape_functions_triangle_p2.cc) +mito_test_driver(tests/mito.lib/fem/isoparametric_triangle.cc) # io mito_test_driver(tests/mito.lib/io/summit_mesh_reader_2D.cc) @@ -64,9 +77,8 @@ endif() # solvers if(WITH_PETSC) - mito_test_driver(tests/mito.lib/solvers/petsc_external_initialize.cc) - mito_test_driver(tests/mito.lib/solvers/petsc_internal_initialize.cc) - mito_test_driver(tests/mito.lib/solvers/petsc_solve_linear_system.cc) + mito_test_driver(tests/mito.lib/matrix_solvers/petsc_initialize_finalize.cc) + mito_test_driver(tests/mito.lib/matrix_solvers/petsc_ksp.cc) endif() # tensor @@ -78,7 +90,10 @@ mito_test_driver(tests/mito.lib/tensor/tensor_product_forms.cc) # fields mito_test_driver(tests/mito.lib/fields/fields.cc) mito_test_driver(tests/mito.lib/fields/fields_traits.cc) +mito_test_driver(tests/mito.lib/fields/calculus_identities.cc) mito_test_driver(tests/mito.lib/fields/calculus_scalar_field.cc) +mito_test_driver(tests/mito.lib/fields/calculus_vector_field.cc) +mito_test_driver(tests/mito.lib/fields/gradient_non_square.cc) mito_test_driver(tests/mito.lib/fields/polar_metric_field.cc) mito_test_driver(tests/mito.lib/fields/spherical_metric_field.cc) @@ -152,6 +167,7 @@ mito_test_driver(tests/mito.lib/utilities/segmented_vector_iterator.cc) mito_test_driver(tests/mito.lib/utilities/segmented_vector_subscript.cc) mito_test_driver(tests/mito.lib/utilities/segmented_vector_print.cc) mito_test_driver(tests/mito.lib/utilities/shared_pointer.cc) +mito_test_driver(tests/mito.lib/utilities/named_class.cc) # quadrature mito_test_driver(tests/mito.lib/quadrature/quadrature_parametric_segment.cc) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..11038a7e9 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,84 @@ +# global args +ARG user=user +ARG homedir=/home/${user} +ARG nprocs=4 + +FROM ubuntu:latest + +# to avoid user interaction with tzdata asking for timezone +ARG DEBIAN_FRONTEND=noninteractive + +# +ARG user +ARG homedir +ARG nprocs + +# environment +# colorize the terminal +ENV TERM=xterm-256color +# openmpi +ENV OMPI_ALLOW_RUN_AS_ROOT=1 +ENV OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1 +ENV OMPI_MCA_rmaps_base_oversubscribe=1 + +# get the latest +RUN apt dist-upgrade -y + +# update the package repository +RUN apt update -y + +# install multipurpose packages +RUN apt install -y cmake-curses-gui +RUN apt install -y git +RUN apt install -y git-core +RUN apt install -y bash-completion +RUN apt install -y make +RUN apt install -y vim +RUN apt install -y sudo + +# install mito dependencies +RUN apt install -y libgtest-dev +RUN apt install -y libbenchmark-dev +RUN apt install -y libopenmpi-dev +RUN apt install -y libmetis-dev +RUN apt install -y libvtk9-dev +RUN apt install -y petsc-dev +RUN apt install -y valgrind +RUN apt install -y libpython3-dev +RUN apt install -y python3-pytest + +# install pyre dependencies +RUN apt install -y pybind11-dev + +# install pyre +WORKDIR ${homedir} +RUN git clone https://github.com/pyre/pyre \ + && cd pyre \ + && git checkout tensor \ + && mkdir build \ + && cd build \ + && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local/pyre .. \ + && make -j ${nprocs} \ + && make install \ + && cd .. \ + && rm -rf build + +# setup {user} user +ENV USER=${user} +ENV HOME=${homedir} +RUN useradd -s /bin/bash --create-home --home-dir $HOME $USER \ + && usermod -aG sudo $USER \ + && chown -R $USER:$USER $HOME +RUN echo "$USER:password" | chpasswd +# copy the bashrc +COPY --chown=${user}:${user} bashrc .bashrc + +# default user and work directory when running the container +USER ${user} +WORKDIR ${homedir} +# remove annoying message on how to run a sudo command +RUN touch ~/.sudo_as_admin_successful +# configure git +RUN git config --global pull.ff only + +# end of file diff --git a/.devcontainer/bashrc b/.devcontainer/bashrc new file mode 100644 index 000000000..d18cd4fc4 --- /dev/null +++ b/.devcontainer/bashrc @@ -0,0 +1,6 @@ +# enable bash completion +if [ -f /etc/bash_completion ]; then + . /etc/bash_completion +elif [ -f /usr/share/bash-completion/bash_completion ]; then + . /usr/share/bash-completion/bash_completion +fi \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..484c24ac3 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,16 @@ +// .devcontainer/devcontainer.json +{ + "name": "mito", + "build": { + "dockerfile": "Dockerfile" + }, + "mounts": [ + "source=${localWorkspaceFolder},target=/home/user/mito,type=bind" + ], + "extensions": [ + "ms-vscode.cpptools-extension-pack", + "eamodio.gitlens", + "med-h.git-graph-revamped", + ], + "remoteUser": "user" +} \ No newline at end of file diff --git a/.github/workflows/cmake-macos.yaml b/.github/workflows/cmake-macos.yaml index 49cac6664..7369bb870 100644 --- a/.github/workflows/cmake-macos.yaml +++ b/.github/workflows/cmake-macos.yaml @@ -23,11 +23,11 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-13] + os: [macos-14] target: [Debug, Release] python: ["3.12"] suite: [gcc] - suiteVersion: ["13"] + suiteVersion: ["14"] include: - suite: gcc cc: gcc @@ -39,9 +39,7 @@ jobs: echo " -- install our dependencies" brew install make cmake metis open-mpi # install {google-benchmark} from source to make sure it is built with the same compiler - brew install --build-from-source --cc=gcc-12 google-benchmark - curl https://raw.githubusercontent.com/Homebrew/homebrew-core/67b277e6d1faee20d2aa4fc8b2dc229a241fa807/Formula/vtk.rb > vtk.rb - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install vtk.rb + brew install --build-from-source --cc=gcc-14 google-benchmark - name: python ${{matrix.python}} setup uses: actions/setup-python@v4 @@ -51,7 +49,7 @@ jobs: - name: install dependencies run: | python3 -m pip install --upgrade pip - pip3 install distro 'numpy<2.0' pybind11 pytest vtk + pip3 install distro 'numpy<2.0' pybind11 pytest - name: checkout gtest uses: actions/checkout@v3 @@ -84,7 +82,7 @@ jobs: repository: pyre/pyre fetch-depth: 0 path: ${{github.workspace}}/pyre - ref: main + ref: tensor - name: retrieve pyre commit SHA id: pyre-sha @@ -155,8 +153,6 @@ jobs: find ${pythonLocation}/lib/python${pythonVersion}/site-packages/pybind11 echo " -- pytest" pip3 show pytest - echo " -- vtk" - pip3 show vtk env: pythonVersion: ${{matrix.python}} @@ -172,7 +168,7 @@ jobs: mkdir build cd build echo " -- configuring the build" - cmake -DCMAKE_INSTALL_PREFIX=${prefix} -DCMAKE_BUILD_TYPE=${target} -DCMAKE_CXX_COMPILER=${cxx} -DPython_EXECUTABLE=${pythonLocation}/bin/python${pythonVersion} -Dpybind11_DIR=${pythonLocation}/lib/python${pythonVersion}/site-packages/pybind11/share/cmake/pybind11 -DMITO_BUILD_TESTING=ON -DMITO_BUILD_BENCHMARKS=ON -Dpyre_DIR=${{runner.temp}}/pyre_install/share/cmake/pyre -DGTest_DIR=${{runner.temp}}/gtest_install/lib/cmake/GTest -DWITH_VTK=ON -DWITH_METIS=ON ${{github.workspace}}/mito + cmake -DCMAKE_INSTALL_PREFIX=${prefix} -DCMAKE_BUILD_TYPE=${target} -DCMAKE_CXX_COMPILER=${cxx} -DPython_EXECUTABLE=${pythonLocation}/bin/python${pythonVersion} -Dpybind11_DIR=${pythonLocation}/lib/python${pythonVersion}/site-packages/pybind11/share/cmake/pybind11 -DMITO_BUILD_TESTING=ON -DMITO_BUILD_BENCHMARKS=ON -Dpyre_DIR=${{runner.temp}}/pyre_install/share/cmake/pyre -DGTest_DIR=${{runner.temp}}/gtest_install/lib/cmake/GTest -DWITH_VTK=OFF -DWITH_METIS=ON ${{github.workspace}}/mito echo " -- building mito" make -j 2 install env: diff --git a/.github/workflows/cmake-ubuntu.yaml b/.github/workflows/cmake-ubuntu.yaml index 09a5f0aa9..c7699f432 100644 --- a/.github/workflows/cmake-ubuntu.yaml +++ b/.github/workflows/cmake-ubuntu.yaml @@ -27,7 +27,7 @@ jobs: target: [Debug, Release] python: ["3.12"] suite: [gcc] - suiteVersion: ["13"] + suiteVersion: ["14"] include: - suite: gcc cc: gcc @@ -38,10 +38,6 @@ jobs: run: | echo " -- update the package cache" sudo apt update - echo " -- upgradables" - sudo apt list --upgradable - echo " -- upgrade" - sudo apt dist-upgrade echo " -- install our dependencies" sudo apt install -y make cmake libgtest-dev libbenchmark-dev libvtk9-dev libopenmpi-dev libmetis-dev valgrind petsc-dev @@ -61,7 +57,7 @@ jobs: repository: pyre/pyre fetch-depth: 0 path: ${{github.workspace}}/pyre - ref: main + ref: tensor - name: retrieve pyre commit SHA id: pyre-sha diff --git a/benchmarks/mito.lib/integration/integration.cc b/benchmarks/mito.lib/integration/integration.cc index 06f3e0e5f..f21c564a4 100644 --- a/benchmarks/mito.lib/integration/integration.cc +++ b/benchmarks/mito.lib/integration/integration.cc @@ -76,7 +76,7 @@ main() auto integrator = mito::quadrature::integrator(manifold); auto result = integrator.integrate(f); - auto exact = mito::tensor::scalar_t(0.9460830607878437); + auto exact = mito::tensor::scalar_t{ 0.9460830607878437 }; channel << "Integration of cos(x*y): Result = " << result << ", Error = " << std::fabs(result - exact) << journal::endl; diff --git a/benchmarks/mito.lib/pdes/poisson.cc b/benchmarks/mito.lib/pdes/poisson.cc new file mode 100644 index 000000000..f7e30148e --- /dev/null +++ b/benchmarks/mito.lib/pdes/poisson.cc @@ -0,0 +1,151 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include + + +// cartesian coordinates in 2D +using coordinates_t = mito::geometry::coordinates_t<2, mito::geometry::CARTESIAN>; + +// simplicial cells in 2D +using cell_t = mito::geometry::triangle_t<2>; +// second degree finite elements +constexpr int degree = 2; +// assemble the finite element type +using finite_element_t = mito::fem::isoparametric_simplex_t; + +// the reference simplex +using reference_simplex_t = mito::geometry::reference_triangle_t; +// degree of exactness for the quadrature rule +constexpr int doe = 2; +// Gauss quadrature on triangles with degree of exactness 2 +using quadrature_rule_t = + mito::quadrature::quadrature_rule_t; + +// typedef for a linear system of equations +using linear_system_t = mito::matrix_solvers::petsc::linear_system_t; +// typedef for a matrix solver +using matrix_solver_t = mito::matrix_solvers::petsc::ksp_t; + +// the x scalar field in 2D +constexpr auto x = mito::functions::component; +// the y scalar field in 2D +constexpr auto y = mito::functions::component; + + +int +main() +{ + // initialize PETSc + mito::petsc::initialize(); + + // make a channel + journal::info_t channel("tests.poisson_square"); + + // the coordinate system + auto coord_system = mito::geometry::coordinate_system(); + + // read the mesh of a square in 2D + std::ifstream fileStream("square.summit"); + auto mesh = mito::io::summit::reader(fileStream, coord_system); + + // // TOFIX: tetra does not work with the current implementation + // // do one tetra mesh refinement + // const auto subdivisions = 1; + // auto mesh = mito::mesh::tetra(original_mesh, coord_system, subdivisions); + + // create the body manifold + auto manifold = mito::manifolds::manifold(mesh, coord_system); + + // get the boundary mesh + auto boundary_mesh = mito::mesh::boundary(mesh); + + // the zero field + auto zero = mito::fields::field(mito::functions::zero); + + // set homogeneous Dirichlet boundary condition + auto constraints = mito::constraints::dirichlet_bc(boundary_mesh, zero); + + // the function space (linear elements on the manifold) + auto function_space = mito::fem::function_space(manifold, constraints); + + // a grad-grad matrix block + auto fem_lhs_block = mito::fem::blocks::grad_grad_block(); + + // the right hand side + auto f = mito::fields::field( + 2.0 * std::numbers::pi * std::numbers::pi * mito::functions::sin(std::numbers::pi * x) + * mito::functions::sin(std::numbers::pi * y)); + // channel << "Right hand side: " << f(coordinates_t{ 0.5, 0.5 }) << journal::endl; + + // a source term block + auto fem_rhs_block = + mito::fem::blocks::source_term_block(f); + + // create the weak form and populate it with the blocks + auto weakform = mito::fem::weakform(); + weakform.add_block(fem_lhs_block); + weakform.add_block(fem_rhs_block); + + // the discrete system + auto discrete_system = + mito::fem::discrete_system("mysystem", function_space, weakform); + + // instantiate a linear solver for the discrete system + auto solver = mito::solvers::linear_solver(discrete_system); + + // set options for the backend {petsc} matrix solver + solver.set_options("-ksp_type preonly -pc_type cholesky"); + + // solve the system + solver.solve(); + + // free the solver + solver.destroy(); + + // the exact solution field + auto u_ex = mito::fields::field( + mito::functions::sin(std::numbers::pi * x) * mito::functions::sin(std::numbers::pi * y)); + + // compute the L2 error + auto error_L2 = discrete_system.compute_l2_error(u_ex); + // report + channel << "L2 error: " << error_L2 << journal::endl; + + // compute the H1 error + auto error_H1 = discrete_system.compute_h1_error(u_ex); + // report + channel << "H1 error: " << error_H1 << journal::endl; + +#ifdef WITH_VTK + // the forcing term mesh field on the mesh (for visualization) + auto forcing = mito::discrete::mesh_field(mesh, coord_system, f, "forcing term"); + // the exact solution mesh field on the mesh (for visualization) + auto exact_solution = mito::discrete::mesh_field(mesh, coord_system, u_ex, "exact solution"); + // write mesh to vtk file + auto writer = mito::io::vtk::field_writer("poisson_square_data", mesh, coord_system); + // sign {forcing} up with the writer + writer.record(forcing); + // sign {exact_solution} up with the writer + writer.record(exact_solution); + // write output file + writer.write(); + + // get the solution field + const auto & solution = discrete_system.solution(); + // write mesh to vtk file + auto writer_solution = + mito::io::vtk::field_writer("poisson_square_solution", function_space, coord_system); + // sign {solution} up with the writer + writer_solution.record(solution); + // write output file + writer_solution.write(); +#endif + + // finalize PETSc + mito::petsc::finalize(); +} + +// end of file diff --git a/lib/mito/discretization.h b/lib/mito/constraints.h similarity index 67% rename from lib/mito/discretization.h rename to lib/mito/constraints.h index 6288e2e37..dfdb50804 100644 --- a/lib/mito/discretization.h +++ b/lib/mito/constraints.h @@ -8,8 +8,8 @@ // publish the interface -// the api is in "discretization/api.h" -#include "discretization/public.h" +// the api is in "constraints/api.h" +#include "constraints/public.h" // end of file diff --git a/lib/mito/constraints/Constraint.h b/lib/mito/constraints/Constraint.h new file mode 100644 index 000000000..2d062d469 --- /dev/null +++ b/lib/mito/constraints/Constraint.h @@ -0,0 +1,17 @@ + +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + + +// code guard +#pragma once + + +namespace mito::constraints { + + // class Constraint + class Constraint {}; + +} // namespace mito::constraints \ No newline at end of file diff --git a/lib/mito/constraints/Dirichlet.h b/lib/mito/constraints/Dirichlet.h new file mode 100644 index 000000000..d328b61e9 --- /dev/null +++ b/lib/mito/constraints/Dirichlet.h @@ -0,0 +1,44 @@ + +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + + +// code guard +#pragma once + + +namespace mito::constraints { + + // class Dirichlet boundary condition + template + class Dirichlet : Constraint { + // types + public: + using domain_type = meshT; + using function_type = fieldT; + using node_type = typename domain_type::node_type; + using nodes_type = std::set; + + // interface + public: + // constructor + Dirichlet(const domain_type & domain, const function_type & function) : + _domain(domain), + _function(function) + {} + + // destructor + ~Dirichlet() = default; + + // accessors + auto domain() const -> const domain_type & { return _domain; } + auto function() const -> const function_type & { return _function; } + + private: + const domain_type & _domain; + function_type _function; + }; + +} // namespace mito::constraints \ No newline at end of file diff --git a/lib/mito/constraints/api.h b/lib/mito/constraints/api.h new file mode 100644 index 000000000..e56b13376 --- /dev/null +++ b/lib/mito/constraints/api.h @@ -0,0 +1,23 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::constraints { + + // Dirichlet class alias + template + using dirichlet_bc_t = Dirichlet; + + // factory for Dirichlet boundary conditions + template + constexpr auto dirichlet_bc(const meshT & mesh, const fieldT & function) + -> dirichlet_bc_t; +} + + +// end of file diff --git a/lib/mito/discretization/externals.h b/lib/mito/constraints/externals.h similarity index 83% rename from lib/mito/discretization/externals.h rename to lib/mito/constraints/externals.h index 34ea18fe1..cb4f544dc 100644 --- a/lib/mito/discretization/externals.h +++ b/lib/mito/constraints/externals.h @@ -8,12 +8,11 @@ // externals -#include // support #include "../journal.h" -#include "../tensor.h" #include "../mesh.h" +#include "../fields.h" // end of file diff --git a/lib/mito/constraints/factories.h b/lib/mito/constraints/factories.h new file mode 100644 index 000000000..f11ac75eb --- /dev/null +++ b/lib/mito/constraints/factories.h @@ -0,0 +1,23 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::constraints { + + // factory for Dirichlet boundary conditions + template + constexpr auto dirichlet_bc(const meshT & mesh, const fieldT & function) + -> dirichlet_bc_t + { + return dirichlet_bc_t(mesh, function); + } + +} + + +// end of file diff --git a/lib/mito/constraints/forward.h b/lib/mito/constraints/forward.h new file mode 100644 index 000000000..9a9182fd5 --- /dev/null +++ b/lib/mito/constraints/forward.h @@ -0,0 +1,26 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::constraints { + + // class Dirichlet boundary condition + template + class Dirichlet; + + // concept of a constraint + template + concept constraint_c = requires(F c) { + // require that F only binds to {Dirichlet} specializations + [](const Dirichlet &) { + }(c); + }; +} + + +// end of file diff --git a/lib/mito/constraints/public.h b/lib/mito/constraints/public.h new file mode 100644 index 000000000..c351b0b41 --- /dev/null +++ b/lib/mito/constraints/public.h @@ -0,0 +1,27 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// external packages +#include "externals.h" + +// get the forward declarations +#include "forward.h" + +// published types factories; this is the file you are looking for... +#include "api.h" + +// classes implementation +#include "Constraint.h" +#include "Dirichlet.h" + +// factories implementation +#include "factories.h" + + +// end of file diff --git a/lib/mito/coordinates/Coordinates.h b/lib/mito/coordinates/Coordinates.h index 4a61069fb..28d33df3b 100644 --- a/lib/mito/coordinates/Coordinates.h +++ b/lib/mito/coordinates/Coordinates.h @@ -47,7 +47,7 @@ namespace mito::geometry { constexpr Coordinates(mito::tensor::scalar_t (&&coords)[D]) : _array(std::move(coords)) {} // constructor - constexpr Coordinates(const array_t coords) : _array(coords) {} + constexpr Coordinates(const array_t & coords) : _array(coords) {} // constructor from parameter pack template diff --git a/lib/mito/discrete.h b/lib/mito/discrete.h new file mode 100644 index 000000000..d9bb76532 --- /dev/null +++ b/lib/mito/discrete.h @@ -0,0 +1,15 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// publish the interface +// the api is in "discrete/api.h" +#include "discrete/public.h" + + +// end of file diff --git a/lib/mito/discretization/DiscreteField.h b/lib/mito/discrete/DiscreteField.h similarity index 75% rename from lib/mito/discretization/DiscreteField.h rename to lib/mito/discrete/DiscreteField.h index 77431396a..5ebebb933 100644 --- a/lib/mito/discretization/DiscreteField.h +++ b/lib/mito/discrete/DiscreteField.h @@ -7,17 +7,20 @@ #pragma once -namespace mito::discretization { +namespace mito::discrete { - template + template class DiscreteField { private: // the key type using key_type = keyT; + // the value type + using value_type = valueT; - // a map from {key_type} to {Y} values - using map_type = std::unordered_map>; + // a map from {key_type} to {value_type} values + using map_type = + std::unordered_map>; public: // constructor @@ -39,8 +42,8 @@ namespace mito::discretization { // delete copy constructor DiscreteField(const DiscreteField &) = delete; - // delete move copy constructor - DiscreteField(DiscreteField &&) = delete; + // default move constructor + DiscreteField(DiscreteField &&) = default; // delete copy assignment auto operator=(const DiscreteField &) -> DiscreteField & = delete; @@ -52,7 +55,7 @@ namespace mito::discretization { /** * accessor for the value of a given entry */ - inline auto operator()(const key_type & key) const -> const Y & + inline auto operator()(const key_type & key) const -> const value_type & { return _map_entry_to_values.at(key); } @@ -60,12 +63,15 @@ namespace mito::discretization { /** * mutator for the value of a given entry */ - inline auto operator()(const key_type & key) -> Y & { return _map_entry_to_values.at(key); } + inline auto operator()(const key_type & key) -> value_type & + { + return _map_entry_to_values.at(key); + } /** * insert a new entry to the field */ - inline auto insert(const key_type & key, const Y & entry = Y()) -> void + inline auto insert(const key_type & key, const value_type & entry = value_type()) -> void { // add a new entry to the field _map_entry_to_values[key] = entry; @@ -89,6 +95,8 @@ namespace mito::discretization { inline auto end() const { return std::cend(_map_entry_to_values); } inline auto begin() { return std::begin(_map_entry_to_values); } inline auto end() { return std::end(_map_entry_to_values); } + inline auto cbegin() const { return std::cbegin(_map_entry_to_values); } + inline auto cend() const { return std::cend(_map_entry_to_values); } private: // the underlying mapping of entries to nodal values diff --git a/lib/mito/discrete/DiscretizationNode.h b/lib/mito/discrete/DiscretizationNode.h new file mode 100644 index 000000000..404678a11 --- /dev/null +++ b/lib/mito/discrete/DiscretizationNode.h @@ -0,0 +1,42 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + + +// code guard +#pragma once + +/* + * This class represents a discretization node in D-dimensional space. + * + */ + +namespace mito::discrete { + + class DiscretizationNode : public utilities::Invalidatable { + + public: + // default constructor + constexpr DiscretizationNode() = default; + + // move constructor + constexpr DiscretizationNode(DiscretizationNode &&) noexcept = default; + + // copy constructor + constexpr DiscretizationNode(const DiscretizationNode &) = default; + + // destructor + constexpr ~DiscretizationNode() = default; + + // default assignment operator + constexpr DiscretizationNode & operator=(const DiscretizationNode &) = default; + + // default move assignment operator + constexpr DiscretizationNode & operator=(DiscretizationNode &&) noexcept = default; + }; + +} + + +// end of file diff --git a/lib/mito/discretization/api.h b/lib/mito/discrete/api.h similarity index 53% rename from lib/mito/discretization/api.h rename to lib/mito/discrete/api.h index 2240b1a4b..85c04260b 100644 --- a/lib/mito/discretization/api.h +++ b/lib/mito/discrete/api.h @@ -7,16 +7,23 @@ #pragma once -namespace mito::discretization { +namespace mito::discrete { - // nodal field + // discretization node alias + using discretization_node_t = utilities::std_shared_ptr; + + // mesh field template - using nodal_field_t = DiscreteField, Y>; + using mesh_field_t = DiscreteField, Y>; // point field template using point_field_t = DiscreteField, Y>; + // nodal field + template + using nodal_field_t = DiscreteField; + // quadrature field alias template using quadrature_field_t = DiscreteField>; @@ -25,13 +32,20 @@ namespace mito::discretization { template constexpr auto quadrature_field(const meshT & mesh, std::string name); - // nodal field factory + // mesh field factory template - constexpr auto nodal_field(const mesh::mesh_c auto & mesh, std::string name); + constexpr auto mesh_field(const mesh::mesh_c auto & mesh, std::string name); + + // mesh field factory from a continuous field + template + constexpr auto mesh_field( + const mesh::mesh_c auto & mesh, const geometry::coordinate_system_c auto & coord_system, + const fieldT & field, std::string name); // point field factory template constexpr auto point_field(const cloudT & cloud, std::string name); + } diff --git a/lib/mito/solvers/backend/petsc/externals.h b/lib/mito/discrete/externals.h similarity index 68% rename from lib/mito/solvers/backend/petsc/externals.h rename to lib/mito/discrete/externals.h index cdea5cbf7..3f27eafc0 100644 --- a/lib/mito/solvers/backend/petsc/externals.h +++ b/lib/mito/discrete/externals.h @@ -9,11 +9,9 @@ // externals #include -#include -#include - -// petsc support -#include +// support +#include "../mesh.h" +#include "../utilities.h" // end of file diff --git a/lib/mito/discretization/factories.h b/lib/mito/discrete/factories.h similarity index 52% rename from lib/mito/discretization/factories.h rename to lib/mito/discrete/factories.h index 94f9b307f..23a910a8a 100644 --- a/lib/mito/discretization/factories.h +++ b/lib/mito/discrete/factories.h @@ -7,7 +7,7 @@ #pragma once -namespace mito::discretization { +namespace mito::discrete { // quadrature field factory template @@ -19,9 +19,9 @@ namespace mito::discretization { mesh.cells(), name); } - // nodal field factory + // mesh field factory template - constexpr auto nodal_field(const meshT & mesh, std::string name) + constexpr auto mesh_field(const meshT & mesh, std::string name) { // assemble the node type using node_type = geometry::node_t; @@ -30,8 +30,29 @@ namespace mito::discretization { std::unordered_set> nodes; mesh::get_nodes(mesh, nodes); - // build a nodal field on the nodes collected from the mesh - return nodal_field_t(nodes, name); + // build a mesh field on the nodes collected from the mesh + return mesh_field_t(nodes, name); + } + + // mesh field factory from a continuous field + template + constexpr auto mesh_field( + const mesh::mesh_c auto & mesh, const geometry::coordinate_system_c auto & coord_system, + const fieldT & field, std::string name) + { + // create a mesh field on the mesh + auto m_field = mesh_field(mesh, name); + + // populate the mesh field with the values of the continuous field + for (auto & [node, value] : m_field) { + // get the position of {node} + auto coord = coord_system.coordinates(node->point()); + // evaluate the continuousfield at {coord} + value = field(coord); + } + + // return the mesh field + return m_field; } // point field factory @@ -41,6 +62,7 @@ namespace mito::discretization { // build a point field on the points collected from the cloud return point_field_t(cloud.points(), name); } + } diff --git a/lib/mito/discretization/forward.h b/lib/mito/discrete/forward.h similarity index 71% rename from lib/mito/discretization/forward.h rename to lib/mito/discrete/forward.h index 8187f62d1..ea0878b43 100644 --- a/lib/mito/discretization/forward.h +++ b/lib/mito/discrete/forward.h @@ -7,12 +7,14 @@ #pragma once -namespace mito::discretization { +namespace mito::discrete { + + // class discretization node + class DiscretizationNode; // class discrete field template class DiscreteField; - } diff --git a/lib/mito/discretization/public.h b/lib/mito/discrete/public.h similarity index 92% rename from lib/mito/discretization/public.h rename to lib/mito/discrete/public.h index cbda855c1..e78183a77 100644 --- a/lib/mito/discretization/public.h +++ b/lib/mito/discrete/public.h @@ -18,9 +18,9 @@ // classes implementation #include "DiscreteField.h" +#include "DiscretizationNode.h" // factories implementation #include "factories.h" - // end of file diff --git a/lib/mito/fem.h b/lib/mito/fem.h new file mode 100644 index 000000000..1a2a834fb --- /dev/null +++ b/lib/mito/fem.h @@ -0,0 +1,15 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// publish the interface +// the api is in "fem/api.h" +#include "fem/public.h" + + +// end of file diff --git a/lib/mito/fem/DiscreteSystem.h b/lib/mito/fem/DiscreteSystem.h new file mode 100644 index 000000000..de88210da --- /dev/null +++ b/lib/mito/fem/DiscreteSystem.h @@ -0,0 +1,344 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + // TOFIX: for now the discrete system is one per function space. We should figure out a way to + // extend the design to the case that there are multiple finite element discretizations that + // end up on the same linear system. + + template + class DiscreteSystem { + + private: + // the function space type + using function_space_type = functionSpaceT; + // the element type + using element_type = typename function_space_type::element_type; + // the weakform type + using weakform_type = weakform_t; + // the linear system type + using linear_system_type = linearSystemT; + // the label type + using label_type = std::string; + // the type of node + using node_type = typename function_space_type::discretization_node_type; + // QUESTION: is std::map the best choice for {equation_map_type}? + // the equation map type (map associating an equation number to each node degree of freedom) + using equation_map_type = std::map; + // TOFIX: what if the solution is not a scalar field? Generalize to different types of + // solutions + // the solution field type + using solution_field_type = tensor::scalar_t; + // the nodal field type + using nodal_field_type = discrete::nodal_field_t; + // the number of nodes per element + static constexpr int n_element_nodes = element_type::n_nodes; + + public: + // constructor + constexpr DiscreteSystem( + const label_type & label, const function_space_type & function_space, + const weakform_type & weakform) : + _function_space(function_space), + _weakform(weakform), + _equation_map(), + _solution_field(nodal_field(function_space, label + ".solution")), + _linear_system(label) + { + // make a channel + journal::info_t channel("discretization.discrete_system"); + + // build the equations map and get the number of equations + _n_equations = _build_equation_map(); + + // print the number of equations + channel << "Number of equations: " << _n_equations << journal::endl; + + // create the linear system and allocate the memory + _linear_system.create(_n_equations); + + // all done + return; + } + + // destructor + constexpr ~DiscreteSystem() = default; + + // delete move constructor + constexpr DiscreteSystem(DiscreteSystem &&) noexcept = delete; + + // delete copy constructor + constexpr DiscreteSystem(const DiscreteSystem &) = delete; + + // delete assignment operator + constexpr DiscreteSystem & operator=(const DiscreteSystem &) = delete; + + // delete move assignment operator + constexpr DiscreteSystem & operator=(DiscreteSystem &&) noexcept = delete; + + private: + // build the equation map and return the number of equations + auto _build_equation_map() -> int + { + // make a channel + journal::info_t channel("discretization.discrete_system"); + + // get all the nodes in the function space + std::set nodes; + get_discretization_nodes(_function_space, nodes); + channel << "Number of nodes: " << std::size(nodes) << journal::endl; + + // get the constrained nodes in the function space + const auto & constrained_nodes = _function_space.constrained_nodes(); + channel << "Number of constrained nodes: " << std::size(constrained_nodes) + << journal::endl; + + // get all the interior nodes as the difference between all the nodes and the boundary + // nodes + std::set interior_nodes; + std::set_difference( + nodes.begin(), nodes.end(), constrained_nodes.begin(), constrained_nodes.end(), + std::inserter(interior_nodes, interior_nodes.begin())); + channel << "Number of interior nodes: " << std::size(interior_nodes) << journal::endl; + + // populate the equation map (from node to equation, one equations per node) + int equation = 0; + + // loop on all the boundary nodes of the mesh + for (const auto & node : constrained_nodes) { + // check if the node is already in the equation map + if (_equation_map.find(node) == _equation_map.end()) { + // add the node to the equation map with a -1 indicating that the node is on the + // boundary + _equation_map[node] = -1; + } + } + + // loop on all the interior nodes of the mesh + for (const auto & node : interior_nodes) { + // check if the node is already in the equation map + if (_equation_map.find(node) == _equation_map.end()) { + // add the node to the equation map + _equation_map[node] = equation; + // increment the equation number + equation++; + } + } + + // return the number of equations + return equation; + } + + public: + // accessor to the linear system + constexpr auto linear_system() noexcept -> linear_system_type & { return _linear_system; } + + // assemble the discrete system + constexpr auto assemble() -> void + { + // check that the number of equations matches that of the linear system + assert(_n_equations == _linear_system.n_equations()); + + // QUESTION: can we flip the element and block loops? What is the expected layout in + // memory? + // + // loop on all the cells of the mesh + for (const auto & element : _function_space.elements()) { + // get the elementary contributions to matrix and right-hand side from the weakform + auto [elementary_matrix, elementary_vector] = _weakform.compute_blocks(element); + + // assemble the elementary blocks into the linear system of equations + tensor::constexpr_for_1([&]() { + // get the a-th discretization node of the element + const auto & node_a = element.connectivity()[a]; + // get the equation number of {node_a} + int eq_a = _equation_map.at(node_a); + assert(eq_a < _n_equations); + // non boundary nodes + if (eq_a != -1) { + // assemble the value in the right hand side + _linear_system.add_rhs_value(eq_a, elementary_vector[{ a }]); + // loop on the b-th discretization node of the element + tensor::constexpr_for_1([&]() { + // get the b-th discretization node of the element + const auto & node_b = element.connectivity()[b]; + // get the equation number of {node_b} + int eq_b = _equation_map.at(node_b); + assert(eq_b < _n_equations); + // non boundary nodes + if (eq_b != -1) { + // assemble the value in the stiffness matrix + _linear_system.add_matrix_value( + eq_a, eq_b, elementary_matrix[{ a, b }]); + } + }); + } + }); + } + } + + // read the solution nodal field + constexpr void read_solution() + { + // check that the number of equations matches that of the linear system + assert(_n_equations == _linear_system.n_equations()); + + // read the solution + auto u = std::vector(_n_equations); + _linear_system.get_solution(u); + + // TODO: ask the function space to populate the constrained nodes appropriately + + // get the node map from the function space + auto node_map = _function_space.node_map(); + // fill information in nodal field + for (auto & [node, value] : _solution_field) { + // get the equation number of {node} + int eq = _equation_map.at(node); + if (eq != -1) { + // read the solution at {eq} + value = u[eq]; + } + } + + // all done + return; + } + + // accessor to the solution nodal field + constexpr auto solution() const noexcept -> const nodal_field_type & + { + return _solution_field; + } + + // accessor to the number of equations + constexpr auto n_equations() const noexcept -> int { return _n_equations; } + + // compute the L2 norm of the solution + template + constexpr auto compute_l2_error(const fields::field_c auto & u_exact) const + -> tensor::scalar_t + { + // initialize the norm + auto norm = tensor::scalar_t{ 0.0 }; + + // helper lambda to assemble the solution on a given element + constexpr auto _assemble_element_solution = []( + const auto & element, + const auto & _solution_field, + tensor::integer_sequence) { + // assemble the solution field from the shape functions + return ( + (element.template shape() * _solution_field(element.connectivity()[a])) + + ...); + }; + + // loop on all the cells of the mesh + for (const auto & element : _function_space.elements()) { + // assemble the solution field on this element + auto u_numerical = _assemble_element_solution( + element, _solution_field, tensor::make_integer_sequence()); + // assemble the elementary error field as a function of the barycentric coordinates + auto u_error = + u_numerical - u_exact.function()(element.parametrization().function()); + + // compute the elementary contribution to the L2 norm + norm += blocks::l2_norm_block(u_error.function()) + .compute(element); + } + + return std::sqrt(norm); + } + + // compute the H1 norm of the solution + template + constexpr auto compute_h1_error(const fields::field_c auto & u_exact) const + -> tensor::scalar_t + { + // initialize the norm + auto norm = tensor::scalar_t{ 0.0 }; + + // QUESTION: is there a proper place to put this method? Perhaps this belongs to the + // isoparametric element class? + // helper lambda to assemble the solution on a given element + constexpr auto _assemble_element_solution = []( + const auto & element, + const auto & _solution_field, + tensor::integer_sequence) { + // assemble the solution field from the shape functions + return ( + (element.template shape() * _solution_field(element.connectivity()[a])) + + ...); + }; + + // TOFIX: we should deduce this from {_assemble_element_solution} + // helper lambda to assemble the solution on a given element + constexpr auto _assemble_element_solution_gradient = + []( + const auto & element, const auto & _solution_field, + tensor::integer_sequence) { + // assemble the solution field from the shape functions + return ( + (element.template gradient() + * _solution_field(element.connectivity()[a])) + + ...); + }; + + // loop on all the cells of the mesh + for (const auto & element : _function_space.elements()) { + // assemble the solution field on this element + auto u_numerical = _assemble_element_solution( + element, _solution_field, tensor::make_integer_sequence()); + // assemble the elementary error field as a function of the barycentric coordinates + auto u_error = + u_numerical - u_exact.function()(element.parametrization().function()); + // + auto u_numerical_gradient = _assemble_element_solution_gradient( + element, _solution_field, tensor::make_integer_sequence()); + // assemble the elementary error gradient as a function of the barycentric + // coordinates + auto u_error_gradient = + u_numerical_gradient + - fields::gradient(u_exact).function()(element.parametrization().function()); + // compute the elementary contributions to the H1 norm + norm += blocks::l2_norm_block(u_error.function()) + .compute(element) + + blocks::l2_norm_block( + u_error_gradient.function()) + .compute(element); + } + + return std::sqrt(norm); + } + + private: + // a const reference to the function space + const function_space_type & _function_space; + + // the weakform + const weakform_type & _weakform; + + // the equation map + equation_map_type _equation_map; + + // the solution nodal field + nodal_field_type _solution_field; + + // the linear system of equations + linear_system_type _linear_system; + + // the number of equations in the linear system + int _n_equations = 0; + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/fem/FunctionSpace.h b/lib/mito/fem/FunctionSpace.h new file mode 100644 index 000000000..f4e7b8246 --- /dev/null +++ b/lib/mito/fem/FunctionSpace.h @@ -0,0 +1,121 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + // Class {FunctionSpace} represents a collection of finite elements of order {p} defined on a + // manifold and subjected to a set of constraints. + // TOFIX: add concept for element type + template + class FunctionSpace { + + public: + // the constraints type + using constraints_type = constraintsT; + // my template parameter, the finite element type + using element_type = elementT; + // typedef for a collection of finite elements + using elements_type = utilities::segmented_vector_t; + // the degree of the finite element + static constexpr int degree = element_type::degree; + // the dimension of the physical space + static constexpr int dim = element_type::dim; + // assemble the mesh node type + using mesh_node_type = geometry::node_t; + // the discretization node type + using discretization_node_type = typename element_type::discretization_node_type; + // the constrained nodes type + using constrained_nodes_type = std::set; + // the type of a map between the mesh nodes and discretization nodes + using map_type = std::unordered_map< + mesh_node_type, discretization_node_type, utilities::hash_function>; + + + public: + // the constructor + template < + manifolds::manifold_c manifoldT, + discretization_t discretizationT = discretization_t::CG> + // require compatibility between the manifold cell and the finite element cell + requires(std::is_same_v< + typename manifoldT::mesh_type::cell_type, typename element_type::cell_type>) + constexpr FunctionSpace(const manifoldT & manifold, const constraints_type & constraints) : + _elements(manifold.nElements()), + _constraints(constraints), + _node_map() + { + // discretize the manifold subject to the constraints + discretize( + manifold, constraints, _elements, _node_map, _constrained_nodes); + } + + // destructor + constexpr ~FunctionSpace() = default; + + // delete move constructor + constexpr FunctionSpace(FunctionSpace &&) noexcept = delete; + + // delete copy constructor + constexpr FunctionSpace(const FunctionSpace &) = delete; + + // delete assignment operator + constexpr FunctionSpace & operator=(const FunctionSpace &) = delete; + + // delete move assignment operator + constexpr FunctionSpace & operator=(FunctionSpace &&) noexcept = delete; + + public: + // TOFIX: not sure this should be constexpr + // accessor for the constraints + constexpr auto constraints() const noexcept -> const constraints_type & + { + return _constraints; + } + + // get the finite elements + auto elements() const noexcept -> const elements_type & { return _elements; } + + // get the constrained nodes + constexpr auto constrained_nodes() const noexcept -> const constrained_nodes_type & + { + return _constrained_nodes; + } + + // accessor for the node map + constexpr auto node_map() const noexcept -> const map_type & { return _node_map; } + + private: + // a collection of finite elements + elements_type _elements; + + // TOFIX: this should be a collection of constraints. Also, constraints may involve + // different degrees of freedom (e.g. periodic boundary conditions to impose relations + // between beam rotations). Therefore, the function space should be aware of the spatial + // dimension of the shape functions. + // + // QUESTION: do we need to maintain a reference to the constraints? + // + // the constraints + const constraints_type & _constraints; + + // the constrained nodes + constrained_nodes_type _constrained_nodes; + + // QUESTION: the reason why we need this map is to write the solution in the vtk writer file + // (we need to know how the solution maps to the mesh nodes). I am not sure this is a good + // reason to build and store this map, though. Also, if we plan to keep this map, we should + // come up with a better name + // a map between the mesh nodes and discretization nodes + map_type _node_map; + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/fem/Weakform.h b/lib/mito/fem/Weakform.h new file mode 100644 index 000000000..c13961e73 --- /dev/null +++ b/lib/mito/fem/Weakform.h @@ -0,0 +1,115 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + // TODO: concept for element type + template + class Weakform { + + private: + // the element type + using element_type = elementT; + // the number of nodes per element + static constexpr int n_element_nodes = element_type::n_nodes; + // the elementary matrix type + using elementary_matrix_type = tensor::matrix_t; + // the elementary vector type + using elementary_vector_type = tensor::vector_t; + // the type of the lhs assembly block + using lhs_assembly_block_type = + blocks::assembly_block_t; + // a collection of lhs assembly blocks + using lhs_assembly_blocks_type = std::vector; + // the type of the rhs assembly block + using rhs_assembly_block_type = + blocks::assembly_block_t; + // a collection of rhs assembly blocks + using rhs_assembly_blocks_type = std::vector; + + public: + // default constructor + constexpr Weakform() = default; + + // destructor + constexpr ~Weakform() = default; + + // delete move constructor + constexpr Weakform(Weakform &&) noexcept = delete; + + // delete copy constructor + constexpr Weakform(const Weakform &) = delete; + + // delete assignment operator + constexpr Weakform & operator=(const Weakform &) = delete; + + // delete move assignment operator + constexpr Weakform & operator=(Weakform &&) noexcept = delete; + + public: + // add a left hand side assembly block + constexpr auto add_block(const lhs_assembly_block_type & block) -> void + { + // add the block to the collection + _lhs_assembly_blocks.push_back(&block); + + // all done + return; + } + + // add a right hand side assembly block + constexpr auto add_block(const rhs_assembly_block_type & block) -> void + { + // add the block to the collection + _rhs_assembly_blocks.push_back(&block); + + // all done + return; + } + + // compute the elementary contributions to matrix and right-hand side from the weakform + constexpr auto compute_blocks(const element_type & element) const + -> std::pair + { + // instantiate the elementary matrix + auto elementary_matrix = elementary_matrix_type(); + // loop on the left hand side assembly blocks + for (const auto & block : _lhs_assembly_blocks) { + // compute the elementary contribution of the block + auto matrix_block = block->compute(element); + // add the elementary contribution to the elementary matrix + elementary_matrix += matrix_block; + } + + // instantiate the elementary vector + auto elementary_vector = elementary_vector_type(); + // loop on the right hand side assembly blocks + for (const auto & block : _rhs_assembly_blocks) { + // compute the elementary contribution of the block + auto vector_block = block->compute(element); + // add the elementary contribution to the elementary vector + elementary_vector += vector_block; + } + + // return the elementary matrix and vector + return { elementary_matrix, elementary_vector }; + } + + private: + // the collection of left hand side assembly blocks + lhs_assembly_blocks_type _lhs_assembly_blocks; + + // the collection of right hand side assembly blocks + rhs_assembly_blocks_type _rhs_assembly_blocks; + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/fem/api.h b/lib/mito/fem/api.h new file mode 100644 index 000000000..b0222bb33 --- /dev/null +++ b/lib/mito/fem/api.h @@ -0,0 +1,46 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + // nodal field factory + template + constexpr auto nodal_field(const functionSpaceT & function_space, std::string name); + + // the possible discretization types: continuous Galerking (CG) vs. discontinuous Galerkin (DG) + enum class discretization_t { CG, DG }; + + // function space alias + template + using function_space_t = FunctionSpace; + + // function space factory + template + constexpr auto function_space(const manifoldT & manifold, const constraintsT & constraints); + + // weakform alias + template + using weakform_t = Weakform; + + // weakform factory + template + constexpr auto weakform(); + + // discrete system alias + template + using discrete_system_t = DiscreteSystem; + + // discrete system factory + template + constexpr auto discrete_system( + const functionSpaceT & function_space, const std::string & label); +} + + +// end of file diff --git a/lib/mito/fem/blocks.h b/lib/mito/fem/blocks.h new file mode 100644 index 000000000..213fd9643 --- /dev/null +++ b/lib/mito/fem/blocks.h @@ -0,0 +1,14 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// publish the interface +#include "blocks/public.h" + + +// end of file diff --git a/lib/mito/fem/blocks/AssemblyBlock.h b/lib/mito/fem/blocks/AssemblyBlock.h new file mode 100644 index 000000000..4d44ec69b --- /dev/null +++ b/lib/mito/fem/blocks/AssemblyBlock.h @@ -0,0 +1,50 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem::blocks { + + // TODO: implement sum and subtraction operators for the blocks (only for blocks that result in + // the same elementary type) + + template + class AssemblyBlock { + + public: + // my template parameters + using element_type = elementT; + using elementary_block_type = blockT; + + public: + // the constructor + constexpr AssemblyBlock() = default; + + // destructor + constexpr ~AssemblyBlock() = default; + + // delete move constructor + constexpr AssemblyBlock(AssemblyBlock &&) noexcept = delete; + + // delete copy constructor + constexpr AssemblyBlock(const AssemblyBlock &) = delete; + + // delete assignment operator + constexpr AssemblyBlock & operator=(const AssemblyBlock &) = delete; + + // delete move assignment operator + constexpr AssemblyBlock & operator=(AssemblyBlock &&) noexcept = delete; + + public: + // compute the elementary contribution of this block + virtual auto compute(const element_type & element) const -> elementary_block_type = 0; + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/fem/blocks/GradGradBlock.h b/lib/mito/fem/blocks/GradGradBlock.h new file mode 100644 index 000000000..68fd7d1b2 --- /dev/null +++ b/lib/mito/fem/blocks/GradGradBlock.h @@ -0,0 +1,73 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem::blocks { + + template + class GradGradBlock : public AssemblyBlock> { + + public: + // my template parameters + using element_type = elementT; + using elementary_block_type = tensor::matrix_t; + using quadrature_rule_type = quadratureRuleT; + + public: + // instantiate the quadrature rule + static constexpr auto quadrature_rule = quadrature_rule_type(); + + public: + // compute the elementary contribution of this block + auto compute(const element_type & element) const -> elementary_block_type override + { + // the number of nodes per element + constexpr int n_nodes = element_type::n_nodes; + + // the number of quadrature points per element + constexpr int n_quads = quadrature_rule_type::npoints; + + // the elementary matrix + elementary_block_type elementary_matrix; + + // loop on the quadrature points + tensor::constexpr_for_1([&]() { + // the parametric coordinates of the quadrature point + constexpr auto xi = quadrature_rule.point(q); + + // the quadrature weight at this point scaled with the area of the canonical simplex + constexpr auto w = + element_type::canonical_element_type::area * quadrature_rule.weight(q); + + // precompute the common factor + auto factor = w * tensor::determinant(element.jacobian()(xi)); + + // loop on the nodes of the element + tensor::constexpr_for_1([&]() { + // evaluate the spatial gradient of the element's a-th shape function at {xi} + auto dphi_a = element.template gradient()(xi); + // loop on the nodes of the element + tensor::constexpr_for_1([&]() { + // evaluate the spatial gradient of the element's b-th shape function at + // {xi} + auto dphi_b = element.template gradient()(xi); + // populate the elementary contribution to the matrix + elementary_matrix[{ a, b }] += factor * dphi_a * dphi_b; + }); + }); + }); + + // all done + return elementary_matrix; + } + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/fem/blocks/L2NormBlock.h b/lib/mito/fem/blocks/L2NormBlock.h new file mode 100644 index 000000000..ec34df694 --- /dev/null +++ b/lib/mito/fem/blocks/L2NormBlock.h @@ -0,0 +1,73 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem::blocks { + + template + // require that {functionT} is a function in barycentric coordinates + requires(std::is_same_v< + typename functionT::input_type, typename quadratureRuleT::quadrature_point_type>) + class L2NormBlock : public AssemblyBlock { + + public: + // my template parameters + using element_type = elementT; + using elementary_block_type = tensor::scalar_t; + using quadrature_rule_type = quadratureRuleT; + + // the type of the function to compute the L2 norm of + using function_type = functionT; + + public: + // instantiate the quadrature rule + static constexpr auto quadrature_rule = quadrature_rule_type(); + + public: + // constructor + L2NormBlock(const function_type & function) : _function(function) {} + + public: + // compute the elementary contribution of this block + auto compute(const element_type & element) const -> elementary_block_type override + { + // the number of quadrature points per element + constexpr int n_quads = quadrature_rule_type::npoints; + + // the elementary contribution to the L2 norm + auto elementary_contribution = elementary_block_type{}; + + // loop on the quadrature points + tensor::constexpr_for_1([&]() { + // the barycentric coordinates of the quadrature point + constexpr auto xi = quadrature_rule.point(q); + + // the quadrature weight at this point scaled with the area of the canonical simplex + constexpr auto w = + element_type::canonical_element_type::area * quadrature_rule.weight(q); + + // precompute the common factor + auto factor = w * tensor::determinant(element.jacobian()(xi)); + + // populate the elementary contribution to the matrix + elementary_contribution += factor * _function(xi) * _function(xi); + }); + + // all done + return elementary_contribution; + } + + private: + // the function to compute the L2 norm of + const function_type & _function; + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/fem/blocks/MassBlock.h b/lib/mito/fem/blocks/MassBlock.h new file mode 100644 index 000000000..8d95eb710 --- /dev/null +++ b/lib/mito/fem/blocks/MassBlock.h @@ -0,0 +1,73 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem::blocks { + + template + class MassBlock : public AssemblyBlock> { + + public: + // my template parameters + using element_type = elementT; + using elementary_block_type = tensor::matrix_t; + using quadrature_rule_type = quadratureRuleT; + + public: + // instantiate the quadrature rule + static constexpr auto quadrature_rule = quadrature_rule_type(); + + public: + // compute the elementary contribution of this block + auto compute(const element_type & element) const -> elementary_block_type override + { + // the number of nodes per element + constexpr int n_nodes = element_type::n_nodes; + + // the number of quadrature points per element + constexpr int n_quads = quadrature_rule_type::npoints; + + // the elementary matrix + elementary_block_type elementary_matrix; + + // loop on the quadrature points + tensor::constexpr_for_1([&]() { + // the parametric coordinates of the quadrature point + constexpr auto xi = quadrature_rule.point(q); + + // the quadrature weight at this point scaled with the area of the canonical simplex + constexpr auto w = + element_type::canonical_element_type::area * quadrature_rule.weight(q); + + // precompute the common factor + auto factor = w * tensor::determinant(element.jacobian()(xi)); + + // loop on the nodes of the element + tensor::constexpr_for_1([&]() { + // evaluate the spatial gradient of the element's a-th shape function at {xi} + auto phi_a = element.template shape()(xi); + // loop on the nodes of the element + tensor::constexpr_for_1([&]() { + // evaluate the spatial gradient of the element's b-th shape function at + // {xi} + auto phi_b = element.template shape()(xi); + // populate the elementary contribution to the matrix + elementary_matrix[{ a, b }] += factor * phi_a * phi_b; + }); + }); + }); + + // all done + return elementary_matrix; + } + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/fem/blocks/SourceTermBlock.h b/lib/mito/fem/blocks/SourceTermBlock.h new file mode 100644 index 000000000..5dc123036 --- /dev/null +++ b/lib/mito/fem/blocks/SourceTermBlock.h @@ -0,0 +1,83 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem::blocks { + + // TOFIX: the source does not need to be necessarily a scalar field, it can be some other field + // see if we can use {field_c} instead of {scalar_field_c} + template + class SourceTermBlock : public AssemblyBlock> { + + public: + // my template parameters + using element_type = elementT; + using elementary_block_type = tensor::vector_t; + using quadrature_rule_type = quadratureRuleT; + + // the type of the source term function + using source_field_type = sourceFieldT; + + public: + // instantiate the quadrature rule + static constexpr auto quadrature_rule = quadrature_rule_type(); + + public: + // constructor + SourceTermBlock(const source_field_type & source_field) : _source_field(source_field) {} + + public: + // compute the elementary contribution of this block + auto compute(const element_type & element) const -> elementary_block_type override + { + // the number of nodes per element + constexpr int n_nodes = element_type::n_nodes; + + // the number of quadrature points per element + constexpr int n_quads = quadrature_rule_type::npoints; + + // the elementary rhs + elementary_block_type elementary_rhs{}; + + // loop on the quadrature points + tensor::constexpr_for_1([&]() { + // the barycentric coordinates of the quadrature point + constexpr auto xi = quadrature_rule.point(q); + + // the coordinates of the quadrature point + auto coord = element.parametrization()(xi); + + // the quadrature weight at this point scaled with the area of the canonical simplex + constexpr auto w = + element_type::canonical_element_type::area * quadrature_rule.weight(q); + + // precompute the common factor + auto factor = w * tensor::determinant(element.jacobian()(xi)); + + // loop on the nodes of the element + tensor::constexpr_for_1([&]() { + // evaluate the a-th shape function at {xi} + auto phi_a = element.template shape()(xi); + // populate the elementary contribution to the rhs + elementary_rhs[{ a }] += factor * _source_field(coord) * phi_a; + }); + }); + + // all done + return elementary_rhs; + } + + private: + // the source term field + const source_field_type & _source_field; + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/fem/blocks/api.h b/lib/mito/fem/blocks/api.h new file mode 100644 index 000000000..e68a2584b --- /dev/null +++ b/lib/mito/fem/blocks/api.h @@ -0,0 +1,50 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem::blocks { + + // assembly block + template + using assembly_block_t = AssemblyBlock; + + // grad grad block + template + using grad_grad_block_t = GradGradBlock; + + // grad grad block factory + template + constexpr auto grad_grad_block(); + + // mass block + template + using mass_block_t = MassBlock; + + // mass block factory + template + constexpr auto mass_block(); + + // source term block + template + using source_term_block_t = SourceTermBlock; + + // source term block factory + template + constexpr auto source_term_block(const sourceFieldT & f); + + // L2 norm block + template + using l2_norm_block_t = L2NormBlock; + + // L2 norm block factory + template + constexpr auto l2_norm_block(const functionT & f); +} + + +// end of file diff --git a/lib/mito/fem/blocks/externals.h b/lib/mito/fem/blocks/externals.h new file mode 100644 index 000000000..d3746bcb8 --- /dev/null +++ b/lib/mito/fem/blocks/externals.h @@ -0,0 +1,18 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// externals + +// support +#include "../../tensor.h" +#include "../../manifolds.h" +#include "../../quadrature.h" + + +// end of file diff --git a/lib/mito/fem/blocks/factories.h b/lib/mito/fem/blocks/factories.h new file mode 100644 index 000000000..014e0a95a --- /dev/null +++ b/lib/mito/fem/blocks/factories.h @@ -0,0 +1,47 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem::blocks { + + // matrix block factory + template + constexpr auto grad_grad_block() + { + // all done + return grad_grad_block_t(); + } + + // mass block factory + template + constexpr auto mass_block() + { + // all done + return mass_block_t(); + } + + // source term block factory + template + constexpr auto source_term_block(const sourceFieldT & f) + { + // all done + return source_term_block_t(f); + } + + // L2 norm block factory + template + constexpr auto l2_norm_block(const functionT & f) + { + // all done + return l2_norm_block_t(f); + } + +} + + +// end of file diff --git a/lib/mito/fem/blocks/forward.h b/lib/mito/fem/blocks/forward.h new file mode 100644 index 000000000..ec91f5397 --- /dev/null +++ b/lib/mito/fem/blocks/forward.h @@ -0,0 +1,37 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem::blocks { + + // assembly block + template + class AssemblyBlock; + + // grad grad block + template + class GradGradBlock; + + // mass block + template + class MassBlock; + + // source term block + template + class SourceTermBlock; + + // L2 norm block for a function defined at quadrature points in barycentric coordinates + template + // require that {functionT} is a function in barycentric coordinates + requires(std::is_same_v< + typename functionT::input_type, typename quadratureRuleT::quadrature_point_type>) + class L2NormBlock; +} + + +// end of file diff --git a/lib/mito/fem/blocks/public.h b/lib/mito/fem/blocks/public.h new file mode 100644 index 000000000..f212c0ad6 --- /dev/null +++ b/lib/mito/fem/blocks/public.h @@ -0,0 +1,29 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// external packages +#include "externals.h" + +// get the forward declarations +#include "forward.h" + +// published types factories; this is the file you are looking for... +#include "api.h" + +// classes implementation +#include "AssemblyBlock.h" +#include "GradGradBlock.h" +#include "MassBlock.h" +#include "SourceTermBlock.h" +#include "L2NormBlock.h" + +// factories implementation +#include "factories.h" + +// end of file diff --git a/lib/mito/fem/elements.h b/lib/mito/fem/elements.h new file mode 100644 index 000000000..2992dad3d --- /dev/null +++ b/lib/mito/fem/elements.h @@ -0,0 +1,14 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// publish the interface +#include "elements/public.h" + + +// end of file diff --git a/lib/mito/fem/elements/Discretizer.h b/lib/mito/fem/elements/Discretizer.h new file mode 100644 index 000000000..23077a0cf --- /dev/null +++ b/lib/mito/fem/elements/Discretizer.h @@ -0,0 +1,34 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + template + struct Discretizer { + template < + typename manifoldT, typename constraintsT, typename elements_type, typename map_type, + typename constrained_nodes_type> + static void apply( + const manifoldT &, const constraintsT &, elements_type &, map_type &, + constrained_nodes_type &); + }; + + template + auto discretize( + const auto & manifold, const auto & constraints, auto & elements, auto & node_map, + auto & constrained_nodes) + { + Discretizer::apply( + manifold, constraints, elements, node_map, constrained_nodes); + } + +} + + +// end of file diff --git a/lib/mito/fem/elements/IsoparametricTriangle.h b/lib/mito/fem/elements/IsoparametricTriangle.h new file mode 100644 index 000000000..7c8ed5bcb --- /dev/null +++ b/lib/mito/fem/elements/IsoparametricTriangle.h @@ -0,0 +1,76 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// DESIGN NOTES +// Class {IsoparametricTriangle} represents a second order simplex equipped barycentric coordinates. + + +namespace mito::fem { + + class IsoparametricTriangle : public utilities::Invalidatable { + public: + // the dimension of the physical space + static constexpr int dim = 2; + // the discretization node type + using discretization_node_type = discrete::discretization_node_t; + // the underlying cell type + using cell_type = geometry::triangle_t; + + protected: + // cartesian coordinates in 2D + using coordinates_type = geometry::coordinates_t<2, geometry::CARTESIAN>; + // the coordinate system type + using coordinate_system_type = geometry::coordinate_system_t; + // the vector type + using vector_type = tensor::vector_t<2>; + + public: + // the default constructor + constexpr IsoparametricTriangle( + const cell_type & cell, const coordinate_system_type & coord_system) : + _cell(cell), + _x0{ coord_system.coordinates(cell.nodes()[0]->point()) - coordinates_type{} }, + _x1{ coord_system.coordinates(cell.nodes()[1]->point()) - coordinates_type{} }, + _x2{ coord_system.coordinates(cell.nodes()[2]->point()) - coordinates_type{} } + {} + + // destructor + constexpr ~IsoparametricTriangle() = default; + + // delete move constructor + constexpr IsoparametricTriangle(IsoparametricTriangle &&) noexcept = delete; + + // delete copy constructor + constexpr IsoparametricTriangle(const IsoparametricTriangle &) = delete; + + // delete assignment operator + constexpr IsoparametricTriangle & operator=(const IsoparametricTriangle &) = delete; + + // delete move assignment operator + constexpr IsoparametricTriangle & operator=(IsoparametricTriangle &&) noexcept = delete; + + public: + // get the geometric simplex + constexpr auto cell() const noexcept -> const cell_type & { return _cell; } + + protected: + // QUESTION: do we need to maintain a reference to the geometric simplex? + // a const reference to the geometric simplex + const cell_type & _cell; + + // the coordinates of the discretization nodes of the triangle + const vector_type _x0; + const vector_type _x1; + const vector_type _x2; + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/fem/elements/api.h b/lib/mito/fem/elements/api.h new file mode 100644 index 000000000..d37901314 --- /dev/null +++ b/lib/mito/fem/elements/api.h @@ -0,0 +1,19 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + // type alias for convenient access to the isoparametric simplex type + template + using isoparametric_simplex_t = typename isoparametric_simplex::type; + +} + + +// end of file diff --git a/lib/mito/fem/elements/elements_library.h b/lib/mito/fem/elements/elements_library.h new file mode 100644 index 000000000..d52cfc68d --- /dev/null +++ b/lib/mito/fem/elements/elements_library.h @@ -0,0 +1,14 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +#include "tri1/public.h" +#include "tri2/public.h" + + +// end of file diff --git a/lib/mito/fem/elements/externals.h b/lib/mito/fem/elements/externals.h new file mode 100644 index 000000000..bdd89b728 --- /dev/null +++ b/lib/mito/fem/elements/externals.h @@ -0,0 +1,10 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// end of file diff --git a/lib/mito/fem/elements/factories.h b/lib/mito/fem/elements/factories.h new file mode 100644 index 000000000..3abada991 --- /dev/null +++ b/lib/mito/fem/elements/factories.h @@ -0,0 +1,16 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + +} + + +// end of file diff --git a/lib/mito/fem/elements/forward.h b/lib/mito/fem/elements/forward.h new file mode 100644 index 000000000..f8c9f3078 --- /dev/null +++ b/lib/mito/fem/elements/forward.h @@ -0,0 +1,24 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + // the struct that implements the discretization strategy + template + struct Discretizer; + + // struct storing the type of an isoparametric simplex of polynomial degree {degree} on a + // geometric simplex of type {geometricSimplexT} + template + struct isoparametric_simplex; + +} + + +// end of file diff --git a/lib/mito/fem/elements/public.h b/lib/mito/fem/elements/public.h new file mode 100644 index 000000000..40f144579 --- /dev/null +++ b/lib/mito/fem/elements/public.h @@ -0,0 +1,32 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// external packages +#include "externals.h" + +// get the forward declarations +#include "forward.h" + +// published types factories; this is the file you are looking for... +#include "api.h" + +// classes implementation +#include "IsoparametricTriangle.h" +#include "Discretizer.h" + +// library of finite elements +#include "elements_library.h" + +// factories implementation +#include "factories.h" + +// utilities implementation +#include "utilities.h" + +// end of file diff --git a/lib/mito/fem/elements/tri1/DiscretizerCG.h b/lib/mito/fem/elements/tri1/DiscretizerCG.h new file mode 100644 index 000000000..efeadf7af --- /dev/null +++ b/lib/mito/fem/elements/tri1/DiscretizerCG.h @@ -0,0 +1,68 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + // discretizer specialization for {IsoparametricTriangleP1} with continuous Galerkin + template <> + struct Discretizer { + template < + typename manifoldT, typename constraintsT, typename elements_type, typename map_type, + typename constrained_nodes_type> + static void apply( + const manifoldT & manifold, const constraintsT & constraints, elements_type & elements, + map_type & node_map, constrained_nodes_type & constrained_nodes) + { + + // the discretization node type + using discretization_node_type = + typename IsoparametricTriangleP1::discretization_node_type; + + // the connectivity type + using connectivity_type = typename IsoparametricTriangleP1::connectivity_type; + + // get the coordinate system of the manifold + const auto & coord_system = manifold.coordinate_system(); + + // loop on the cells of the mesh + for (const auto & cell : manifold.elements()) { + + // get the nodes of the cell + const auto & nodes = cell.nodes(); + + // add the nodes to the map (if the mesh node is already present in the map, + // then the present discretization node is used) + auto node_0 = + node_map.insert({ nodes[0], discretization_node_type() }).first->second; + auto node_1 = + node_map.insert({ nodes[1], discretization_node_type() }).first->second; + auto node_2 = + node_map.insert({ nodes[2], discretization_node_type() }).first->second; + + // create a finite element for each cell and add it to the pile + elements.emplace(cell, coord_system, connectivity_type{ node_0, node_1, node_2 }); + } + + // populate the constrained nodes + for (const auto & cell : constraints.domain().cells()) { + for (const auto & node : cell.nodes()) { + // get the discretization node associated with the mesh node from the map + auto it = node_map.find(node); + // add the node to the constrained nodes + constrained_nodes.insert(it->second); + } + } + + // all done + return; + } + }; +} + +// end of file diff --git a/lib/mito/fem/elements/tri1/IsoparametricTriangleP1.h b/lib/mito/fem/elements/tri1/IsoparametricTriangleP1.h new file mode 100644 index 000000000..fcd527d92 --- /dev/null +++ b/lib/mito/fem/elements/tri1/IsoparametricTriangleP1.h @@ -0,0 +1,138 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// DESIGN NOTES +// Class {IsoparametricTriangleP1} represents a second order simplex living in 2D cartesian space, +// equipped with linear shape functions defined in the parametric space. + + +namespace mito::fem { + + class IsoparametricTriangleP1 : public IsoparametricTriangle { + + public: + // the degree of the finite element + static constexpr int degree = 1; + // the type of shape functions + using shape_functions_type = ShapeTriangleP1; + // the canonical element type + using canonical_element_type = typename shape_functions_type::reference_element_type; + // the parametric coordinates type + using parametric_coordinates_type = + typename canonical_element_type::parametric_coordinates_type; + // the linear shape functions + static constexpr auto shape_functions = shape_functions_type(); + // the number of discretization discretization nodes + static constexpr int n_nodes = shape_functions_type::N; + // a collection of discretization discretization nodes + using connectivity_type = std::array; + + public: + // the default constructor + inline IsoparametricTriangleP1( + const cell_type & geometric_simplex, const coordinate_system_type & coord_system, + const connectivity_type & connectivity) : + IsoparametricTriangle(geometric_simplex, coord_system), + _connectivity(connectivity) + {} + + // destructor + inline ~IsoparametricTriangleP1() = default; + + // delete move constructor + constexpr IsoparametricTriangleP1(IsoparametricTriangleP1 &&) noexcept = delete; + + // delete copy constructor + constexpr IsoparametricTriangleP1(const IsoparametricTriangleP1 &) = delete; + + // delete assignment operator + constexpr IsoparametricTriangleP1 & operator=(const IsoparametricTriangleP1 &) = delete; + + // delete move assignment operator + constexpr IsoparametricTriangleP1 & operator=(IsoparametricTriangleP1 &&) noexcept = delete; + + public: + // get the discretization nodes + constexpr auto connectivity() const noexcept -> const connectivity_type & + { + return _connectivity; + } + + // get the isoparametric mapping from barycentric coordinates to physical coordinates + constexpr auto parametrization() const + { + // get the shape functions + constexpr auto phi_0 = shape_functions.shape<0>(); + constexpr auto phi_1 = shape_functions.shape<1>(); + constexpr auto phi_2 = shape_functions.shape<2>(); + + // return the isoparametric mapping from parametric to physical coordinates + return mito::fields::linear_combination( + std::array{ _x0, _x1, _x2 }, phi_0, phi_1, phi_2); + } + + // get the shape function associated with local node {a} + template + requires(a >= 0 && a < n_nodes) + constexpr auto shape() const + { + // return the shape functions + return shape_functions.shape(); + } + + // get the jacobian of the isoparametric mapping from barycentric to actual coordinates + constexpr auto jacobian() const + { + // assemble the jacobian as a function of barycentric coordinates + auto jacobian_function = functions::function( + [&](const parametric_coordinates_type & xi) -> tensor::matrix_t<2> { + // get the shape functions derivatives + constexpr auto dphi_0 = shape_functions.dshape<0>(); + constexpr auto dphi_1 = shape_functions.dshape<1>(); + constexpr auto dphi_2 = shape_functions.dshape<2>(); + + // compute the gradient of the isoparametric mapping + return ( + tensor::dyadic(_x0, dphi_0(xi)) + tensor::dyadic(_x1, dphi_1(xi)) + + tensor::dyadic(_x2, dphi_2(xi))); + }); + + // and return it + return jacobian_function; + } + + // get the gradient of the a-th shape function as a function of barycentric coordinates + template + requires(a >= 0 && a < n_nodes) + constexpr auto gradient() const + { + // assemble the gradient as a function of barycentric coordinates + auto gradient_function = + fields::field([&](const parametric_coordinates_type & xi) -> tensor::vector_t<2> { + // the jacobian of the mapping from the reference element to the physical + // element evaluated at {xi} + auto J = jacobian()(xi); + // the derivative of the coordinates with respect to the barycentric coordinates + auto J_inv = tensor::inverse(J); + // return the spatial gradients of the shape functions evaluated at {xi} + return shape_functions.dshape()(xi) * J_inv; + }); + // and return it + return gradient_function; + } + + private: + // the discretization nodes of the simplex + const connectivity_type _connectivity; + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/fem/elements/tri1/ShapeTriangleP1.h b/lib/mito/fem/elements/tri1/ShapeTriangleP1.h new file mode 100644 index 000000000..f90867ba6 --- /dev/null +++ b/lib/mito/fem/elements/tri1/ShapeTriangleP1.h @@ -0,0 +1,61 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + class ShapeTriangleP1 { + + public: + // the reference element + using reference_element_type = geometry::reference_triangle_t; + // the number of shape functions + static constexpr int N = 3; + + private: + // linear shape functions on the reference triangle in parametric coordinates + static constexpr auto xi_0 = reference_element_type::xi<0>; + static constexpr auto xi_1 = reference_element_type::xi<1>; + static constexpr auto xi_2 = 1.0 - xi_0 - xi_1; + + // linear shape functions on the triangle + static constexpr auto phi_0 = xi_0; + static constexpr auto phi_1 = xi_1; + static constexpr auto phi_2 = xi_2; + + // the shape functions + static constexpr auto phi = std::make_tuple(phi_0, phi_1, phi_2); + + // the gradients of the shape functions + static constexpr auto dphi = std::make_tuple( + fields::gradient(phi_0), fields::gradient(phi_1), fields::gradient(phi_2)); + + public: + // get the a-th shape function as a function of parametric coordinates + template + requires(a >= 0 && a < N) + constexpr auto shape() const + { + // return the a-th shape function + return std::get(phi); + } + + // get the a-th shape function's derivative as a function of parametric coordinates + template + requires(a >= 0 && a < N) + constexpr auto dshape() const + { + // return the a-th shape function's gradient + return std::get(dphi); + } + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/fem/elements/tri1/api.h b/lib/mito/fem/elements/tri1/api.h new file mode 100644 index 000000000..937d35638 --- /dev/null +++ b/lib/mito/fem/elements/tri1/api.h @@ -0,0 +1,21 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + // specialization for linear shape functions on triangles in 2D + template <> + struct isoparametric_simplex<1, geometry::triangle_t<2>> { + using type = IsoparametricTriangleP1; + }; + +} + + +// end of file diff --git a/lib/mito/fem/elements/tri1/public.h b/lib/mito/fem/elements/tri1/public.h new file mode 100644 index 000000000..01bfcf009 --- /dev/null +++ b/lib/mito/fem/elements/tri1/public.h @@ -0,0 +1,19 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// classes implementation +#include "ShapeTriangleP1.h" +#include "IsoparametricTriangleP1.h" +#include "DiscretizerCG.h" + +// published types and factories +#include "api.h" + + +// end of file diff --git a/lib/mito/fem/elements/tri2/DiscretizerCG.h b/lib/mito/fem/elements/tri2/DiscretizerCG.h new file mode 100644 index 000000000..919f368a4 --- /dev/null +++ b/lib/mito/fem/elements/tri2/DiscretizerCG.h @@ -0,0 +1,109 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + // discretizer specialization for {IsoparametricTriangleP2} with continuous Galerkin + template <> + struct Discretizer { + template < + typename manifoldT, typename constraintsT, typename elements_type, typename map_type, + typename constrained_nodes_type> + static void apply( + const manifoldT & manifold, const constraintsT & constraints, elements_type & elements, + map_type & node_map, constrained_nodes_type & constrained_nodes) + { + // the dimension of the physical space + constexpr int dim = IsoparametricTriangleP2::dim; + + // assemble the mesh node type + using mesh_node_type = geometry::node_t; + + // the discretization node type + using discretization_node_type = + typename IsoparametricTriangleP2::discretization_node_type; + + // the connectivity type + using connectivity_type = typename IsoparametricTriangleP2::connectivity_type; + + // id type of mesh nodes + using mesh_node_id_t = utilities::index_t; + + // the type of a map between the two vertices and the middle discretization nodes + using mid_nodes_map_type = + std::map, discretization_node_type>; + + // create a map to store the mid nodes + auto mid_nodes_map = mid_nodes_map_type(); + + // get the coordinate system of the manifold + const auto & coord_system = manifold.coordinate_system(); + + // loop on the cells of the mesh + for (const auto & cell : manifold.elements()) { + + // get the nodes of the cell + const auto & nodes = cell.nodes(); + + // add the nodes to the map (if the mesh node is already present in the map, + // then the present discretization node is used) + auto node_0 = + node_map.insert({ nodes[0], discretization_node_type() }).first->second; + auto node_1 = + node_map.insert({ nodes[1], discretization_node_type() }).first->second; + auto node_2 = + node_map.insert({ nodes[2], discretization_node_type() }).first->second; + + auto ordered_nodes_3 = (node_0.id() < node_1.id()) ? + std::array{ node_0.id(), node_1.id() } : + std::array{ node_1.id(), node_0.id() }; + auto node_3 = mid_nodes_map.insert({ ordered_nodes_3, discretization_node_type() }) + .first->second; + auto ordered_nodes_4 = (node_1.id() < node_2.id()) ? + std::array{ node_1.id(), node_2.id() } : + std::array{ node_2.id(), node_1.id() }; + auto node_4 = mid_nodes_map.insert({ ordered_nodes_4, discretization_node_type() }) + .first->second; + auto ordered_nodes_5 = (node_2.id() < node_0.id()) ? + std::array{ node_2.id(), node_0.id() } : + std::array{ node_0.id(), node_2.id() }; + auto node_5 = mid_nodes_map.insert({ ordered_nodes_5, discretization_node_type() }) + .first->second; + + // create a finite element for each cell and add it to the pile + elements.emplace( + cell, coord_system, + connectivity_type{ node_0, node_1, node_2, node_3, node_4, node_5 }); + } + + // populate the constrained nodes + for (const auto & cell : constraints.domain().cells()) { + for (const auto & node : cell.nodes()) { + // get the discretization node associated with the mesh node from the map + auto it = node_map.find(node); + // add the node to the constrained nodes + constrained_nodes.insert(it->second); + } + auto node_0 = node_map.at(cell.nodes()[0]); + auto node_1 = node_map.at(cell.nodes()[1]); + auto ordered_nodes = (node_0.id() < node_1.id()) ? + std::array{ node_0.id(), node_1.id() } : + std::array{ node_1.id(), node_0.id() }; + auto node = mid_nodes_map.at(ordered_nodes); + // add the node to the constrained nodes + constrained_nodes.insert(node); + } + + // all done + return; + } + }; +} + +// end of file diff --git a/lib/mito/fem/elements/tri2/IsoparametricTriangleP2.h b/lib/mito/fem/elements/tri2/IsoparametricTriangleP2.h new file mode 100644 index 000000000..8248875df --- /dev/null +++ b/lib/mito/fem/elements/tri2/IsoparametricTriangleP2.h @@ -0,0 +1,153 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// DESIGN NOTES +// Class {IsoparametricTriangleP2} represents a second order simplex living in 2D cartesian space +// equipped with quadratic shape functions defined in the parametric space. + + +namespace mito::fem { + + class IsoparametricTriangleP2 : public IsoparametricTriangle { + + public: + // the degree of the finite element + static constexpr int degree = 2; + // the type of shape functions + using shape_functions_type = ShapeTriangleP2; + // the canonical element type + using canonical_element_type = typename shape_functions_type::reference_element_type; + // type of a point in parametric coordinates + using parametric_coordinates_type = + typename canonical_element_type::parametric_coordinates_type; + // the linear shape functions + static constexpr auto shape_functions = shape_functions_type(); + // the number of discretization nodes + static constexpr int n_nodes = shape_functions_type::N; + // a collection of discretization nodes + using connectivity_type = std::array; + + public: + // the default constructor + inline IsoparametricTriangleP2( + const cell_type & geometric_simplex, const coordinate_system_type & coord_system, + const connectivity_type & connectivity) : + IsoparametricTriangle(geometric_simplex, coord_system), + _connectivity(connectivity) + {} + + // destructor + ~IsoparametricTriangleP2() = default; + + // delete move constructor + constexpr IsoparametricTriangleP2(IsoparametricTriangleP2 &&) noexcept = delete; + + // delete copy constructor + constexpr IsoparametricTriangleP2(const IsoparametricTriangleP2 &) = delete; + + // delete assignment operator + constexpr IsoparametricTriangleP2 & operator=(const IsoparametricTriangleP2 &) = delete; + + // delete move assignment operator + constexpr IsoparametricTriangleP2 & operator=(IsoparametricTriangleP2 &&) noexcept = delete; + + public: + // get the isoparametric mapping from barycentric coordinates to physical coordinates + constexpr auto parametrization() const + { + auto x3 = 0.5 * (_x0 + _x1); + auto x4 = 0.5 * (_x1 + _x2); + auto x5 = 0.5 * (_x2 + _x0); + + // get the shape functions + constexpr auto phi_0 = shape_functions.shape<0>(); + constexpr auto phi_1 = shape_functions.shape<1>(); + constexpr auto phi_2 = shape_functions.shape<2>(); + constexpr auto phi_3 = shape_functions.shape<3>(); + constexpr auto phi_4 = shape_functions.shape<4>(); + constexpr auto phi_5 = shape_functions.shape<5>(); + + // return the isoparametric mapping from barycentric to physical coordinates + return mito::fields::linear_combination( + std::array{ _x0, _x1, _x2, x3, x4, x5 }, phi_0, phi_1, phi_2, phi_3, phi_4, phi_5); + } + + // get the discretization nodes + constexpr auto connectivity() const noexcept -> const connectivity_type & + { + return _connectivity; + } + + // get the shape function associated with local node {a} + template + requires(a >= 0 && a < n_nodes) + constexpr auto shape() const + { + // return the shape functions + return shape_functions.shape(); + } + + // get the jacobian of the isoparametric mapping from barycentric to actual coordinates + constexpr auto jacobian() const + { + // assemble the jacobian as a function of barycentric coordinates + auto jacobian_function = functions::function( + [&](const parametric_coordinates_type & xi) -> tensor::matrix_t<2> { + auto x3 = 0.5 * (_x0 + _x1); + auto x4 = 0.5 * (_x1 + _x2); + auto x5 = 0.5 * (_x2 + _x0); + + // get the shape functions derivatives + constexpr auto dphi_0 = shape_functions.dshape<0>(); + constexpr auto dphi_1 = shape_functions.dshape<1>(); + constexpr auto dphi_2 = shape_functions.dshape<2>(); + constexpr auto dphi_3 = shape_functions.dshape<3>(); + constexpr auto dphi_4 = shape_functions.dshape<4>(); + constexpr auto dphi_5 = shape_functions.dshape<5>(); + + // compute the gradient of the isoparametric mapping + return ( + tensor::dyadic(_x0, dphi_0(xi)) + tensor::dyadic(_x1, dphi_1(xi)) + + tensor::dyadic(_x2, dphi_2(xi)) + tensor::dyadic(x3, dphi_3(xi)) + + tensor::dyadic(x4, dphi_4(xi)) + tensor::dyadic(x5, dphi_5(xi))); + }); + + // and return it + return jacobian_function; + } + + // get the gradient of the a-th shape function as a function of barycentric coordinates + template + requires(a >= 0 && a < n_nodes) + constexpr auto gradient() const + { + // assemble the gradient as a function of barycentric coordinates + auto gradient_function = + fields::field([&](const parametric_coordinates_type & xi) -> tensor::vector_t<2> { + // the jacobian of the mapping from the reference element to the physical + // element evaluated at {xi} + auto J = jacobian()(xi); + // the derivative of the coordinates with respect to the barycentric coordinates + auto J_inv = tensor::inverse(J); + // return the spatial gradients of the shape functions evaluated at {xi} + return shape_functions.dshape()(xi) * J_inv; + }); + // and return it + return gradient_function; + } + + private: + // the discretization nodes of the simplex + const connectivity_type _connectivity; + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/fem/elements/tri2/ShapeTriangleP2.h b/lib/mito/fem/elements/tri2/ShapeTriangleP2.h new file mode 100644 index 000000000..0640fb564 --- /dev/null +++ b/lib/mito/fem/elements/tri2/ShapeTriangleP2.h @@ -0,0 +1,65 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + class ShapeTriangleP2 { + + public: + // the reference element + using reference_element_type = geometry::reference_triangle_t; + // the number of shape functions + static constexpr int N = 6; + + private: + // get the parametric coordinates from the reference element + static constexpr auto xi_0 = reference_element_type::xi<0>; + static constexpr auto xi_1 = reference_element_type::xi<1>; + static constexpr auto xi_2 = 1.0 - xi_0 - xi_1; + + // quadratic shape functions on the triangle + static constexpr auto phi_3 = 4.0 * xi_0 * xi_1; + static constexpr auto phi_4 = 4.0 * xi_1 * xi_2; + static constexpr auto phi_5 = 4.0 * xi_0 * xi_2; + static constexpr auto phi_0 = xi_0 - 0.5 * phi_5 - 0.5 * phi_3; + static constexpr auto phi_1 = xi_1 - 0.5 * phi_3 - 0.5 * phi_4; + static constexpr auto phi_2 = xi_2 - 0.5 * phi_5 - 0.5 * phi_4; + + // the shape functions + static constexpr auto phi = std::make_tuple(phi_0, phi_1, phi_2, phi_3, phi_4, phi_5); + + // the gradients of the shape functions + static constexpr auto dphi = std::make_tuple( + fields::gradient(phi_0), fields::gradient(phi_1), fields::gradient(phi_2), + fields::gradient(phi_3), fields::gradient(phi_4), fields::gradient(phi_5)); + + public: + // get the a-th shape function as a function of parametric coordinates + template + requires(a >= 0 && a < N) + constexpr auto shape() const + { + // return the a-th shape function + return std::get(phi); + } + + // get the a-th shape function's gradient as a function of parametric coordinates + template + requires(a >= 0 && a < N) + constexpr auto dshape() const + { + // return the a-th shape function's gradient + return std::get(dphi); + } + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/fem/elements/tri2/api.h b/lib/mito/fem/elements/tri2/api.h new file mode 100644 index 000000000..4edff71ce --- /dev/null +++ b/lib/mito/fem/elements/tri2/api.h @@ -0,0 +1,21 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + // specialization for quadratic shape functions on triangles in 2D + template <> + struct isoparametric_simplex<2, geometry::triangle_t<2>> { + using type = IsoparametricTriangleP2; + }; + +} + + +// end of file diff --git a/lib/mito/fem/elements/tri2/public.h b/lib/mito/fem/elements/tri2/public.h new file mode 100644 index 000000000..f0d81b37d --- /dev/null +++ b/lib/mito/fem/elements/tri2/public.h @@ -0,0 +1,19 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// classes implementation +#include "ShapeTriangleP2.h" +#include "IsoparametricTriangleP2.h" +#include "DiscretizerCG.h" + +// published types and factories +#include "api.h" + + +// end of file diff --git a/lib/mito/fem/elements/utilities.h b/lib/mito/fem/elements/utilities.h new file mode 100644 index 000000000..5812d04ed --- /dev/null +++ b/lib/mito/fem/elements/utilities.h @@ -0,0 +1,30 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + // populate a container with a collection of all nodes in a function space + template + inline auto get_discretization_nodes( + const functionSpaceT & function_space, nodesCollectionT & nodes) -> void + { + for (const auto & element : function_space.elements()) { + for (const auto & node : element.connectivity()) { + nodes.insert(node); + } + } + + // all done + return; + } + +} + + +// end of file diff --git a/lib/mito/fem/externals.h b/lib/mito/fem/externals.h new file mode 100644 index 000000000..cda32c32c --- /dev/null +++ b/lib/mito/fem/externals.h @@ -0,0 +1,21 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// externals +#include + +// support +#include "../journal.h" +#include "../manifolds.h" +#include "../constraints.h" +#include "../discrete.h" +#include "../utilities.h" + + +// end of file diff --git a/lib/mito/fem/factories.h b/lib/mito/fem/factories.h new file mode 100644 index 000000000..6148588a8 --- /dev/null +++ b/lib/mito/fem/factories.h @@ -0,0 +1,60 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + // nodal field factory + template + constexpr auto nodal_field(const functionSpaceT & function_space, std::string name) + { + // assemble the node type + using node_type = functionSpaceT::discretization_node_type; + + // get the nodes in the mesh + std::unordered_set> nodes; + get_discretization_nodes(function_space, nodes); + + // build a nodal field on the discretization nodes collected from the function space + return discrete::nodal_field_t(nodes, name); + } + + // TOFIX: create a constructor that takes no constraints + + // TOFIX: {constraints} should be a collection of constraints as opposed to an instance of a + // single constraint + // function space factory + template < + class elementT, manifolds::manifold_c manifoldT, constraints::constraint_c constraintsT> + // require compatibility between the manifold cell and the finite element cell + requires(std::is_same_v) + constexpr auto function_space(const manifoldT & manifold, const constraintsT & constraints) + { + // build a function space on the manifold and return it + return function_space_t(manifold, constraints); + } + + // weakform factory + template + constexpr auto weakform() + { + return weakform_t(); + } + + // discrete system factory + template + constexpr auto discrete_system( + const std::string & label, const functionSpaceT & function_space, + const weakformT & weakform) + { + return discrete_system_t(label, function_space, weakform); + } +} + + +// end of file diff --git a/lib/mito/fem/forward.h b/lib/mito/fem/forward.h new file mode 100644 index 000000000..d212e8f4a --- /dev/null +++ b/lib/mito/fem/forward.h @@ -0,0 +1,35 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::fem { + + // class function space + template + class FunctionSpace; + + // concept of a function space + template + concept function_space_c = requires(F c) { + // require that F only binds to {FunctionSpace} specializations + []( + const FunctionSpace &) { + }(c); + }; + + // weakform alias + template + class Weakform; + + // class discrete system + template + class DiscreteSystem; +} + + +// end of file diff --git a/lib/mito/fem/public.h b/lib/mito/fem/public.h new file mode 100644 index 000000000..1ce8000ab --- /dev/null +++ b/lib/mito/fem/public.h @@ -0,0 +1,33 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// external packages +#include "externals.h" + +// get the forward declarations +#include "forward.h" + +// published types factories; this is the file you are looking for... +#include "api.h" + +// blocks implementation +#include "blocks.h" + +// classes implementation +#include "FunctionSpace.h" +#include "DiscreteSystem.h" +#include "Weakform.h" + +// finite elements implementation +#include "elements.h" + +// factories implementation +#include "factories.h" + +// end of file diff --git a/lib/mito/fields/differential.h b/lib/mito/fields/differential.h index 92788518d..e18646475 100644 --- a/lib/mito/fields/differential.h +++ b/lib/mito/fields/differential.h @@ -36,6 +36,37 @@ namespace mito::fields { return _grad(field, std::make_index_sequence{}); } + // function to compute the gradient of a vector field + template + constexpr auto gradient(const F & field) + { + // the type of coordinate + using coordinate_t = F::coordinates_type; + // the spatial dimension of the field + constexpr int D = coordinate_t::dim; + // the number of components of the vector field + constexpr int N = F::output_type::size; + + // helper function to compute the gradient of a scalar + constexpr auto _grad = [](const F & field, std::index_sequence) { + constexpr auto _grad_with_respect_to = []( + const F & field, std::index_sequence) { + // the tensor of the partial derivatives + // (\partial field_I / \partial x_K ) * e_IK, I = 0, ..., N-1, K = 0, ..., D-1 + return ( + (derivative(field * uniform_field(tensor::e)) + * uniform_field(tensor::unit, I, K>)) + + ...); + }; + + return ( + (_grad_with_respect_to.template operator()(field, std::make_index_sequence{})) + + ...); + }; + + return _grad(field, std::make_index_sequence{}); + } + // function to compute the divergence of a vector field template constexpr auto divergence(const F & field) @@ -54,6 +85,37 @@ namespace mito::fields { // all done return _div(field, std::make_index_sequence{}); } + + // function to compute the divergence of a tensor field + template + constexpr auto divergence(const F & field) + { + // the type of coordinate + using coordinate_t = F::coordinates_type; + // the spatial dimension of the field + constexpr int D = coordinate_t::dim; + + // helper function to compute the gradient of a scalar + constexpr auto _div = [](const F & field, std::index_sequence) { + constexpr auto _div_with_respect_to = []( + const F & field, std::index_sequence) { + // the vector of the partial derivatives + // (\partial field_KI / \partial x_I) * e_K + return ( + (derivative( + (field * uniform_field(tensor::e)) + * uniform_field(tensor::e)) + * uniform_field(tensor::e)) + + ...); + }; + + return ( + (_div_with_respect_to.template operator()(field, std::make_index_sequence{})) + + ...); + }; + + return _div(field, std::make_index_sequence{}); + } } diff --git a/lib/mito/fields/fields_algebra.h b/lib/mito/fields/fields_algebra.h index 282f95718..b9c1dece7 100644 --- a/lib/mito/fields/fields_algebra.h +++ b/lib/mito/fields/fields_algebra.h @@ -29,6 +29,13 @@ namespace mito::fields { })); } + // f transpose + template + constexpr auto transpose(const F & f) + { + return field(functions::transpose(f.function())); + } + // sqrt(f) template constexpr auto sqrt(const F & f) diff --git a/lib/mito/fields/fields_arithmetic.h b/lib/mito/fields/fields_arithmetic.h index 73a2c5216..01a73d102 100644 --- a/lib/mito/fields/fields_arithmetic.h +++ b/lib/mito/fields/fields_arithmetic.h @@ -10,6 +10,14 @@ // Arithmetic operations on Fields namespace mito::fields { + // addition of N fields fa + ... + fn + template + constexpr auto linear_combination(std::array coeffs, const Fs &... fs) + requires(compatible_fields_c) + { + return field(functions::linear_combination(coeffs, fs.function()...)); + } + // addition of fields fa + fb template constexpr auto operator+(const F1 & fA, const F2 & fB) diff --git a/lib/mito/fields/fields_functions_arithmetic.h b/lib/mito/fields/fields_functions_arithmetic.h index 50b7e6dea..bb479e484 100644 --- a/lib/mito/fields/fields_functions_arithmetic.h +++ b/lib/mito/fields/fields_functions_arithmetic.h @@ -51,7 +51,7 @@ namespace mito::fields { } // field1 / field2 - template + template constexpr auto operator/(const F1 & f1, const F2 & f2) { return field(f1.function() / f2); diff --git a/lib/mito/fields/forward.h b/lib/mito/fields/forward.h index 735e20721..09749055d 100644 --- a/lib/mito/fields/forward.h +++ b/lib/mito/fields/forward.h @@ -60,10 +60,13 @@ namespace mito::fields { concept p_form_field_c = field_c and tensor::p_form_c; // concept of two fields being compatible with each other (i.e. defined on the same coordinates) - template - // a scalar field is a field returning a scalar + template concept compatible_fields_c = - std::is_same_v; + (sizeof...(FIELDS) <= 1) || // trivially true for 0 or 1 + (std::same_as< + typename std::tuple_element_t<0, std::tuple>::coordinates_type, + typename FIELDS::coordinates_type> + && ...); } diff --git a/lib/mito/functions/algebra.h b/lib/mito/functions/algebra.h index e4da29a64..2bc6c72ab 100644 --- a/lib/mito/functions/algebra.h +++ b/lib/mito/functions/algebra.h @@ -15,7 +15,7 @@ namespace mito::functions { // f1 + f2 constexpr auto operator+(const function_c auto & f1, const function_c auto & f2) { - return Sum(f1, f2); + return Summation(f1, f2); } // a + f @@ -33,13 +33,13 @@ namespace mito::functions { // a * f constexpr auto operator*(const tensor::tensor_or_scalar_c auto & a, const function_c auto & f) { - return FunctionTimesConstant(f, a); + return ConstantTimesFunction(a, f); } // f * a constexpr auto operator*(const function_c auto & f, const tensor::tensor_or_scalar_c auto & a) { - return a * f; + return FunctionTimesConstant(f, a); } // f / a @@ -80,16 +80,35 @@ namespace mito::functions { // a / f constexpr auto operator/( - const tensor::tensor_or_scalar_c auto & a, const scalar_function_c auto & f) + const tensor::tensor_or_scalar_c auto & a, const scalar_valued_function_c auto & f) { return a * Reciprocal(f); } // f1 / f2 - constexpr auto operator/(const function_c auto & f1, const scalar_function_c auto & f2) + constexpr auto operator/(const function_c auto & f1, const scalar_valued_function_c auto & f2) { return f1 * Reciprocal(f2); } + + // f transposed + constexpr auto transpose(const function_c auto & f) + { + return Transpose(f); + } + + // f inverse + constexpr auto inverse(const function_c auto & f) + { + return Inverse(f); + } + + // dyadic product of vector-valued functions f and g + constexpr auto dyadic( + const vector_valued_function_c auto & f, const vector_valued_function_c auto & g) + { + return DyadicProduct(f, g); + } } diff --git a/lib/mito/functions/api.h b/lib/mito/functions/api.h index b19052eed..bb0162b8d 100644 --- a/lib/mito/functions/api.h +++ b/lib/mito/functions/api.h @@ -71,6 +71,13 @@ namespace mito::functions { template [[maybe_unused]] constexpr auto x = component, N>; + // the linear combination + template + constexpr auto linear_combination(std::array coeffs, Funcs... funcs); + + // returns the summation of functions + template + constexpr auto summation(Funcs... funcs); } diff --git a/lib/mito/functions/derivation_rules.h b/lib/mito/functions/derivation_rules.h index 46cbb9a4a..eee2e8025 100644 --- a/lib/mito/functions/derivation_rules.h +++ b/lib/mito/functions/derivation_rules.h @@ -12,53 +12,104 @@ namespace mito::functions { - // the {I...}-th first partial derivative of a function sum - template - constexpr auto derivative(const Sum & f) + // the I-th first partial derivative of a constant + template + constexpr auto derivative(const T &) { - return derivative(f.f1()) + derivative(f.f2()); + return tensor::zero; } - // the {I...}-th first partial derivative of a constant plus a function - template + // the I-th first partial derivative of a function sum + template + constexpr auto derivative(const Summation & f) + { + return std::apply( + [&](const auto &... funcs) { return (derivative(funcs) + ...); }, f.functions()); + } + + // the I-th first partial derivative of a constant plus a function + template constexpr auto derivative(const FunctionPlusConstant & f) { - return derivative(f.f()); + return derivative(f.f()); } - // the {I...}-th first partial derivative of a function times a constant - template + // the I-th first partial derivative of a function times a constant + template constexpr auto derivative(const FunctionTimesConstant & f) { - return derivative(f.f()) * f.constant(); + return derivative(f.f()) * f.constant(); } - // the {I...}-th first partial derivative of a constant times a function - template + // the I-th first partial derivative of a constant times a function + template constexpr auto derivative(const ConstantTimesFunction & f) { - return f.constant() * derivative(f.f()); + return f.constant() * derivative(f.f()); } - // the {I...}-th first partial derivative of a product of functions - template + // the I-th first partial derivative of a product of functions + template constexpr auto derivative(const Product & f) { - return f.f1() * derivative(f.f2()) + derivative(f.f1()) * f.f2(); + return f.f1() * derivative(f.f2()) + derivative(f.f1()) * f.f2(); } - // the {I...}-th first partial derivative of a product of the reciprocal function - template + // the I-th first partial derivative of a product of the reciprocal function + template constexpr auto derivative(const Reciprocal & f) { - return -1.0 * derivative(f.f()) / (f.f() * f.f()); + return -1.0 * derivative(f.f()) / (f.f() * f.f()); } - // the chain rule for the {I...}-th first partial derivative - template + // the chain rule for the I-th first partial derivative (f1(f2(x)) with f2 scalar valued) + template + requires(scalar_valued_function_c) constexpr auto derivative(const Composition & f) { - return derivative(f.f1())(f.f2()) * derivative(f.f2()); + return derivative(f.f1())(f.f2()) * derivative(f.f2()); + } + + // the chain rule for the I-th first partial derivative (f1(f2(x)) with f2 tensor function) + template + requires(tensor_function_c) + constexpr auto derivative(const Composition & f) + { + // the number of components of the vector (output of F2) + constexpr int N = F2::output_type::size; + + // helper function to compute the N partial derivatives + constexpr auto _derivative = [](const auto & f, std::index_sequence) { + // the vector of the partial derivatives + return ((derivative(f.f1())(f.f2()) * derivative(f.f2()[J])) + ...); + }; + + return _derivative(f, std::make_index_sequence{}); + } + + // the derivative of the transpose of a function + template + constexpr auto derivative(const Transpose & f) + { + // the derivative of the transpose is the transpose of the derivative + return functions::transpose(derivative(f.f())); + } + + // the I-th first partial derivative of the {Subscript} function + template + constexpr auto derivative(const Subscript & f) + { + // the derivative of the subscript is the subscript of the derivative + return Subscript(derivative(f.f()), f.index()); + } + + // the I-th first partial derivative of a linear combination + template + constexpr auto derivative(const LinearCombination & f) + { + // the derivative of a linear combination is the linear combination of the derivatives + return linear_combination( + f.coefficients(), derivative(std::get(f.functions()))...); } } diff --git a/lib/mito/functions/factories.h b/lib/mito/functions/factories.h index d60b332d7..c17c358f7 100644 --- a/lib/mito/functions/factories.h +++ b/lib/mito/functions/factories.h @@ -22,6 +22,20 @@ namespace mito::functions { { return FunctionFromFunctor(std::forward(f)); } + + // returns the summation of functions + template + constexpr auto summation(Funcs... funcs) + { + return Summation(std::forward(funcs)...); + } + + // returns the linear combination + template + constexpr auto linear_combination(std::array coeffs, Funcs... funcs) + { + return LinearCombination(coeffs, std::forward(funcs)...); + } } diff --git a/lib/mito/functions/forward.h b/lib/mito/functions/forward.h index 83b68bd8f..8a47519d2 100644 --- a/lib/mito/functions/forward.h +++ b/lib/mito/functions/forward.h @@ -9,10 +9,6 @@ namespace mito::functions { - // the functor concept - template - concept functor_c = requires { &F::operator(); }; - // class {Function} template class Function; @@ -25,14 +21,71 @@ namespace mito::functions { }(c); }; + // the functor concept + template + concept functor_c = requires { &F::operator(); } and not function_c; + + // concept of a function defined on scalars + template + concept scalar_domain_function_c = function_c and tensor::scalar_c; + // concept of a scalar-valued function template - concept scalar_function_c = function_c and tensor::scalar_c; + concept scalar_valued_function_c = function_c and tensor::scalar_c; + + // concept of a scalar-valued function of scalars + template + concept scalar_function_c = scalar_domain_function_c and scalar_valued_function_c; + + // concept of a function defined on vectors + template + concept vector_domain_function_c = function_c and tensor::vector_c; + + // concept of a vector-valued function + template + concept vector_valued_function_c = function_c and tensor::vector_c; + + // concept of a vector-valued function of vectors + template + concept vector_function_c = vector_domain_function_c and vector_valued_function_c; + + // concept of a function defined on tensors + template + concept tensor_domain_function_c = function_c and tensor::tensor_c; + + // concept of a tensor-valued function + template + concept tensor_valued_function_c = function_c and tensor::tensor_c; + + // concept of a tensor-valued function of tensors + template + concept tensor_function_c = tensor_domain_function_c and tensor_valued_function_c; + + // concept of a subscriptable type + template + concept subscriptable_c = requires(F f, int i) { + { f[i] }; + }; + + // concept of a subscriptable function + template + concept subscriptable_function_c = function_c and subscriptable_c; + + // concept of functions taking the same input type + template + concept same_input_c = (sizeof...(Funcs) <= 1) || // trivially true for 0 or 1 + (std::same_as< + typename std::tuple_element_t<0, std::tuple>::input_type, + typename Funcs::input_type> + && ...); // function composition template class Composition; + // function subscript + template + class Subscript; } diff --git a/lib/mito/functions/function.h b/lib/mito/functions/function.h index 982d28df5..0f7e58c86 100644 --- a/lib/mito/functions/function.h +++ b/lib/mito/functions/function.h @@ -43,6 +43,20 @@ namespace mito::functions { return Composition(*this, f); } + // subscript operator: only available if {output_type} is subscriptable + constexpr auto operator[](int i) const + requires subscriptable_c + { + return Subscript(*this, i); + } + + // subscript operator with multi-index: only available if {output_type} is a tensor + template + constexpr auto operator[](const typename outputT::index_t & i) const + { + return Subscript(*this, i); + } + // call operator constexpr auto operator()(const input_type & x) const -> output_type { return _f(x); } @@ -56,10 +70,12 @@ namespace mito::functions { template class Constant : public Function { public: + // the type of function (what I derive from) + using function_type = Function; // the input type - using input_type = Function::input_type; + using input_type = function_type::input_type; // the output type - using output_type = Function::output_type; + using output_type = function_type::output_type; public: // constructor @@ -75,6 +91,20 @@ namespace mito::functions { return Composition(*this, f); } + // subscript operator: only available if {output_type} is subscriptable + constexpr auto operator[](int i) const + requires subscriptable_c + { + return Subscript(*this, i); + } + + // subscript operator with multi-index: only available if {output_type} is a tensor + template + constexpr auto operator[](const typename outputT::index_t & i) const + { + return Subscript(*this, i); + } + // call operator constexpr auto operator()(const input_type &) const -> output_type { return _c; } @@ -85,19 +115,23 @@ namespace mito::functions { // the sum of two functions - template - class Sum : public function_sum::type { + template + class Summation : public function_sum::type { public: // the type of sum function (what I derive from) - using sum_type = function_sum::type; + using function_type = function_sum::type; // the input type of the sum - using input_type = sum_type::input_type; + using input_type = function_type::input_type; // the output type of the sum - using output_type = sum_type::output_type; + using output_type = function_type::output_type; + + private: + // the type of functions + using functions_type = std::tuple; public: // constructor - constexpr Sum(const F & f, const G & g) : _f(f), _g(g) {} + constexpr Summation(const Funcs &... funcs) : _funcs(funcs...) {} // call operator for function composition template @@ -106,23 +140,107 @@ namespace mito::functions { return Composition(*this, f); } + // subscript operator: only available if {output_type} is subscriptable + constexpr auto operator[](int i) const + requires subscriptable_c + { + return Subscript(*this, i); + } + + // subscript operator with multi-index: only available if {output_type} is a tensor + template + constexpr auto operator[](const typename outputT::index_t & i) const + { + return Subscript(*this, i); + } + // call operator constexpr auto operator()(const input_type & x) const -> output_type { - return _f(x) + _g(x); + // the sum of all functions + return std::apply([&](const auto &... funcs) { return (funcs(x) + ...); }, _funcs); } - // the first in the sum - constexpr auto f1() const -> const F & { return _f; } - - // the second in the sum - constexpr auto f2() const -> const G & { return _g; } + // accessor for the functions in the summation + constexpr auto functions() const -> const functions_type & { return _funcs; } private: - const F _f; - const G _g; + // the functions in the summation + functions_type _funcs; }; + // the linear combination of functions {Funcs} with coefficients of type {T}... + template + class LinearCombination : public function_linear_combination::type { + public: + // the type of the function (what I derive from) + using function_type = function_linear_combination::type; + // the input type + using input_type = function_type::input_type; + // the output type + using output_type = function_type::output_type; + + private: + // the number of functions in the linear combination + static constexpr std::size_t N = sizeof...(Funcs); + // the type of coefficients + using coefficients_type = std::array; + // the type of functions + using functions_type = std::tuple; + + public: + // constructor + constexpr LinearCombination(const coefficients_type & coeffs, const Funcs &... funcs) : + _coeffs(coeffs), + _funcs(funcs...) + {} + + // call operator for function composition + template + constexpr auto operator()(const H & f) const + { + return Composition(*this, f); + } + + // subscript operator: only available if {output_type} is subscriptable + constexpr auto operator[](int i) const + requires subscriptable_c + { + return Subscript(*this, i); + } + + // subscript operator with multi-index: only available if {output_type} is a tensor + template + constexpr auto operator[](const typename outputT::index_t & i) const + { + return Subscript(*this, i); + } + + // call operator + constexpr auto operator()(const input_type & x) const -> output_type + { + // helper function to assemble the linear combination + auto eval = [&](const input_type & x, std::index_sequence) { + return ((_coeffs[Is] * std::get(_funcs)(x)) + ...); + }; + + // return the result of the linear combination + return eval(x, std::make_index_sequence{}); + } + + public: + // accessor for the coefficients + constexpr auto coefficients() const -> const coefficients_type & { return _coeffs; } + + // accessor for the functions + constexpr auto functions() const -> const functions_type & { return _funcs; } + + private: + // the coefficients in the linear combination + coefficients_type _coeffs; + // the functions in the linear combination + functions_type _funcs; + }; // the sum of a function with a constant template @@ -130,11 +248,11 @@ namespace mito::functions { public: // the type of function (what I derive from) - using my_function_type = function_type; + using function_type = functions::function_type; // the input type of the sum - using input_type = my_function_type::input_type; + using input_type = function_type::input_type; // the output type of the sum - using output_type = my_function_type::output_type; + using output_type = function_type::output_type; public: // constructor @@ -150,6 +268,20 @@ namespace mito::functions { return Composition(*this, f); } + // subscript operator: only available if {output_type} is subscriptable + constexpr auto operator[](int i) const + requires subscriptable_c + { + return Subscript(*this, i); + } + + // subscript operator with multi-index: only available if {output_type} is a tensor + template + constexpr auto operator[](const typename outputT::index_t & i) const + { + return Subscript(*this, i); + } + // call operator constexpr auto operator()(const input_type & x) const -> output_type { return _f(x) + _a; } @@ -169,11 +301,11 @@ namespace mito::functions { class Product : public function_product::type { public: // the type of product function (what I derive from) - using product_type = function_product::type; + using function_type = function_product::type; // the input type of the sum - using input_type = product_type::input_type; + using input_type = function_type::input_type; // the output type of the sum - using output_type = product_type::output_type; + using output_type = function_type::output_type; public: // constructor @@ -186,6 +318,20 @@ namespace mito::functions { return Composition(*this, f); } + // subscript operator: only available if {output_type} is subscriptable + constexpr auto operator[](int i) const + requires subscriptable_c + { + return Subscript(*this, i); + } + + // subscript operator with multi-index: only available if {output_type} is a tensor + template + constexpr auto operator[](const typename outputT::index_t & i) const + { + return Subscript(*this, i); + } + // call operator constexpr auto operator()(const input_type & x) const -> output_type { @@ -211,11 +357,11 @@ namespace mito::functions { public: // the type of function (what I derive from) - using my_function_type = function_product>::type; + using function_type = function_product>::type; // the input type of the sum - using input_type = my_function_type::input_type; + using input_type = function_type::input_type; // the output type of the sum - using output_type = my_function_type::output_type; + using output_type = function_type::output_type; public: // constructor @@ -231,6 +377,20 @@ namespace mito::functions { return Composition(*this, f); } + // subscript operator: only available if {output_type} is subscriptable + constexpr auto operator[](int i) const + requires subscriptable_c + { + return Subscript(*this, i); + } + + // subscript operator with multi-index: only available if {output_type} is a tensor + template + constexpr auto operator[](const typename outputT::index_t & i) const + { + return Subscript(*this, i); + } + // call operator constexpr auto operator()(const input_type & x) const -> output_type { return _f(x) * _a; } @@ -252,11 +412,11 @@ namespace mito::functions { public: // the type of function (what I derive from) - using my_function_type = function_product, F>::type; + using function_type = function_product, F>::type; // the input type of the sum - using input_type = my_function_type::input_type; + using input_type = function_type::input_type; // the output type of the sum - using output_type = my_function_type::output_type; + using output_type = function_type::output_type; public: // constructor @@ -272,6 +432,20 @@ namespace mito::functions { return Composition(*this, f); } + // subscript operator: only available if {output_type} is subscriptable + constexpr auto operator[](int i) const + requires subscriptable_c + { + return Subscript(*this, i); + } + + // subscript operator with multi-index: only available if {output_type} is a tensor + template + constexpr auto operator[](const typename outputT::index_t & i) const + { + return Subscript(*this, i); + } + // call operator constexpr auto operator()(const input_type & x) const -> output_type { return _a * _f(x); } @@ -287,16 +461,16 @@ namespace mito::functions { // the reciprocal of a scalar function - template + template class Reciprocal : public function_type { public: // the type of function (what I derive from) - using my_function_type = function_type; + using function_type = functions::function_type; // the input type of the sum - using input_type = my_function_type::input_type; + using input_type = function_type::input_type; // the output type of the sum - using output_type = my_function_type::output_type; + using output_type = function_type::output_type; public: // constructor @@ -326,11 +500,11 @@ namespace mito::functions { public: // the type of composition function (what I derive from) - using composition_type = function_composition::type; + using function_type = function_composition::type; // the input type of the composition - using input_type = composition_type::input_type; + using input_type = function_type::input_type; // the output type of the composition - using output_type = composition_type::output_type; + using output_type = function_type::output_type; public: // constructor @@ -343,6 +517,20 @@ namespace mito::functions { return Composition(*this, f); } + // subscript operator: only available if {output_type} is subscriptable + constexpr auto operator[](int i) const + requires subscriptable_c + { + return Subscript(*this, i); + } + + // subscript operator with multi-index: only available if {output_type} is a tensor + template + constexpr auto operator[](const typename outputT::index_t & i) const + { + return Subscript(*this, i); + } + // call operator constexpr auto operator()(const input_type & x) const -> output_type { return _f(_g(x)); } @@ -356,6 +544,202 @@ namespace mito::functions { const F _f; const G _g; }; + + + template + class Subscript : public function_subscript::type { + + public: + // the type of the function (what I derive from) + using function_type = function_subscript::type; + // the input type + using input_type = function_type::input_type; + // the output type + using output_type = function_type::output_type; + // the type of index + using index_type = indexT; + + public: + // constructor + constexpr Subscript(const F & f, const index_type i) : _f(f), _i(i) {} + + // call operator for function composition + template + constexpr auto operator()(const H & f) const + { + return Composition(*this, f); + } + + // call operator + constexpr auto operator()(const input_type & x) const -> output_type { return _f(x)[_i]; } + + // the function to subscript + constexpr auto f() const -> const F & { return _f; } + + // the index + constexpr auto index() const -> index_type { return _i; } + + private: + const F _f; + const index_type _i; + }; + + + // function transposing a function of a second order tensor + template + requires(tensor::matrix_c) + class Transpose : public function_transpose::type { + + public: + // the type of the function (what I derive from) + using function_type = function_transpose::type; + // the input type + using input_type = function_type::input_type; + // the output type + using output_type = function_type::output_type; + + public: + // constructor + constexpr Transpose(const F & f) : _f(f) {} + + // call operator for function composition + template + constexpr auto operator()(const H & f) const + { + return Composition(*this, f); + } + + // subscript operator: only available if {output_type} is subscriptable + constexpr auto operator[](int i) const + requires subscriptable_c + { + return Subscript(*this, i); + } + + // subscript operator with multi-index: only available if {output_type} is a tensor + template + constexpr auto operator[](const typename outputT::index_t & i) const + { + return Subscript(*this, i); + } + + // call operator + constexpr auto operator()(const input_type & x) const -> output_type + { + return tensor::transpose(_f(x)); + } + + // the function to transpose + constexpr auto f() const -> const F & { return _f; } + + private: + const F _f; + }; + + // function computing the inverse of a second order tensor + template + requires(tensor::square_matrix_c) + class Inverse : public Function { + + public: + // the type of the function (what I derive from) + using function_type = Function; + // the input type + using input_type = function_type::input_type; + // the output type + using output_type = function_type::output_type; + + public: + // constructor + constexpr Inverse(const F & f) : _f(f) {} + + // call operator for function composition + template + constexpr auto operator()(const H & f) const + { + return Composition(*this, f); + } + + // subscript operator: only available if {output_type} is subscriptable + constexpr auto operator[](int i) const + requires subscriptable_c + { + return Subscript(*this, i); + } + + // subscript operator with multi-index: only available if {output_type} is a tensor + template + constexpr auto operator[](const typename outputT::index_t & i) const + { + return Subscript(*this, i); + } + + // call operator + constexpr auto operator()(const input_type & x) const -> output_type + { + return tensor::inverse(_f(x)); + } + + // the matrix function to invert + constexpr auto f() const -> const F & { return _f; } + + private: + const F _f; + }; + + // function computing the dyadic of two vector valued functions + template + class DyadicProduct : public function_dyadic_product::type { + + public: + // the type of the function (what I derive from) + using function_type = function_dyadic_product::type; + // the input type + using input_type = function_type::input_type; + // the output type + using output_type = function_type::output_type; + + public: + // constructor + constexpr DyadicProduct(const F1 & f1, const F2 & f2) : _f1(f1), _f2(f2) {} + + // call operator for function composition + template + constexpr auto operator()(const H & f) const + { + return Composition(*this, f); + } + + // subscript operator: only available if {output_type} is subscriptable + constexpr auto operator[](int i) const + requires subscriptable_c + { + return Subscript(*this, i); + } + + // subscript operator with multi-index: only available if {output_type} is a tensor + template + constexpr auto operator[](const typename outputT::index_t & i) const + { + return Subscript(*this, i); + } + + // call operator + constexpr auto operator()(const input_type & x) const -> output_type + { + return tensor::dyadic(_f1(x), _f2(x)); + } + + // the first function in the dyadic product + constexpr auto f1() const -> const F1 & { return _f1; } + + // the second function in the dyadic product + constexpr auto f2() const -> const F2 & { return _f2; } + + private: + const F1 _f1; + const F2 _f2; + }; } diff --git a/lib/mito/functions/library_derivatives.h b/lib/mito/functions/library_derivatives.h index 83265077a..f25066e8f 100644 --- a/lib/mito/functions/library_derivatives.h +++ b/lib/mito/functions/library_derivatives.h @@ -9,104 +9,104 @@ namespace mito::functions { - // the {I...}-th first partial derivative of the {Constant} function - template + // the I-th first partial derivative of the {Constant} function + template constexpr auto derivative(const Constant &) { // the zero constant return Constant({}); } - // the {I...}-th first partial derivative of the {Power} function - template + // the I-th first partial derivative of the {Power} function + template constexpr auto derivative(const Power &) { return N * pow; } - // the {I...}-th first partial derivative of the {Power<1>} function - template + // the I-th first partial derivative of the {Power<1>} function + template constexpr auto derivative(const Power<1> &) { return one; } - // the {I...}-th first partial derivative of the {Sin} function - template + // the I-th first partial derivative of the {Sin} function + template constexpr auto derivative(const Sin &) { return cos; } - // the {I...}-th first partial derivative of the {Cos} function - template + // the I-th first partial derivative of the {Cos} function + template constexpr auto derivative(const Cos &) { return -sin; } - // the {I...}-th first partial derivative of the {Tan} function - template + // the I-th first partial derivative of the {Tan} function + template constexpr auto derivative(const Tan &) { return 1.0 / pow<2>(cos); } - // the {I...}-th first partial derivative of the {ArcCos} function - template + // the I-th first partial derivative of the {ArcCos} function + template constexpr auto derivative(const ArcCos &) { return -1.0 / sqrt(1.0 - pow<2>); } - // the {I...}-th first partial derivative of the {ArcSin} function - template + // the I-th first partial derivative of the {ArcSin} function + template constexpr auto derivative(const ArcSin &) { return 1.0 / sqrt(1.0 - pow<2>); } - // the {I...}-th first partial derivative of the {ArcTan} function - template + // the I-th first partial derivative of the {ArcTan} function + template constexpr auto derivative(const ArcTan &) { return 1.0 / (1.0 + pow<2>); } - // the {I...}-th first partial derivative of the {Exp} function - template + // the I-th first partial derivative of the {Exp} function + template constexpr auto derivative(const Exp &) { return exp; } - // the {I...}-th first partial derivative of the {Log} function - template + // the I-th first partial derivative of the {Log} function + template constexpr auto derivative(const Log &) { return 1.0 / pow<1>; } - // the {I...}-th first partial derivative of the {Sqrt} function - template + // the I-th first partial derivative of the {Sqrt} function + template constexpr auto derivative(const Sqrt &) { return 0.5 / sqrt; } - // the {I...}-th first partial derivative of the {Cbrt} function - template + // the I-th first partial derivative of the {Cbrt} function + template constexpr auto derivative(const Cbrt &) { return (1.0 / 3.0) / pow<2>(cbrt); } - // the {I...}-th first partial derivative of the {Component} function - template - constexpr auto derivative(const Component &) + // the I-th first partial derivative of the {Component} function + template + constexpr auto derivative(const Component &) { // the dirac delta - if constexpr (std::make_tuple(I...) == std::make_tuple(N...)) + if constexpr (I == N) return one; else return zero; diff --git a/lib/mito/functions/library_functions.h b/lib/mito/functions/library_functions.h index 9622e8be3..bd92f4a2a 100644 --- a/lib/mito/functions/library_functions.h +++ b/lib/mito/functions/library_functions.h @@ -12,8 +12,8 @@ namespace mito::functions { - // function extracting the {I...} component of a tensor - template + // function extracting the I-th component of a tensor + template class Component : public Function { public: @@ -31,7 +31,7 @@ namespace mito::functions { } // call operator - constexpr auto operator()(const input_type & x) const -> output_type { return x[{ I... }]; } + constexpr auto operator()(const input_type & x) const -> output_type { return x[I]; } }; diff --git a/lib/mito/functions/partial_derivatives.h b/lib/mito/functions/partial_derivatives.h new file mode 100644 index 000000000..c935a3444 --- /dev/null +++ b/lib/mito/functions/partial_derivatives.h @@ -0,0 +1,94 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// Partial derivatives + + +namespace mito::functions { + + template + constexpr auto derivative(const F & f) + { + return derivative<0>(f); + } + + namespace { + // implementation of the assembly of the matrix of partial derivatives + // (row: output index; column: input index) + template + constexpr auto derivative_impl(const F & f) + { + // the output matrix type + using output_matrix = tensor::matrix_t; + + // helper function to compute the derivative + constexpr auto _derivative = [](const F & f, std::index_sequence) { + constexpr auto _derivative_with_respect_to = + [](const F & f, std::index_sequence) { + // the matrix of the partial derivatives + // (\partial field_I / \partial x_K ) * e_IK, I = 0, ..., N-1, K = 0, ..., + // D-1 + if constexpr (N == 1) { + return ((derivative(f) * tensor::unit) +...); + } else { + return ((derivative(f[I]) * tensor::unit) +...); + } + }; + + return ( + (_derivative_with_respect_to.template operator()( + f, std::make_index_sequence{})) + + ...); + }; + + return _derivative(f, std::make_index_sequence{}); + } + } + + // the derivative of a tensor-valued function of tensors + template + constexpr auto derivative(const F & f) + { + // the number of components of the input vector of F + constexpr int D = F::input_type::size; + // the number of components of the output vector of F + constexpr int N = F::output_type::size; + // call the implementation + return derivative_impl(f); + } + + // the derivative of a scalar-valued function of tensors + template + requires(tensor_domain_function_c and scalar_valued_function_c) + constexpr auto derivative(const F & f) + { + // the number of components of the input vector of F + constexpr int D = F::input_type::size; + // the number of components of the output vector of F + constexpr int N = 1; + // call the implementation + return derivative_impl(f); + } + + // the derivative of a tensor-valued function of scalars + template + requires(scalar_domain_function_c and tensor_valued_function_c) + constexpr auto derivative(const F & f) + { + // the number of components of the input vector of F + constexpr int D = 1; + // the number of components of the output vector of F + constexpr int N = F::output_type::size; + // call the implementation + return derivative_impl(f); + } +} + + +// end of file diff --git a/lib/mito/functions/public.h b/lib/mito/functions/public.h index 681fc99b0..0ff689b6f 100644 --- a/lib/mito/functions/public.h +++ b/lib/mito/functions/public.h @@ -33,6 +33,8 @@ #include "library_derivatives.h" // the derivation rules #include "derivation_rules.h" +// compact notation for partial derivatives +#include "partial_derivatives.h" // end of file diff --git a/lib/mito/functions/traits.h b/lib/mito/functions/traits.h index be8243f02..4629f7e4a 100644 --- a/lib/mito/functions/traits.h +++ b/lib/mito/functions/traits.h @@ -9,17 +9,42 @@ namespace mito::functions { + // the type of the product of {C} and {X} + template + using product_result_t = std::invoke_result_t, C, X>; + + // helper: fold "plus" over a pack of types + template + struct sum_result; + + // helper: base case + template + struct sum_result { + using type = T; + }; + + // helper: recursion + template + struct sum_result { + using type = std::invoke_result_t, T1, typename sum_result::type>; + }; + + // the type of the sum of {Ts...} + template + using sum_result_t = typename sum_result::type; + // the type of the sum of two functions... - template + template // ... which take the same input type {input_type}... - requires(std::is_same_v) + requires(same_input_c) struct function_sum { - // ... is the function that takes {input_type} in input and returns the type of the sum of - // the output types - using type = Function< - typename F::input_type, - typename std::invoke_result< - std::plus<>, typename F::output_type, typename G::output_type>::type>; + // the common input type + using input_type = typename std::tuple_element<0, std::tuple>::type::input_type; + // the type of the sum of the outputs + using output_type = sum_result_t; + // ... is the function that takes {input_type} in input and + // returns the type of the sum of the output types + using type = Function; }; // the type of the product of two functions... @@ -31,8 +56,7 @@ namespace mito::functions { // of the output types using type = Function< typename F::input_type, - typename std::invoke_result< - std::multiplies<>, typename F::output_type, typename G::output_type>::type>; + product_result_t>; }; // the type of the composition of two functions (F external, G internal)... @@ -45,6 +69,25 @@ namespace mito::functions { using type = Function; }; + // the type of the subscript of a tensor function... + template + struct function_subscript { + // ... is the function that takes the {input_type} of F and returns the scalar type  + using type = Function; + }; + + // the type of the transpose of a function... + template + // with matrix values... + requires(tensor::matrix_c) + struct function_transpose { + // ... is the function that takes {input_type} in input and returns the type of the + // transposed of the output matrix type + using type = Function< + typename F::input_type, + typename tensor::traits::transpose::type>; + }; + // the type of function for a function of class {F} template using function_type = Function; @@ -66,6 +109,46 @@ namespace mito::functions { std::remove_cvref_t::input_type>, typename functor_traits::output_type>; }; + + // the type of the linear combination of functions {Funcs} with coefficients of type {T}... + template + // require that all the functions to combine take the same input type + requires(same_input_c) + struct function_linear_combination { + // the input type (which is the same for all functions) + using input_type = typename std::tuple_element<0, std::tuple>::type::input_type; + // the output of the linear combination + using output_type = sum_result_t...>; + + // ... is the function that takes {input_type} in input and returns the type of the sum of + // the output types multiplied by the coefficients + using type = Function; + }; + + // the type of the dyadic product of two vector-valued functions... + template + requires( + // ... which take the same input type {input_type}... + std::is_same_v + // ... and return vectors of same size ... + && F::output_type::size == G::output_type::size) + struct function_dyadic_product { + // the common input type + using input_type = typename F::input_type; + // the scalar type of the output of the first function + using scalar_type_1 = typename F::output_type::scalar_type; + // the scalar type of the output of the second function + using scalar_type_2 = typename G::output_type::scalar_type; + // the dimension of the two vectors in output + static constexpr int output_dimension = F::output_type::size; + // the type of the sum of the outputs + using output_type = tensor::matrix_t< + F::output_type::size, F::output_type::size, + product_result_t>; + // ... is the function that takes {input_type} in input and returns the type of the dyadic + // product of the output types + using type = Function; + }; } diff --git a/lib/mito/geometry/CoordinateSystem.h b/lib/mito/geometry/CoordinateSystem.h index aab0c82fb..3e9b3d947 100644 --- a/lib/mito/geometry/CoordinateSystem.h +++ b/lib/mito/geometry/CoordinateSystem.h @@ -28,12 +28,13 @@ namespace mito::geometry { public: // constructor - CoordinateSystem() : _coordinates_map() {} + constexpr CoordinateSystem() : _coordinates_map() {} // constructor for change of coordinates template requires(coordT::dim == otherCoordT::dim) - CoordinateSystem(const CoordinateSystem & other_coord_sys) : _coordinates_map() + constexpr CoordinateSystem(const CoordinateSystem & other_coord_sys) : + _coordinates_map() { // loop on the points and coordinates of the other coordinate system for (const auto & [point, coord] : other_coord_sys) { @@ -43,24 +44,24 @@ namespace mito::geometry { } // destructor - ~CoordinateSystem() = default; + constexpr ~CoordinateSystem() = default; private: // delete copy constructor - CoordinateSystem(const CoordinateSystem &) = delete; + constexpr CoordinateSystem(const CoordinateSystem &) = delete; // delete move constructor - CoordinateSystem(CoordinateSystem &&) noexcept = delete; + constexpr CoordinateSystem(CoordinateSystem &&) noexcept = delete; // delete assignment operator - void operator=(const CoordinateSystem &) = delete; + constexpr void operator=(const CoordinateSystem &) = delete; // delete move assignment operator - void operator=(CoordinateSystem &&) noexcept = delete; + constexpr void operator=(CoordinateSystem &&) noexcept = delete; public: // place the {point} at location {coord} - auto place(const point_type & point, const coordinates_type & coord) -> void + constexpr auto place(const point_type & point, const coordinates_type & coord) -> void { // record the new point-coordinates pair auto ret = _coordinates_map.emplace(point, coord); @@ -81,14 +82,14 @@ namespace mito::geometry { } // fetch the coordinates at point {point} - auto coordinates(const point_type & point) const -> const coordinates_type & + constexpr auto coordinates(const point_type & point) const -> const coordinates_type & { // look-up the coordinates of the point in the map return _coordinates_map.at(point); } // get the coordinates of the midpoint between {point_a} and {point_b} - auto midpoint(const point_type & point_a, const point_type & point_b) const + constexpr auto midpoint(const point_type & point_a, const point_type & point_b) const -> coordinates_type { // easy enough @@ -96,8 +97,8 @@ namespace mito::geometry { } // support for ranged for loops - inline auto begin() const { return std::begin(_coordinates_map); } - inline auto end() const { return std::end(_coordinates_map); } + constexpr auto begin() const { return std::begin(_coordinates_map); } + constexpr auto end() const { return std::end(_coordinates_map); } private: // the coordinates of all points diff --git a/lib/mito/geometry/GeometricSimplex.h b/lib/mito/geometry/GeometricSimplex.h index d45996fb9..5e6555e51 100644 --- a/lib/mito/geometry/GeometricSimplex.h +++ b/lib/mito/geometry/GeometricSimplex.h @@ -54,6 +54,12 @@ namespace mito::geometry { template using cell_topological_family_type = typename topology::cell_family; + // the reference simplex type + using reference_simplex_type = reference_simplex_t; + + // type of a point in barycentric coordinates + using parametric_coordinates_type = reference_simplex_type::parametric_coordinates_type; + private: constexpr auto _sanity_check() const -> bool { @@ -89,6 +95,7 @@ namespace mito::geometry { assert(_sanity_check()); } + // QUESTION: do we need this method? // constructor with an existing oriented simplex and a collection of nodes constexpr GeometricSimplex(const nodes_type & nodes) : Invalidatable(), @@ -127,6 +134,40 @@ namespace mito::geometry { // return the composition of this simplex in terms of its vertices constexpr auto nodes() const -> const nodes_type & { return _nodes; } + // get the coordinates of a parametric point in physical space + template + constexpr auto parametrization( + const parametric_coordinates_type & point, + const coordinateSystemT & coordinate_system) const + -> coordinateSystemT::coordinates_type + { + // get the coordinates of the first node + auto coord_0 = coordinate_system.coordinates(_nodes[0]->point()); + + // the vector going from {coord_0} to the position of the parametric point (initialize + // with the zero vector) + auto result = coord_0 - coord_0; + // the sum of the parametric coordinates (initialize with zero) + auto xi = 0.0; + // loop on the vertices of the element + tensor::constexpr_for_1([&]() { + // get the coordinates of the i-th vertex + const auto & coord_i = coordinate_system.coordinates(_nodes[i]->point()); + // assemble the contribution of the i-th vertex + result += (coord_i - coord_0) * point[i]; + // accumulate the parametric coordinate + xi += point[i]; + }); + // get the coordinates of the last vertex + const auto & coord_i = coordinate_system.coordinates(_nodes[n_vertices - 1]->point()); + // add the contribution of the last vertex + // (note that the last barycentric coordinate is 1 - sum of the others) + result += (coord_i - coord_0) * (1.0 - xi); + + // return the coordinates of the parametric point + return coord_0 + result; + } + private: // the simplex nodes nodes_type _nodes; @@ -179,10 +220,10 @@ namespace mito::geometry { public: // accessor for the underlying vertex - auto vertex() const noexcept -> const vertex_type & { return _vertex; } + constexpr auto vertex() const noexcept -> const vertex_type & { return _vertex; } // accessor for the underlying point - auto point() const noexcept -> const point_type & { return _point; } + constexpr auto point() const noexcept -> const point_type & { return _point; } private: // the vertex that this node is attached to @@ -191,14 +232,6 @@ namespace mito::geometry { point_type _point; }; - // operator== for nodes - template - constexpr auto operator==( - const GeometricSimplex<0, D> & node_a, const GeometricSimplex<0, D> & node_b) -> bool - { - // two nodes are the same if they have the same id - return node_a.id() == node_b.id(); - } } diff --git a/lib/mito/geometry/ReferenceSimplex.h b/lib/mito/geometry/ReferenceSimplex.h new file mode 100644 index 000000000..6b94a0bd4 --- /dev/null +++ b/lib/mito/geometry/ReferenceSimplex.h @@ -0,0 +1,34 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::geometry { + + template + class ReferenceSimplex { + public: + // the order of the reference simplex + constexpr static int order = N; + + // the area of the reference simplex + constexpr static double area = 1.0 / mito::tensor::factorial(); + + public: + // the type of coordinates in the parametric space + using parametric_coordinates_type = coordinates_t; + + // the function extracting the I component of a parametric point + template + static constexpr auto xi = + fields::field(functions::component); + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/geometry/api.h b/lib/mito/geometry/api.h index f98027129..8bdbb8ec8 100644 --- a/lib/mito/geometry/api.h +++ b/lib/mito/geometry/api.h @@ -17,6 +17,10 @@ namespace mito::geometry { template using point_t = utilities::shared_ptr>; + // reference simplex alias + template + using reference_simplex_t = ReferenceSimplex; + // geometric simplex alias template using geometric_simplex_t = GeometricSimplex; @@ -25,6 +29,15 @@ namespace mito::geometry { template using node_t = utilities::std_shared_ptr>; + // reference segment alias + using reference_segment_t = reference_simplex_t<1>; + + // reference triangle alias + using reference_triangle_t = reference_simplex_t<2>; + + // reference tetrahedron alias + using reference_tetrahedron_t = reference_simplex_t<3>; + // segment alias template requires(D > 0) diff --git a/lib/mito/geometry/forward.h b/lib/mito/geometry/forward.h index be6ef700d..9b0bfdb5b 100644 --- a/lib/mito/geometry/forward.h +++ b/lib/mito/geometry/forward.h @@ -29,6 +29,10 @@ namespace mito::geometry { template class Point; + // reference simplex + template + class ReferenceSimplex; + // class geometric simplex template requires((N >= 0) && (N <= D) && (D > 0)) diff --git a/lib/mito/geometry/public.h b/lib/mito/geometry/public.h index d72d534b8..ea5f547bd 100644 --- a/lib/mito/geometry/public.h +++ b/lib/mito/geometry/public.h @@ -33,6 +33,7 @@ // classes implementation #include "CoordinateSystem.h" #include "Point.h" +#include "ReferenceSimplex.h" #include "GeometricSimplex.h" #include "PointCloud.h" diff --git a/lib/mito/geometry/utilities.h b/lib/mito/geometry/utilities.h index a59fdff2a..cd4c28189 100644 --- a/lib/mito/geometry/utilities.h +++ b/lib/mito/geometry/utilities.h @@ -102,9 +102,8 @@ namespace mito::geometry { }; // return the geometric simplex - return geometric_simplex_type({ node_type( - vertices[K], - (*std::ranges::find_if(nodes, has_vertex(vertices[K])))->point())... }); + return geometric_simplex_type( + { (*std::ranges::find_if(nodes, has_vertex(vertices[K])))... }); }; // build a geometric simplex based on {simplex} with the vertex-point pair as appears in @@ -134,7 +133,7 @@ namespace mito::geometry { }; // return the node - return node_type(vertex, (*std::ranges::find_if(nodes, has_vertex(vertex)))->point()); + return *std::ranges::find_if(nodes, has_vertex(vertex)); } // returns the geometric simplex with opposite orientation to {simplex} diff --git a/lib/mito/io/externals.h b/lib/mito/io/externals.h index 29d77e1b3..deb1c8864 100644 --- a/lib/mito/io/externals.h +++ b/lib/mito/io/externals.h @@ -14,7 +14,7 @@ #include "../journal.h" #include "../geometry.h" #include "../mesh.h" -#include "../discretization.h" +#include "../fem.h" // end of file diff --git a/lib/mito/io/vtk/FieldVTKWriter.h b/lib/mito/io/vtk/FieldVTKWriter.h index 002b3a28c..93eab2364 100644 --- a/lib/mito/io/vtk/FieldVTKWriter.h +++ b/lib/mito/io/vtk/FieldVTKWriter.h @@ -9,23 +9,54 @@ namespace mito::io::vtk { - // the type of the grid writer depending on the type of grid and on the coordinate system - template - struct field_type; - - // specialization to {mesh_c} case - template - struct field_type { - template - using type = discretization::nodal_field_t; - }; + namespace { + // the type of the grid writer depending on the type of grid and on the coordinate system + template + struct field_type; + + // specialization to {mesh_c} case + template + struct field_type { + template + using type = discrete::mesh_field_t; + }; + + // specialization to {point_cloud_c} case + template + struct field_type { + template + using type = discrete::point_field_t; + }; + + // specialization to {function_space_c} case + template + struct field_type { + template + using type = discrete::nodal_field_t; + }; + + // utility function to get the data pointer + template + constexpr auto dim() -> int + { + if constexpr (std::is_arithmetic_v) { + return 1; + } else { + return Y::size; + } + } - // specialization to {point_cloud_c} case - template - struct field_type { - template - using type = discretization::point_field_t; - }; + // utility function to get the data pointer + template + auto begin(Y & value) + { + if constexpr (std::is_arithmetic_v) { + return &value; + } else { + return std::begin(value); + } + } + } template class FieldVTKWriter { @@ -53,30 +84,27 @@ namespace mito::io::vtk { template auto _attach_field(const field_type & field, std::string fieldname) -> void { - // get the number of entries in the field - auto field_size = field.size(); - // get the grid auto & grid = _grid_writer.grid(); - // check the number of entries in the field equals the number of points in the grid - assert(field_size == grid->GetNumberOfPoints()); + // get the number of points in the grid + auto n_points = grid->GetNumberOfPoints(); // initialize a vtk array auto vtkArray = vtkSmartPointer::New(); vtkArray->SetName(fieldname.data()); - vtkArray->SetNumberOfComponents(Y::size); - vtkArray->SetNumberOfTuples(field_size); + vtkArray->SetNumberOfComponents(dim()); + vtkArray->SetNumberOfTuples(n_points); // populate the array - if constexpr (mesh::mesh_c) { + if constexpr (mesh::mesh_c or fem::function_space_c) { // get the nodes in the grid const auto & nodes = _grid_writer.nodes(); // populate the array with the nodal values for (const auto & [node, index] : nodes) { // get the index corresponding to the current node - vtkArray->SetTuple(index, field(node).begin()); + vtkArray->SetTuple(index, begin(field(node))); } } else if constexpr (geometry::point_cloud_c) { // get the points in the grid @@ -86,12 +114,19 @@ namespace mito::io::vtk { int index = 0; for (const auto & point : points) { // get the index corresponding to the current point - vtkArray->SetTuple(index++, field(point).begin()); + vtkArray->SetTuple(index++, begin(field(point))); } + } else { + // something went wrong: make a channel and report an error + journal::error_t channel("mito.field_vtk_writer.attach_field"); + channel << "unkown {grid_type}" << journal::endl; } // insert array into output grid grid->GetPointData()->AddArray(vtkArray); + + // all done + return; } public: diff --git a/lib/mito/io/vtk/NodeVTKWriter.h b/lib/mito/io/vtk/NodeVTKWriter.h new file mode 100644 index 000000000..41f0dbf2c --- /dev/null +++ b/lib/mito/io/vtk/NodeVTKWriter.h @@ -0,0 +1,124 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::io::vtk { + + // TOFIX: this writer works for now on discretization nodes that are also mesh nodes, but + // should be generalized to write on any discretization node type, including nodes of higher + // order elements + template < + fem::function_space_c functionSpaceT, geometry::coordinate_system_c coordSystemT, + class vtkGridWriterT> + requires( + utilities::same_dim_c + && utilities::same_dim_c) + class NodeVTKWriter : public vtkGridWriterT { + public: + // the grid type + using grid_type = functionSpaceT; + // the grid writer type + using grid_writer_type = vtkGridWriterT; + + private: + // the element type + using element_type = typename grid_type::element_type; + // the coordinate system type + using coord_system_type = coordSystemT; + // the dimension of the physical space + static constexpr int D = grid_type::dim; + // the type of node + using node_type = typename functionSpaceT::discretization_node_type; + // the type of a collection of nodes (nodes are mapped to the index of the vtk points; + // points that are shared among multiple elements have the same index) + using nodes_type = std::unordered_map>; + + private: + auto _create_vtk_grid( + const grid_type & function_space, const coord_system_type & coordinate_system) + { + // vtk points and cells + auto pointsVtk = vtkSmartPointer::New(); + + // global index assigned to each vtk point + auto indexPointVtk = 0; + + // loop over the elements + for (const auto & element : function_space.elements()) { + // get the cell associated with the element + const auto & cell = element.cell(); + // loop over the nodes of the cell + for (const auto & node : cell.nodes()) { + // try to insert the point in the map + auto [_, inserted] = + _nodes.insert({ function_space.node_map().at(node), indexPointVtk }); + // if the point was inserted the map (i.e. not a duplicate) + if (inserted) { + // insert the new vtk point + insert_vtk_point(coordinate_system.coordinates(node->point()), pointsVtk); + // update global index for the vtk point + ++indexPointVtk; + } + } + } + + // loop over the elements + for (const auto & element : function_space.elements()) { + // get the cell associated with the element + const auto & cell = element.cell(); + + // create vtk cell + auto cellVtk = vtkCellPointer(); + + // local index for the points of the cell + auto indexLocalPointVtk = 0; + + // loop over the nodes of the cell + for (const auto & node : cell.nodes()) { + // assert that the node is present in the collection of nodes + assert(_nodes.contains(function_space.node_map().at(node))); + // get the index of the node + auto index = _nodes.at(function_space.node_map().at(node)); + // set the id of the point + cellVtk->GetPointIds()->SetId(indexLocalPointVtk, index); + // update local index for the points in the cell + ++indexLocalPointVtk; + } + + // insert the new cell + this->_grid->InsertNextCell(cellVtk->GetCellType(), cellVtk->GetPointIds()); + } + + // set the grid points + this->_grid->SetPoints(pointsVtk); + + // all done + return; + } + + public: + NodeVTKWriter( + std::string filename, const grid_type & function_space, + const coord_system_type & coord_system) : + grid_writer_type(filename) + { + _create_vtk_grid(function_space, coord_system); + } + + // accessor for the nodes + auto nodes() const -> const nodes_type & { return _nodes; } + + private: + // a collection of nodes in the grid + nodes_type _nodes; + }; + +} // namespace mito::io::vtk + + +// end of file diff --git a/lib/mito/io/vtk/api.h b/lib/mito/io/vtk/api.h index a5bd91967..b8e9440b9 100644 --- a/lib/mito/io/vtk/api.h +++ b/lib/mito/io/vtk/api.h @@ -19,6 +19,12 @@ namespace mito::io::vtk { requires(utilities::same_dim_c) using cloud_writer_t = PointCloudVTKWriter>; + // node writer alias + template + requires(utilities::same_dim_c) + using node_writer_t = + NodeVTKWriter>; + // field writer alias template using field_writer_t = FieldVTKWriter; @@ -33,6 +39,13 @@ namespace mito::io::vtk { requires(utilities::same_dim_c) auto grid_writer(std::string filename, const cloudT & cloud, const coordSystemT & coord_system); + // node writer factory + template + requires(utilities::same_dim_c) + auto grid_writer( + std::string filename, const functionSpaceT & function_space, + const coordSystemT & coord_system); + // vtk mesh field writer factory template requires(utilities::same_dim_c) @@ -44,6 +57,13 @@ namespace mito::io::vtk { auto field_writer( std::string filename, const cloudT & cloud, const coordSystemT & coord_system); + // node field writer factory + template + requires(utilities::same_dim_c) + auto field_writer( + std::string filename, const functionSpaceT & function_space, + const coordSystemT & coord_system); + #ifdef WITH_PARALLEL_VTK // parallel mesh writer alias template @@ -57,6 +77,12 @@ namespace mito::io::vtk { using parallel_cloud_writer_t = PointCloudVTKWriter>; + // parallel node writer alias + template + requires(utilities::same_dim_c) + using parallel_node_writer_t = + NodeVTKWriter>; + // parallel vtk mesh writer factory template requires(utilities::same_dim_c) @@ -69,6 +95,13 @@ namespace mito::io::vtk { auto parallel_grid_writer( std::string filename, const cloudT & cloud, const coordSystemT & coord_system); + // parallel node writer factory + template + requires(utilities::same_dim_c) + auto parallel_node_writer( + std::string filename, const functionSpaceT & function_space, + const coordSystemT & coord_system); + // parallel vtk mesh field writer factory template requires(utilities::same_dim_c) @@ -80,6 +113,14 @@ namespace mito::io::vtk { requires(utilities::same_dim_c) auto parallel_field_writer( std::string filename, const cloudT & cloud, const coordSystemT & coord_system); + + // parallel node field writer factory + template + requires(utilities::same_dim_c) + auto parallel_field_writer( + std::string filename, const functionSpaceT & function_space, + const coordSystemT & coord_system); + #endif // WITH_PARALLEL_VTK } diff --git a/lib/mito/io/vtk/externals.h b/lib/mito/io/vtk/externals.h index 2b783e4ce..c4eaa8b6a 100644 --- a/lib/mito/io/vtk/externals.h +++ b/lib/mito/io/vtk/externals.h @@ -22,4 +22,7 @@ #include #endif // WITH_PARALLEL_VTK +// support +#include "../../discrete.h" + // end of file diff --git a/lib/mito/io/vtk/factories.h b/lib/mito/io/vtk/factories.h index a705ee288..94c11601d 100644 --- a/lib/mito/io/vtk/factories.h +++ b/lib/mito/io/vtk/factories.h @@ -25,6 +25,15 @@ namespace mito::io::vtk { return cloud_writer_t(filename, cloud, coord_system); } + // vtk node writer factory + template + requires(utilities::same_dim_c) + auto grid_writer( + std::string filename, const functionSpaceT & node, const coordSystemT & coord_system) + { + return node_writer_t(filename, node, coord_system); + } + // vtk mesh field writer factory template requires(utilities::same_dim_c) @@ -43,6 +52,16 @@ namespace mito::io::vtk { filename, cloud, coord_system); } + // vtk node field writer factory + template + requires(utilities::same_dim_c) + auto field_writer( + std::string filename, const functionSpaceT & node, const coordSystemT & coord_system) + { + return field_writer_t, coordSystemT>( + filename, node, coord_system); + } + #ifdef WITH_PARALLEL_VTK // parallel vtk mesh writer factory template @@ -62,6 +81,15 @@ namespace mito::io::vtk { return parallel_cloud_writer_t(filename, cloud, coord_system); } + // parallel vtk node writer factory + template + requires(utilities::same_dim_c) + auto parallel_grid_writer( + std::string filename, const functionSpaceT & node, const coordSystemT & coord_system) + { + return parallel_node_writer_t(filename, node, coord_system); + } + // parallel vtk mesh field writer factory template requires(utilities::same_dim_c) @@ -81,8 +109,18 @@ namespace mito::io::vtk { return field_writer_t, coordSystemT>( filename, cloud, coord_system); } -#endif // WITH_PARALLEL_VTK + // parallel vtk node field writer factory + template + requires(utilities::same_dim_c) + auto parallel_field_writer( + std::string filename, const functionSpaceT & node, const coordSystemT & coord_system) + { + return field_writer_t, coordSystemT>( + filename, node, coord_system); + } + +#endif // WITH_PARALLEL_VTK } diff --git a/lib/mito/io/vtk/forward.h b/lib/mito/io/vtk/forward.h index e770c469f..7615837e6 100644 --- a/lib/mito/io/vtk/forward.h +++ b/lib/mito/io/vtk/forward.h @@ -28,6 +28,15 @@ namespace mito::io::vtk { && utilities::same_dim_c) class PointCloudVTKWriter; + // class node writer + template < + fem::function_space_c functionSpaceT, geometry::coordinate_system_c coordSystemT, + class vtkGridWriterT> + requires( + utilities::same_dim_c + && utilities::same_dim_c) + class NodeVTKWriter; + // class field writer template class FieldVTKWriter; diff --git a/lib/mito/io/vtk/public.h b/lib/mito/io/vtk/public.h index 4f1eab1cf..d4756da54 100644 --- a/lib/mito/io/vtk/public.h +++ b/lib/mito/io/vtk/public.h @@ -24,6 +24,7 @@ #include "GridVTKWriter.h" #include "MeshVTKWriter.h" #include "PointCloudVTKWriter.h" +#include "NodeVTKWriter.h" #include "FieldVTKWriter.h" #ifdef WITH_PARALLEL_VTK #include "ParallelGridVTKWriter.h" diff --git a/lib/mito/manifolds/Manifold.h b/lib/mito/manifolds/Manifold.h index ba53b15ca..6e2de11f7 100644 --- a/lib/mito/manifolds/Manifold.h +++ b/lib/mito/manifolds/Manifold.h @@ -22,10 +22,6 @@ namespace mito::manifolds { static constexpr int D = cellT::dim; // the dimension of the manifold (that is that of the cell) static constexpr int N = cellT::order; - // the dimension of the parametric space - static constexpr int parametricDim = parametric_dim(); - // typedef for a point in parametric coordinates - using parametric_point_type = manifolds::parametric_point_t; public: // typedef for cell type @@ -68,6 +64,15 @@ namespace mito::manifolds { Manifold & operator=(Manifold &&) noexcept = delete; public: + // accessor for the mesh + constexpr auto mesh() const noexcept -> const mesh_type & { return _mesh; } + + // accessor for the coordinate system + constexpr auto coordinate_system() const noexcept -> const coordinate_system_type & + { + return _coordinate_system; + } + constexpr auto elements() const noexcept -> const cells_type & { return _mesh.cells(); } constexpr auto nElements() const noexcept -> int { return std::size(_mesh.cells()); } @@ -78,27 +83,6 @@ namespace mito::manifolds { return _coordinate_system.coordinates(v->point()); } - constexpr auto parametrization( - const cell_type & cell, const parametric_point_type & point) const -> coordinates_type - { - // get the coordinates of the first node - auto coord_0 = coordinates(cell.nodes()[0]); - - // the vector going from {coord_0} to the position of the parametric point (initialize - // with the zero vector) - auto result = coord_0 - coord_0; - - // loop on the element nodes - int v = 0; - for (const auto & node : cell.nodes()) { - result += (coordinates(node) - coord_0) * point[v]; - ++v; - } - - // return the coordinates of the parametric point - return coord_0 + result; - } - constexpr auto print() const -> void { // make a channel diff --git a/lib/mito/manifolds/api.h b/lib/mito/manifolds/api.h index a5e824bfc..bcb81f125 100644 --- a/lib/mito/manifolds/api.h +++ b/lib/mito/manifolds/api.h @@ -9,10 +9,6 @@ namespace mito::manifolds { - // a point in parametric coordinates - template - using parametric_point_t = std::array; - // manifold alias template using manifold_t = Manifold; diff --git a/lib/mito/manifolds/forward.h b/lib/mito/manifolds/forward.h index 2f71ab19f..940a26c99 100644 --- a/lib/mito/manifolds/forward.h +++ b/lib/mito/manifolds/forward.h @@ -13,6 +13,15 @@ namespace mito::manifolds { template requires(cellT::dim == coordsT::dim) class Manifold; + + // concept of a manifold + template + concept manifold_c = requires(F c) { + // require that F only binds to {Manifold} specializations + []( + const Manifold &) { + }(c); + }; } diff --git a/lib/mito/manifolds/parametric.h b/lib/mito/manifolds/parametric.h deleted file mode 100644 index 49b703751..000000000 --- a/lib/mito/manifolds/parametric.h +++ /dev/null @@ -1,29 +0,0 @@ -// -*- c++ -*- -// -// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved -// - - -namespace mito::manifolds { - - template - constexpr auto parametric_dim() noexcept -> int; - - template <> - constexpr auto parametric_dim() noexcept -> int - { - return 2; - } - - template <> - constexpr auto parametric_dim() noexcept -> int - { - return 3; - } - - template <> - constexpr auto parametric_dim() noexcept -> int - { - return 4; - } -} \ No newline at end of file diff --git a/lib/mito/manifolds/public.h b/lib/mito/manifolds/public.h index 9e7ad2335..abf2bc8b6 100644 --- a/lib/mito/manifolds/public.h +++ b/lib/mito/manifolds/public.h @@ -17,7 +17,6 @@ #include "api.h" // classes implementation -#include "parametric.h" #include "Manifold.h" // factories implementation diff --git a/lib/mito/materials/MaterialConsistencyTest.h b/lib/mito/materials/MaterialConsistencyTest.h index f9662eb95..461765dc2 100644 --- a/lib/mito/materials/MaterialConsistencyTest.h +++ b/lib/mito/materials/MaterialConsistencyTest.h @@ -32,7 +32,7 @@ class mito::materials::MaterialConsistencyTest { auto F_perturbed = F; // initialize the energy increment tensor - auto energy_increment = mito::tensor::matrix_t<3>(); + auto energy_increment = mito::tensor::matrix_t<3>{}; // for each component of the deformation gradient for (int i = 0; i < 3; i++) { diff --git a/lib/mito/matrix_solvers.h b/lib/mito/matrix_solvers.h new file mode 100644 index 000000000..565e3939a --- /dev/null +++ b/lib/mito/matrix_solvers.h @@ -0,0 +1,14 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// publish the interface +#include "matrix_solvers/public.h" + + +// end of file \ No newline at end of file diff --git a/lib/mito/matrix_solvers/backend/petsc.h b/lib/mito/matrix_solvers/backend/petsc.h new file mode 100644 index 000000000..f6d8f7ce4 --- /dev/null +++ b/lib/mito/matrix_solvers/backend/petsc.h @@ -0,0 +1,14 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// publish the interface +#include "petsc/public.h" + + +// end of file \ No newline at end of file diff --git a/lib/mito/matrix_solvers/backend/petsc/PETScKrylovSolver.cc b/lib/mito/matrix_solvers/backend/petsc/PETScKrylovSolver.cc new file mode 100644 index 000000000..4ee548109 --- /dev/null +++ b/lib/mito/matrix_solvers/backend/petsc/PETScKrylovSolver.cc @@ -0,0 +1,136 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + + +#include "forward.h" +#include "externals.h" +#include "PETScLinearSystem.h" +#include "PETScKrylovSolver.h" + + +// constructor +mito::matrix_solvers::petsc::PETScKrylovSolver::PETScKrylovSolver( + linear_system_type & linear_system) : + _linear_system(linear_system), + _options_prefix(linear_system.label() + "_") +{} + +// destructor +mito::matrix_solvers::petsc::PETScKrylovSolver::~PETScKrylovSolver() {} + +// create the Krylov solver +auto +mito::matrix_solvers::petsc::PETScKrylovSolver::create() -> void +{ + // create the Krylov solver + PetscCallVoid(KSPCreate(PETSC_COMM_WORLD, &_ksp)); + PetscCallVoid(KSPSetOperators(_ksp, _linear_system._matrix, _linear_system._matrix)); + PetscCallVoid(KSPSetOptionsPrefix(_ksp, _options_prefix.c_str())); + + // all done + return; +} + +// destroy the Krylov solver +auto +mito::matrix_solvers::petsc::PETScKrylovSolver::destroy() -> void +{ + // free the memory of the linear system + _linear_system.destroy(); + + // destroy the Krylov solver + PetscCallVoid(KSPDestroy(&_ksp)); + + // all done + return; +} + +namespace { + // helper function to prepend the prefix {prefix} to each of the space-separated + // options leading with '-' + auto prepend_options_prefix(const std::string & options, const std::string & prefix) + -> std::string + { + // split the {options} string into words and store them in a vector + size_t pos_old = 0; + size_t pos_new = 0; + std::vector options_vector; + while ((pos_new = options.find(" ", pos_old)) != std::string::npos) { + std::string word_string = options.substr(pos_old, pos_new - pos_old); + options_vector.push_back(word_string); + pos_old = pos_new + 1; + } + std::string word_string = options.substr(pos_old, pos_new - pos_old); + options_vector.push_back(word_string); + + // for each word, if the leading character is '-', insert the {prefix} between '-' and the + // rest of the word + for (auto & word : options_vector) { + if (word[0] == '-') { + word.erase(0, 1); + word = "-" + prefix + word; + } + } + + // concatenate the words into a single string + std::string prefixed_options; + for (auto & word : options_vector) { + prefixed_options += word + " "; + } + + // all done + return prefixed_options; + } +} + +// set petsc options +auto +mito::matrix_solvers::petsc::PETScKrylovSolver::set_options(const options_type & options) -> void +{ + // prepend the prefix {_options_prefix} to each of the space-separated options in input + auto prefixed_options = prepend_options_prefix(options, _options_prefix); + + // record the options with PETSc + PetscCallVoid(PetscOptionsInsertString(PETSC_NULLPTR, prefixed_options.c_str())); + + // configure the Krylov solver with the options + PetscCallVoid(KSPSetFromOptions(_ksp)); + + // // show all options that have been set + // PetscOptionsView(PETSC_NULLPTR, PETSC_VIEWER_STDOUT_WORLD); + + // all done + return; +} + +// solve the linear system +auto +mito::matrix_solvers::petsc::PETScKrylovSolver::solve() -> void +{ + // assemble the linear system + _linear_system.assemble(); + + // solve the linear system + PetscCallVoid(KSPSolve(_ksp, _linear_system._rhs, _linear_system._solution)); + + // all done + return; +} + +// print the linear system of equations of the petsc solver +auto +mito::matrix_solvers::petsc::PETScKrylovSolver::print() const -> void +{ + // create a reporting channel + journal::info_t channel("mito.solvers.petsc.PETScKrylovSolver"); + + // print the linear system + _linear_system.print(); + + // all done + return; +} + +// end of file diff --git a/lib/mito/matrix_solvers/backend/petsc/PETScKrylovSolver.h b/lib/mito/matrix_solvers/backend/petsc/PETScKrylovSolver.h new file mode 100644 index 000000000..bce47ef3a --- /dev/null +++ b/lib/mito/matrix_solvers/backend/petsc/PETScKrylovSolver.h @@ -0,0 +1,58 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::matrix_solvers::petsc { + + class PETScKrylovSolver { + private: + // the index type + using index_type = PetscInt; + // the linear system type + using linear_system_type = PETScLinearSystem; + // the solver type + using solver_type = KSP; + // the options type + using options_type = std::string; + + public: + // constructor + PETScKrylovSolver(linear_system_type &); + + // destructor + ~PETScKrylovSolver(); + + public: + // create the Krylov solver + auto create() -> void; + + // destroy the Krylov solver + auto destroy() -> void; + + // set petsc options + auto set_options(const options_type &) -> void; + + // solve the linear system + auto solve() -> void; + + // print the linear system of equations of the petsc solver + auto print() const -> void; + + private: + // the linear system + linear_system_type & _linear_system; + // the prefix for the PETSc options + options_type _options_prefix; + // the Krylov solver + solver_type _ksp; + }; + +} // namespace mito + + +// end of file diff --git a/lib/mito/matrix_solvers/backend/petsc/PETScLinearSystem.cc b/lib/mito/matrix_solvers/backend/petsc/PETScLinearSystem.cc new file mode 100644 index 000000000..cb8adc373 --- /dev/null +++ b/lib/mito/matrix_solvers/backend/petsc/PETScLinearSystem.cc @@ -0,0 +1,158 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + + +#include "forward.h" +#include "externals.h" +#include "PETScLinearSystem.h" + + +// constructor +mito::matrix_solvers::petsc::PETScLinearSystem::PETScLinearSystem(const label_type & label) : + _label(label), + _n_equations(0) +{} + +// destructor +mito::matrix_solvers::petsc::PETScLinearSystem::~PETScLinearSystem() {} + +// allocate memory for the matrix, right-hand side, and solution +auto +mito::matrix_solvers::petsc::PETScLinearSystem::create(index_type size) -> void +{ + // take note of the number of equations + _n_equations = size; + + // create the vectors + PetscCallVoid(VecCreate(PETSC_COMM_WORLD, &_solution)); + PetscCallVoid(VecSetSizes(_solution, PETSC_DECIDE, size)); + PetscCallVoid(VecCreate(PETSC_COMM_WORLD, &_rhs)); + PetscCallVoid(VecSetSizes(_rhs, PETSC_DECIDE, size)); + + // create the matrix + PetscCallVoid(MatCreate(PETSC_COMM_WORLD, &_matrix)); + PetscCallVoid(MatSetSizes(_matrix, PETSC_DECIDE, PETSC_DECIDE, size, size)); + + // set the default options (do not allow the user to control the options for matrix and + // vectors) + PetscCallVoid(MatSetFromOptions(_matrix)); + PetscCallVoid(VecSetFromOptions(_rhs)); + PetscCallVoid(VecSetFromOptions(_solution)); + + // all done + return; +} + +// free memory for the matrix, right-hand side, and solution +auto +mito::matrix_solvers::petsc::PETScLinearSystem::destroy() -> void +{ + // destroy the matrix, right-hand side, solution + PetscCallVoid(MatDestroy(&_matrix)); + PetscCallVoid(VecDestroy(&_solution)); + PetscCallVoid(VecDestroy(&_rhs)); + + // all done + return; +} + +// get the label of the linear system +auto +mito::matrix_solvers::petsc::PETScLinearSystem::label() const -> label_type +{ + // easy enough + return _label; +} + +// assemble the linear system +auto +mito::matrix_solvers::petsc::PETScLinearSystem::assemble() -> void +{ + // assemble matrix + PetscCallVoid(MatAssemblyBegin(_matrix, MAT_FINAL_ASSEMBLY)); + PetscCallVoid(MatAssemblyEnd(_matrix, MAT_FINAL_ASSEMBLY)); + + // // show the matrix and the right-hand-side + // PetscCallVoid(MatView(_matrix, PETSC_VIEWER_STDOUT_WORLD)); + // PetscCallVoid(VecView(_rhs, PETSC_VIEWER_STDOUT_WORLD)); + + // all done + return; +} + +// set the matrix entry at ({row}, {col}) to {value} +auto +mito::matrix_solvers::petsc::PETScLinearSystem::insert_matrix_value( + index_type row, index_type col, const scalar_type & value) -> void +{ + // delegate to PETSc + PetscCallVoid(MatSetValue(_matrix, row, col, value, INSERT_VALUES)); + + // all done + return; +} + +// add {value} to matrix entry at ({row}, {col}) +auto +mito::matrix_solvers::petsc::PETScLinearSystem::add_matrix_value( + index_type row, index_type col, const scalar_type & value) -> void +{ + // delegate to PETSc + PetscCallVoid(MatSetValue(_matrix, row, col, value, ADD_VALUES)); + + // all done + return; +} + +// set the right-hand side entry at {row} to {value} +auto +mito::matrix_solvers::petsc::PETScLinearSystem::insert_rhs_value( + index_type row, const scalar_type & value) -> void +{ + // delegate to PETSc + PetscCallVoid(VecSetValue(_rhs, row, value, INSERT_VALUES)); + + // all done + return; +} + +// add {value} to right-hand side entry at {row} +auto +mito::matrix_solvers::petsc::PETScLinearSystem::add_rhs_value( + index_type row, const scalar_type & value) -> void +{ + // delegate to PETSc + PetscCallVoid(VecSetValue(_rhs, row, value, ADD_VALUES)); + + // all done + return; +} + +auto +mito::matrix_solvers::petsc::PETScLinearSystem::n_equations() const -> int +{ + return _n_equations; +} + + +// print the linear system of equations of the petsc solver +auto +mito::matrix_solvers::petsc::PETScLinearSystem::print() const -> void +{ + // create a reporting channel + journal::info_t channel("mito.solvers.petsc.PETScLinearSystem"); + + // print the matrix + channel << "Matrix:" << journal::endl; + PetscCallVoid(MatView(_matrix, PETSC_VIEWER_STDOUT_WORLD)); + // print the right-hand side + channel << "Right-hand side:" << journal::endl; + PetscCallVoid(VecView(_rhs, PETSC_VIEWER_STDOUT_WORLD)); + + // all done + return; +} + +// end of file diff --git a/lib/mito/solvers/backend/petsc/PETScKrylovSolver.h b/lib/mito/matrix_solvers/backend/petsc/PETScLinearSystem.h similarity index 53% rename from lib/mito/solvers/backend/petsc/PETScKrylovSolver.h rename to lib/mito/matrix_solvers/backend/petsc/PETScLinearSystem.h index f878ada6b..fd1450967 100644 --- a/lib/mito/solvers/backend/petsc/PETScKrylovSolver.h +++ b/lib/mito/matrix_solvers/backend/petsc/PETScLinearSystem.h @@ -7,9 +7,13 @@ #pragma once -namespace mito::solvers::petsc { +namespace mito::matrix_solvers::petsc { + + class PETScLinearSystem { + + // friend declarations + friend class PETScKrylovSolver; - class PETScKrylovSolver { private: // the index type using index_type = PetscInt; @@ -19,42 +23,28 @@ namespace mito::solvers::petsc { using vector_type = Vec; // the matrix type using matrix_type = Mat; - // the solver type - using solver_type = KSP; - // the options type - using options_type = std::string; // the label type using label_type = std::string; public: // constructor - PETScKrylovSolver(const label_type &); + PETScLinearSystem(const label_type &); // destructor - ~PETScKrylovSolver(); - - private: - // initialize PETSc if it has not been initialized before - auto _initialize_petsc() -> void; - - // finalize PETSc if it has been initialized by this instance - auto _finalize_petsc() -> void; - - // create the matrix, right-hand side, solution, and Krylov solver - auto _create_linear_system(index_type) -> void; - - // destroy the matrix, right-hand side, solution, and Krylov solver - auto _destroy_linear_system() -> void; + ~PETScLinearSystem(); public: - // initialize the petsc solver - auto initialize(index_type size) -> void; + // create the matrix, right-hand side, and solution + auto create(index_type) -> void; - // finalize the petsc solver - auto finalize() -> void; + // destroy the matrix, right-hand side, and solution + auto destroy() -> void; - // set petsc options - auto set_options(const options_type &) -> void; + // get the label of the linear system + auto label() const -> label_type; + + // assemble the linear system + auto assemble() -> void; // set the value of a matrix entry auto insert_matrix_value(index_type, index_type, const scalar_type &) -> void; @@ -68,35 +58,38 @@ namespace mito::solvers::petsc { // add a value to a right-hand side entry auto add_rhs_value(index_type, const scalar_type &) -> void; - // solve the linear system - auto solve() -> void; + // accessor to the number of equations + auto n_equations() const -> int; // get the solution vector template auto get_solution(solutionT & solution) const -> void; + // print the linear system + auto print() const -> void; + private: - // the prefix for the PETSc options - label_type _options_prefix; // a flag to recall if this instance has initialized PETSc bool _initialized_petsc; + // the label for the linear system (this is used to prefix PETSc options) + label_type _label; // the matrix matrix_type _matrix; // the right-hand side vector vector_type _rhs; // the solution vector vector_type _solution; - // the Krylov solver - solver_type _ksp; + // the number of equations + int _n_equations; }; } // namespace mito // get the template definitions -#define mito_solvers_backend_petsc_PETScKrylovSolver_icc -#include "PETScKrylovSolver.icc" -#undef mito_solvers_backend_petsc_PETScKrylovSolver_icc +#define mito_solvers_backend_petsc_PETScLinearSystem_icc +#include "PETScLinearSystem.icc" +#undef mito_solvers_backend_petsc_PETScLinearSystem_icc // end of file diff --git a/lib/mito/solvers/backend/petsc/PETScKrylovSolver.icc b/lib/mito/matrix_solvers/backend/petsc/PETScLinearSystem.icc similarity index 75% rename from lib/mito/solvers/backend/petsc/PETScKrylovSolver.icc rename to lib/mito/matrix_solvers/backend/petsc/PETScLinearSystem.icc index 5247c9c49..147993bc6 100644 --- a/lib/mito/solvers/backend/petsc/PETScKrylovSolver.icc +++ b/lib/mito/matrix_solvers/backend/petsc/PETScLinearSystem.icc @@ -4,15 +4,15 @@ // -#if !defined(mito_solvers_backend_petsc_PETScKrylovSolver_icc) -#error This header file contains implementation details of class mito::solvers::petsc::PETScKrylovSolver +#if !defined(mito_solvers_backend_petsc_PETScLinearSystem_icc) +#error This header file contains implementation details of class mito::matrix_solvers::petsc::PETScLinearSystem #else // get the solution at entry {row} template auto -mito::solvers::petsc::PETScKrylovSolver::get_solution(solutionT & solution) const -> void +mito::matrix_solvers::petsc::PETScLinearSystem::get_solution(solutionT & solution) const -> void { // get a pointer to the beginning of {_solution} const scalar_type * u; @@ -36,6 +36,6 @@ mito::solvers::petsc::PETScKrylovSolver::get_solution(solutionT & solution) cons } -#endif // mito_solvers_backend_petsc_PETScKrylovSolver_icc +#endif // mito_solvers_backend_petsc_PETScLinearSystem_icc // end of file diff --git a/lib/mito/solvers/backend/petsc/api.h b/lib/mito/matrix_solvers/backend/petsc/api.h similarity index 63% rename from lib/mito/solvers/backend/petsc/api.h rename to lib/mito/matrix_solvers/backend/petsc/api.h index 3079671e5..c8c4ecafe 100644 --- a/lib/mito/solvers/backend/petsc/api.h +++ b/lib/mito/matrix_solvers/backend/petsc/api.h @@ -7,7 +7,10 @@ #pragma once -namespace mito::solvers::petsc { +namespace mito::matrix_solvers::petsc { + + // petsc linear system + using linear_system_t = PETScLinearSystem; // petsc Krlov solver using ksp_t = PETScKrylovSolver; diff --git a/lib/mito/matrix_solvers/backend/petsc/externals.h b/lib/mito/matrix_solvers/backend/petsc/externals.h new file mode 100644 index 000000000..7d0bf8c63 --- /dev/null +++ b/lib/mito/matrix_solvers/backend/petsc/externals.h @@ -0,0 +1,39 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// externals +#include +#include +#include + +// support +#include "../../../journal.h" + +// petsc support +#include + + +namespace mito::petsc { + + // initialize petsc + inline auto initialize() -> void + { + PetscCallVoid(PetscInitializeNoArguments()); + } + + // finalize petsc + inline auto finalize() -> void + { + PetscCallVoid(PetscFinalize()); + } + +} + + +// end of file diff --git a/lib/mito/matrix_solvers/backend/petsc/factories.h b/lib/mito/matrix_solvers/backend/petsc/factories.h new file mode 100644 index 000000000..1db34c218 --- /dev/null +++ b/lib/mito/matrix_solvers/backend/petsc/factories.h @@ -0,0 +1,26 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::matrix_solvers::petsc { + + // petsc linear system + auto linear_system(const std::string & name) + { + return linear_system_t(name); + } + + // petsc Krylov solver + auto ksp(linear_system_t & linear_system) + { + return ksp_t(linear_system); + } +} + + +// end of file diff --git a/lib/mito/solvers/backend/petsc/forward.h b/lib/mito/matrix_solvers/backend/petsc/forward.h similarity index 65% rename from lib/mito/solvers/backend/petsc/forward.h rename to lib/mito/matrix_solvers/backend/petsc/forward.h index 1a07ea09c..278b45cf2 100644 --- a/lib/mito/solvers/backend/petsc/forward.h +++ b/lib/mito/matrix_solvers/backend/petsc/forward.h @@ -7,7 +7,10 @@ #pragma once -namespace mito::solvers::petsc { +namespace mito::matrix_solvers::petsc { + + // class for PETSc linear system + class PETScLinearSystem; // class for PETSc Krylov solver class PETScKrylovSolver; diff --git a/lib/mito/solvers/backend/petsc/public.h b/lib/mito/matrix_solvers/backend/petsc/public.h similarity index 92% rename from lib/mito/solvers/backend/petsc/public.h rename to lib/mito/matrix_solvers/backend/petsc/public.h index b04e2251f..1e19efdc6 100644 --- a/lib/mito/solvers/backend/petsc/public.h +++ b/lib/mito/matrix_solvers/backend/petsc/public.h @@ -17,6 +17,7 @@ #include "api.h" // classes +#include "PETScLinearSystem.h" #include "PETScKrylovSolver.h" // factories implementation diff --git a/lib/mito/matrix_solvers/public.h b/lib/mito/matrix_solvers/public.h new file mode 100644 index 000000000..13c8ba2fa --- /dev/null +++ b/lib/mito/matrix_solvers/public.h @@ -0,0 +1,15 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +#ifdef WITH_PETSC +#include "backend/petsc.h" +#endif // WITH_PETSC + + +// end of file diff --git a/lib/mito/mesh/tetra.h b/lib/mito/mesh/tetra.h index f58c77272..680696f43 100644 --- a/lib/mito/mesh/tetra.h +++ b/lib/mito/mesh/tetra.h @@ -188,7 +188,7 @@ namespace mito::mesh { for (const auto & cell : mesh.cells()) { // get the nodes of the cell in the order dictated by the orientation - auto nodes = cell.nodes(); + const auto & nodes = cell.nodes(); // helper function to expand array to parameter pack constexpr auto _subdivide = diff --git a/lib/mito/public.h b/lib/mito/public.h index 363d52d32..7b5c879ae 100644 --- a/lib/mito/public.h +++ b/lib/mito/public.h @@ -11,7 +11,9 @@ #include "journal.h" #include "tensor.h" #include "functions.h" -#include "discretization.h" +#include "discrete.h" +#include "fem.h" +#include "constraints.h" #include "coordinates.h" #include "geometry.h" #include "io.h" @@ -24,6 +26,7 @@ #include "simulation.h" #include "topology.h" #include "utilities.h" +#include "matrix_solvers.h" #include "solvers.h" diff --git a/lib/mito/quadrature/Integrator.h b/lib/mito/quadrature/Integrator.h index dc1b0da1e..4490763cc 100644 --- a/lib/mito/quadrature/Integrator.h +++ b/lib/mito/quadrature/Integrator.h @@ -18,18 +18,18 @@ namespace mito::quadrature { // publish my template parameters using manifold_type = manifoldT; using cell_type = typename manifold_type::cell_type::simplex_type; + using reference_cell_type = typename manifold_type::cell_type::reference_simplex_type; using coordinates_type = typename manifold_type::coordinates_type; private: // quadrature_type, cell_type, and r identify a specific quadrature rule - using quadrature_rule_type = quadrature_rule_t; + using quadrature_rule_type = quadrature_rule_t; // the quadrature rule static constexpr auto _quadratureRule = quadrature_rule_type(); // the number of quadrature points static constexpr int Q = quadrature_rule_type::npoints; // the quadrature field type to store the coordinates of the quadrature points - using quadrature_field_type = - discretization::quadrature_field_t; + using quadrature_field_type = discrete::quadrature_field_t; private: template @@ -37,11 +37,13 @@ namespace mito::quadrature { { // loop on elements for (const auto & element : _manifold.elements()) { - // use manifold parametrization to map the position of quadrature points in - // the canonical element to the coordinate of the quadrature point + // use element parametrization and manifold's coordinate systemto map the position + // of quadrature points in the canonical element to the coordinate of the quadrature + // point _coordinates.insert( element.simplex(), - { _manifold.parametrization(element, _quadratureRule.point(q))... }); + { element.parametrization( + _quadratureRule.point(q), _manifold.coordinate_system())... }); } // all done @@ -58,7 +60,7 @@ namespace mito::quadrature { auto integrate(const fields::scalar_field_c auto & f) const -> tensor::scalar_t { - auto result = tensor::scalar_t(0.0); + auto result = tensor::scalar_t{ 0.0 }; // assemble elementary contributions for (const auto & cell : _manifold.elements()) { for (auto q = 0; q < Q; ++q) { diff --git a/lib/mito/quadrature/QuadratureTable.h b/lib/mito/quadrature/QuadratureTable.h index f8bb33be6..3646f491a 100644 --- a/lib/mito/quadrature/QuadratureTable.h +++ b/lib/mito/quadrature/QuadratureTable.h @@ -12,13 +12,10 @@ namespace mito::quadrature { // a table to store quadrature rules in terms of their point-weight pairings template class Table { - private: - // the dimension of the parametric space - static constexpr int parametricDim = manifolds::parametric_dim(); public: - // the type of quadrature points' parametric coordinates - using quadrature_point_type = manifolds::parametric_point_t; + // the type of quadrature points' barycentric coordinates + using quadrature_point_type = typename elementT::parametric_coordinates_type; // the type of quadrature weights using quadrature_weight_type = double; // the type of quadrature point-weight pairing diff --git a/lib/mito/quadrature/externals.h b/lib/mito/quadrature/externals.h index bbebce90e..04b3f0f1c 100644 --- a/lib/mito/quadrature/externals.h +++ b/lib/mito/quadrature/externals.h @@ -13,7 +13,8 @@ #include "../journal.h" #include "../manifolds.h" #include "../math.h" -#include "../discretization.h" +#include "../discrete.h" +#include "../geometry.h" // end of file diff --git a/lib/mito/quadrature/quadrature_tables_segments.icc b/lib/mito/quadrature/quadrature_tables_segments.icc index 399beb44d..f9f381291 100644 --- a/lib/mito/quadrature/quadrature_tables_segments.icc +++ b/lib/mito/quadrature/quadrature_tables_segments.icc @@ -10,21 +10,21 @@ namespace mito::quadrature { template <> - constexpr auto QuadratureTable() -> auto + constexpr auto QuadratureTable() -> auto { constexpr int Q = 1; - using table_type = Table; + using table_type = Table; using point_weight_pair_type = table_type::point_weight_pair_type; return table_type({ /*{point}, weight*/ - point_weight_pair_type({ 0.5, 0.5 }, 1.0) }); + point_weight_pair_type({ 0.5 }, 1.0) }); } template <> - constexpr auto QuadratureTable() -> auto + constexpr auto QuadratureTable() -> auto { constexpr int Q = 2; - using table_type = Table; + using table_type = Table; using point_weight_pair_type = table_type::point_weight_pair_type; constexpr auto a = 0.788675134594813; @@ -32,7 +32,7 @@ namespace mito::quadrature { return table_type( { /*{point}, weight*/ - point_weight_pair_type({ a, b }, 0.5), point_weight_pair_type({ b, a }, 0.5) }); + point_weight_pair_type({ a }, 0.5), point_weight_pair_type({ b }, 0.5) }); } } // namespace mito diff --git a/lib/mito/quadrature/quadrature_tables_tetrahedra.icc b/lib/mito/quadrature/quadrature_tables_tetrahedra.icc index 6e12b2f18..a5c160810 100644 --- a/lib/mito/quadrature/quadrature_tables_tetrahedra.icc +++ b/lib/mito/quadrature/quadrature_tables_tetrahedra.icc @@ -10,23 +10,22 @@ namespace mito::quadrature { template <> - constexpr auto QuadratureTable() -> auto + constexpr auto QuadratureTable() -> auto { constexpr int Q = 1; - using table_type = Table; + using table_type = Table; using point_weight_pair_type = table_type::point_weight_pair_type; /*Hammer rule*/ - return table_type( - { /*{point}, weight*/ - point_weight_pair_type({ 1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0 }, 1.0) }); + return table_type({ /*{point}, weight*/ + point_weight_pair_type({ 1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0 }, 1.0) }); } template <> - constexpr auto QuadratureTable() -> auto + constexpr auto QuadratureTable() -> auto { constexpr int Q = 4; - using table_type = Table; + using table_type = Table; using point_weight_pair_type = table_type::point_weight_pair_type; constexpr auto a = 0.5854101966249685; @@ -34,10 +33,10 @@ namespace mito::quadrature { /*Hammer rule*/ return table_type({ /*{point}, weight*/ - point_weight_pair_type({ a, b, b, b }, 1.0 / 4.0), - point_weight_pair_type({ b, a, b, b }, 1.0 / 4.0), - point_weight_pair_type({ b, b, a, b }, 1.0 / 4.0), - point_weight_pair_type({ b, b, b, a }, 1.0 / 4.0) }); + point_weight_pair_type({ a, b, b }, 1.0 / 4.0), + point_weight_pair_type({ b, a, b }, 1.0 / 4.0), + point_weight_pair_type({ b, b, a }, 1.0 / 4.0), + point_weight_pair_type({ b, b, b }, 1.0 / 4.0) }); } } // namespace mito diff --git a/lib/mito/quadrature/quadrature_tables_triangles.icc b/lib/mito/quadrature/quadrature_tables_triangles.icc index e985467e6..9ae97fa2c 100644 --- a/lib/mito/quadrature/quadrature_tables_triangles.icc +++ b/lib/mito/quadrature/quadrature_tables_triangles.icc @@ -10,35 +10,34 @@ namespace mito::quadrature { template <> - constexpr auto QuadratureTable() -> auto + constexpr auto QuadratureTable() -> auto { constexpr int Q = 1; - using table_type = Table; + using table_type = Table; using point_weight_pair_type = table_type::point_weight_pair_type; return table_type({ /*{point}, weight*/ - point_weight_pair_type({ 1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0 }, 1.0) }); + point_weight_pair_type({ 1.0 / 3.0, 1.0 / 3.0 }, 1.0) }); } template <> - constexpr auto QuadratureTable() -> auto + constexpr auto QuadratureTable() -> auto { constexpr int Q = 3; - using table_type = Table; + using table_type = Table; using point_weight_pair_type = table_type::point_weight_pair_type; - return table_type( - { /*{point}, weight*/ - point_weight_pair_type({ 2.0 / 3.0, 1.0 / 6.0, 1.0 / 6.0 }, 1.0 / 3.0), - point_weight_pair_type({ 1.0 / 6.0, 2.0 / 3.0, 1.0 / 6.0 }, 1.0 / 3.0), - point_weight_pair_type({ 1.0 / 6.0, 1.0 / 6.0, 2.0 / 3.0 }, 1.0 / 3.0) }); + return table_type({ /*{point}, weight*/ + point_weight_pair_type({ 2.0 / 3.0, 1.0 / 6.0 }, 1.0 / 3.0), + point_weight_pair_type({ 1.0 / 6.0, 2.0 / 3.0 }, 1.0 / 3.0), + point_weight_pair_type({ 1.0 / 6.0, 1.0 / 6.0 }, 1.0 / 3.0) }); } template <> - constexpr auto QuadratureTable() -> auto + constexpr auto QuadratureTable() -> auto { constexpr int Q = 6; - using table_type = Table; + using table_type = Table; using point_weight_pair_type = table_type::point_weight_pair_type; constexpr auto a = 0.16288285039589191090016180418490635; @@ -47,12 +46,34 @@ namespace mito::quadrature { constexpr auto w2 = 0.05218353089235368507981901063125638; return table_type({ /*{point}, weight*/ - point_weight_pair_type({ 1.0 - 2.0 * a, a, a }, w1), - point_weight_pair_type({ a, 1.0 - 2.0 * a, a }, w1), - point_weight_pair_type({ a, a, 1.0 - 2.0 * a }, w1), - point_weight_pair_type({ 1.0 - 2.0 * b, b, b }, w2), - point_weight_pair_type({ b, 1.0 - 2.0 * b, b }, w2), - point_weight_pair_type({ b, b, 1.0 - 2.0 * b }, w2) }); + point_weight_pair_type({ 1.0 - 2.0 * a, a }, w1), + point_weight_pair_type({ a, 1.0 - 2.0 * a }, w1), + point_weight_pair_type({ a, a }, w1), + point_weight_pair_type({ 1.0 - 2.0 * b, b }, w2), + point_weight_pair_type({ b, 1.0 - 2.0 * b }, w2), + point_weight_pair_type({ b, b }, w2) }); + } + + // Dunavant's Rule #4, Hammer–Stroud-type rule + template <> + constexpr auto QuadratureTable() -> auto + { + constexpr int Q = 6; + using table_type = Table; + using point_weight_pair_type = table_type::point_weight_pair_type; + + constexpr auto a = 0.816847572980458513080857; + constexpr auto b = 0.091576213509770743459571; + constexpr auto c = 0.108103018168070227363167; + constexpr auto d = 0.445948490915964886318329; + constexpr auto w1 = 0.109951743655321867638326; + constexpr auto w2 = 0.223381589678011465695007; + + return table_type( + { /*{point}, weight*/ + point_weight_pair_type({ a, b }, w1), point_weight_pair_type({ b, a }, w1), + point_weight_pair_type({ b, b }, w1), point_weight_pair_type({ c, d }, w2), + point_weight_pair_type({ d, c }, w2), point_weight_pair_type({ d, d }, w2) }); } } // namespace mito diff --git a/lib/mito/solvers/LinearSolver.h b/lib/mito/solvers/LinearSolver.h new file mode 100644 index 000000000..397f55a5d --- /dev/null +++ b/lib/mito/solvers/LinearSolver.h @@ -0,0 +1,74 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::solvers { + + template + // TODO: require that the {matrixSolverT} is compatible with the linear system in + // {discreteSystemT} + class LinearSolver { + + private: + // the discrete system type + using discrete_system_type = discreteSystemT; + // the matrix solver type + using matrix_solver_type = matrixSolverT; + // the options type + using options_type = std::string; + + public: + // the default constructor + constexpr LinearSolver(discrete_system_type & discrete_system) : + _discrete_system(discrete_system), + _matrix_solver(_discrete_system.linear_system()) + { + // create the matrix solver + _matrix_solver.create(); + } + + // destroy the matrix solver + auto destroy() -> void { return _matrix_solver.destroy(); } + + // set matrix solver options + auto set_options(const options_type & options) -> void + { + return _matrix_solver.set_options(options); + } + + // solve the matrix system + auto solve() -> void + { + // assemble the discrete system + _discrete_system.assemble(); + + // solve the linear system + _matrix_solver.solve(); + + // have the discrete system read the solution + _discrete_system.read_solution(); + + // all done + return; + } + + // print the matrix system + auto print() const -> void { return _matrix_solver.print(); } + + private: + // the discrete system + discrete_system_type & _discrete_system; + // the underlying matrix solver implementation + matrix_solver_type _matrix_solver; + }; + + +} // namespace mito::fem + + +// end of file diff --git a/lib/mito/solvers/api.h b/lib/mito/solvers/api.h new file mode 100644 index 000000000..24c38dfd2 --- /dev/null +++ b/lib/mito/solvers/api.h @@ -0,0 +1,22 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::solvers { + + // linear solver alias + template + using linear_solver_t = LinearSolver; + + // linear solver factory + template + constexpr auto linear_solver(discreteSystemT & discrete_system); +} + + +// end of file diff --git a/lib/mito/solvers/backend/petsc/PETScKrylovSolver.cc b/lib/mito/solvers/backend/petsc/PETScKrylovSolver.cc deleted file mode 100644 index 22f6cb538..000000000 --- a/lib/mito/solvers/backend/petsc/PETScKrylovSolver.cc +++ /dev/null @@ -1,250 +0,0 @@ -// -*- c++ -*- -// -// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved -// - - -#include "public.h" - - -// constructor -mito::solvers::petsc::PETScKrylovSolver::PETScKrylovSolver(const label_type & name) : - _options_prefix(name + "_"), - _initialized_petsc(false) -{} - -// destructor -mito::solvers::petsc::PETScKrylovSolver::~PETScKrylovSolver() {} - -// initialize PETSc if it has not been initialized before -auto -mito::solvers::petsc::PETScKrylovSolver::_initialize_petsc() -> void -{ - // check if PETSc has been initialized - PetscBool petsc_already_initialized; - PetscCallVoid(PetscInitialized(&petsc_already_initialized)); - - // if PETSc has not been initialized before - if (petsc_already_initialized == PETSC_FALSE) { - // initialize PETSc - PetscCallVoid(PetscInitializeNoArguments()); - // and remember that you have initialized PETSc - _initialized_petsc = true; - } - - // all done - return; -} - -// finalize PETSc if it has been initialized by this instance -auto -mito::solvers::petsc::PETScKrylovSolver::_finalize_petsc() -> void -{ - // if PETSc was initialized by this instance - if (_initialized_petsc) { - // finalize PETSc - PetscCallVoid(PetscFinalize()); - } - - // all done - return; -} - -auto -mito::solvers::petsc::PETScKrylovSolver::_create_linear_system(index_type size) -> void -{ - // create the vectors - PetscCallVoid(VecCreate(PETSC_COMM_WORLD, &_solution)); - PetscCallVoid(VecSetSizes(_solution, PETSC_DECIDE, size)); - PetscCallVoid(VecCreate(PETSC_COMM_WORLD, &_rhs)); - PetscCallVoid(VecSetSizes(_rhs, PETSC_DECIDE, size)); - - // create the matrix - PetscCallVoid(MatCreate(PETSC_COMM_WORLD, &_matrix)); - PetscCallVoid(MatSetSizes(_matrix, PETSC_DECIDE, PETSC_DECIDE, size, size)); - - // create the Krylov solver - PetscCallVoid(KSPCreate(PETSC_COMM_WORLD, &_ksp)); - PetscCallVoid(KSPSetOperators(_ksp, _matrix, _matrix)); - PetscCallVoid(KSPSetOptionsPrefix(_ksp, _options_prefix.c_str())); - - // set the default options (do not allow the user to control the options for matrix and vectors) - PetscCallVoid(MatSetFromOptions(_matrix)); - PetscCallVoid(VecSetFromOptions(_rhs)); - PetscCallVoid(VecSetFromOptions(_solution)); - - // all done - return; -} - -auto -mito::solvers::petsc::PETScKrylovSolver::_destroy_linear_system() -> void -{ - // destroy the matrix, right-hand side, solution, and Krylov solver - PetscCallVoid(MatDestroy(&_matrix)); - PetscCallVoid(VecDestroy(&_solution)); - PetscCallVoid(VecDestroy(&_rhs)); - PetscCallVoid(KSPDestroy(&_ksp)); - - // all done - return; -} - -// initialize the petsc solver -auto -mito::solvers::petsc::PETScKrylovSolver::initialize(index_type size) -> void -{ - // initialize PETSc if it has not been initialized before - _initialize_petsc(); - - // create the matrix, right-hand side, solution, and Krylov solver - _create_linear_system(size); - - // all done - return; -} - -// finalize the petsc solver -auto -mito::solvers::petsc::PETScKrylovSolver::finalize() -> void -{ - // destroy the memory for the matrix, right-hand side, solution, and Krylov solver - _destroy_linear_system(); - - // finalize petsc if it has been initialized by this instance - _finalize_petsc(); - - // all done - return; -} - -namespace { - // helper function to prepend the prefix {prefix} to each of the space-separated - // options leading with '-' - auto prepend_options_prefix(const std::string & options, const std::string & prefix) - -> std::string - { - // split the {options} string into words and store them in a vector - size_t pos_old = 0; - size_t pos_new = 0; - std::vector options_vector; - while ((pos_new = options.find(" ", pos_old)) != std::string::npos) { - std::string word_string = options.substr(pos_old, pos_new - pos_old); - options_vector.push_back(word_string); - pos_old = pos_new + 1; - } - std::string word_string = options.substr(pos_old, pos_new - pos_old); - options_vector.push_back(word_string); - - // for each word, if the leading character is '-', insert the {prefix} between '-' and the - // rest of the word - for (auto & word : options_vector) { - if (word[0] == '-') { - word.erase(0, 1); - word = "-" + prefix + word; - } - } - - // concatenate the words into a single string - std::string prefixed_options; - for (auto & word : options_vector) { - prefixed_options += word + " "; - } - - // all done - return prefixed_options; - } -} - -// set petsc options -auto -mito::solvers::petsc::PETScKrylovSolver::set_options(const options_type & options) -> void -{ - // prepend the prefix {_options_prefix} to each of the space-separated options in input - auto prefixed_options = prepend_options_prefix(options, _options_prefix); - - // record the options with PETSc - PetscCallVoid(PetscOptionsInsertString(PETSC_NULLPTR, prefixed_options.c_str())); - - // configure the Krylov solver with the options - PetscCallVoid(KSPSetFromOptions(_ksp)); - - // // show all options that have been set - // PetscOptionsView(PETSC_NULLPTR, PETSC_VIEWER_STDOUT_WORLD); - - // all done - return; -} - -// set the matrix entry at ({row}, {col}) to {value} -auto -mito::solvers::petsc::PETScKrylovSolver::insert_matrix_value( - index_type row, index_type col, const scalar_type & value) -> void -{ - // delegate to PETSc - PetscCallVoid(MatSetValue(_matrix, row, col, value, INSERT_VALUES)); - - // all done - return; -} - -// add {value} to matrix entry at ({row}, {col}) -auto -mito::solvers::petsc::PETScKrylovSolver::add_matrix_value( - index_type row, index_type col, const scalar_type & value) -> void -{ - // delegate to PETSc - PetscCallVoid(MatSetValue(_matrix, row, col, value, ADD_VALUES)); - - // all done - return; -} - -// set the right-hand side entry at {row} to {value} -auto -mito::solvers::petsc::PETScKrylovSolver::insert_rhs_value(index_type row, const scalar_type & value) - -> void -{ - // delegate to PETSc - PetscCallVoid(VecSetValue(_rhs, row, value, INSERT_VALUES)); - - // all done - return; -} - -// add {value} to right-hand side entry at {row} -auto -mito::solvers::petsc::PETScKrylovSolver::add_rhs_value(index_type row, const scalar_type & value) - -> void -{ - // delegate to PETSc - PetscCallVoid(VecSetValue(_rhs, row, value, ADD_VALUES)); - - // all done - return; -} - -// solve the linear system -auto -mito::solvers::petsc::PETScKrylovSolver::solve() -> void -{ - // assemble matrix - PetscCallVoid(MatAssemblyBegin(_matrix, MAT_FINAL_ASSEMBLY)); - PetscCallVoid(MatAssemblyEnd(_matrix, MAT_FINAL_ASSEMBLY)); - - // // show the matrix and the right-hand-side - // PetscCallVoid(MatView(_matrix, PETSC_VIEWER_STDOUT_WORLD)); - // PetscCallVoid(VecView(_rhs, PETSC_VIEWER_STDOUT_WORLD)); - - // solve the linear system - PetscCallVoid(KSPSolve(_ksp, _rhs, _solution)); - - // // show the solution - // PetscCallVoid(VecView(_solution, PETSC_VIEWER_STDOUT_WORLD)); - - // all done - return; -} - - -// end of file diff --git a/lib/mito/solvers/backend/petsc/factories.h b/lib/mito/solvers/backend/petsc/factories.h deleted file mode 100644 index 97f16969d..000000000 --- a/lib/mito/solvers/backend/petsc/factories.h +++ /dev/null @@ -1,21 +0,0 @@ -// -*- c++ -*- -// -// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved -// - -// code guard -#pragma once - - -namespace mito::solvers::petsc { - - // petsc ksp solver - auto ksp(const std::string & name) - { - return ksp_t(name); - } - -} - - -// end of file diff --git a/lib/mito/solvers/externals.h b/lib/mito/solvers/externals.h new file mode 100644 index 000000000..3b1a7812d --- /dev/null +++ b/lib/mito/solvers/externals.h @@ -0,0 +1,14 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +// support +#include "../journal.h" + + +// end of file diff --git a/lib/mito/solvers/factories.h b/lib/mito/solvers/factories.h new file mode 100644 index 000000000..048cac48f --- /dev/null +++ b/lib/mito/solvers/factories.h @@ -0,0 +1,22 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::solvers { + + // linear factory + template + constexpr auto linear_solver(discreteSystemT & discrete_system) + { + return linear_solver_t(discrete_system); + } + +} + + +// end of file diff --git a/lib/mito/solvers/forward.h b/lib/mito/solvers/forward.h new file mode 100644 index 000000000..9841e391d --- /dev/null +++ b/lib/mito/solvers/forward.h @@ -0,0 +1,18 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +// code guard +#pragma once + + +namespace mito::solvers { + + template + class LinearSolver; + +} + + +// end of file diff --git a/lib/mito/solvers/public.h b/lib/mito/solvers/public.h index aca9e6a46..4022c87af 100644 --- a/lib/mito/solvers/public.h +++ b/lib/mito/solvers/public.h @@ -7,9 +7,20 @@ #pragma once -#ifdef WITH_PETSC -#include "backend/petsc/public.h" -#endif // WITH_PETSC +// external packages +#include "externals.h" + +// get the forward declarations +#include "forward.h" + +// published type factories; this is the file you are looking for... +#include "api.h" + +// classes implementation +#include "LinearSolver.h" + +// factories implementation +#include "factories.h" // end of file diff --git a/lib/mito/tensor/api.h b/lib/mito/tensor/api.h index b5822d00f..45c1e602b 100644 --- a/lib/mito/tensor/api.h +++ b/lib/mito/tensor/api.h @@ -42,41 +42,41 @@ namespace mito::tensor { template constexpr auto e_2 = e<2, D>; - template - requires(D > 0) - constexpr auto e_00 = unit, 0, 0>; + template + requires(D1 > 0 && D2 > 0) + constexpr auto e_00 = unit, 0, 0>; - template - requires(D > 1) - constexpr auto e_01 = unit, 0, 1>; + template + requires(D1 > 0 && D2 > 1) + constexpr auto e_01 = unit, 0, 1>; - template - requires(D > 1) - constexpr auto e_10 = unit, 1, 0>; + template + requires(D1 > 1 && D2 > 0) + constexpr auto e_10 = unit, 1, 0>; - template - requires(D > 1) - constexpr auto e_11 = unit, 1, 1>; + template + requires(D1 > 1 && D2 > 1) + constexpr auto e_11 = unit, 1, 1>; - template - requires(D > 2) - constexpr auto e_02 = unit, 0, 2>; + template + requires(D1 > 0 && D2 > 2) + constexpr auto e_02 = unit, 0, 2>; - template - requires(D > 2) - constexpr auto e_20 = unit, 2, 0>; + template + requires(D1 > 2 && D2 > 0) + constexpr auto e_20 = unit, 2, 0>; - template - requires(D > 2) - constexpr auto e_12 = unit, 1, 2>; + template + requires(D1 > 1 && D2 > 2) + constexpr auto e_12 = unit, 1, 2>; - template - requires(D > 2) - constexpr auto e_21 = unit, 2, 1>; + template + requires(D1 > 2 && D2 > 1) + constexpr auto e_21 = unit, 2, 1>; - template - requires(D > 2) - constexpr auto e_22 = unit, 2, 2>; + template + requires(D1 > 2 && D2 > 2) + constexpr auto e_22 = unit, 2, 2>; template requires(D > 0) diff --git a/lib/mito/topology/utilities.h b/lib/mito/topology/utilities.h index 8f8ddd8fb..7ba29abe5 100644 --- a/lib/mito/topology/utilities.h +++ b/lib/mito/topology/utilities.h @@ -8,12 +8,6 @@ namespace mito::topology { - // order of simplex - template - constexpr auto order() -> int - { - return cellT::resource_type::order; - } // number of vertices of simplex template diff --git a/lib/mito/utilities/NamedClass.h b/lib/mito/utilities/NamedClass.h new file mode 100644 index 000000000..a7667c628 --- /dev/null +++ b/lib/mito/utilities/NamedClass.h @@ -0,0 +1,73 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + + +// code guard +#pragma once + + +namespace mito::utilities { + + // TODO: interface with {libuuid} to generate unique identifiers instead of "unnamed" + // + // Class {NamedClass} is meant to attach a string name identifier to a given class + // {classToName}. After implementing class {classToName}, you can define the alias + // {class_to_name_t} as follows: + // using class_to_name_t = NamedClass + // You can then create named instances of the class by using any of the {classToName} + // constructors (in which case, a default name will be used for the class) or by prepending the + // chosen instance name before the other constructor input arguments. + // + template + class NamedClass : public classToNameT { + // types + public: + // my template parameter + using class_to_name_type = classToNameT; + // me + using named_class_type = NamedClass; + // string identifier type + using name_type = std::string; + + // meta methods + public: + // default constructor + template + requires std::constructible_from + constexpr NamedClass(Args &&... args) : + class_to_name_type(std::forward(args)...), + _name("unnamed") + {} + + // named constructor + template + requires std::constructible_from + constexpr NamedClass(const name_type & name, Args &&... args) : + class_to_name_type(std::forward(args)...), + _name(name) + {} + + // destructor + constexpr ~NamedClass() = default; + + // copy constructor + constexpr NamedClass(const named_class_type &) = delete; + + // move constructor + constexpr NamedClass(named_class_type &&) = delete; + + public: + // accessor to name + constexpr auto name() const -> name_type { return _name; } + + private: + // string identifier + name_type _name; + }; + +} + + +// end of file diff --git a/lib/mito/utilities/SharedPointer.h b/lib/mito/utilities/SharedPointer.h index e1e1a9b16..44c22b69d 100644 --- a/lib/mito/utilities/SharedPointer.h +++ b/lib/mito/utilities/SharedPointer.h @@ -23,16 +23,16 @@ namespace mito::utilities { // interface public: // returns the id of this (oriented) simplex - inline auto id() const -> index_t; + constexpr auto id() const -> index_t; // accessor for the number of outstanding references - inline auto references() const -> int; + constexpr auto references() const -> int; // check if the handle is the null pointer - inline auto is_nullptr() const noexcept -> bool; + constexpr auto is_nullptr() const noexcept -> bool; // operator-> - auto operator->() const noexcept -> handle_type; + constexpr auto operator->() const noexcept -> handle_type; // // operator* // auto operator*() const -> const resource_type &; @@ -40,40 +40,40 @@ namespace mito::utilities { // meta methods public: // destructor - inline ~SharedPointer(); + constexpr ~SharedPointer(); // default constructor - inline SharedPointer(); + constexpr SharedPointer(); // constructor - inline SharedPointer(handle_type); + constexpr SharedPointer(handle_type); // copy constructor - inline SharedPointer(const shared_ptr_type &); + constexpr SharedPointer(const shared_ptr_type &); // move constructor - inline SharedPointer(shared_ptr_type &&) noexcept; + constexpr SharedPointer(shared_ptr_type &&) noexcept; // assignment operator - inline shared_ptr_type & operator=(const shared_ptr_type &); + constexpr shared_ptr_type & operator=(const shared_ptr_type &); // move assignment operator - inline shared_ptr_type & operator=(shared_ptr_type &&) noexcept; + constexpr shared_ptr_type & operator=(shared_ptr_type &&) noexcept; private: // accessor for {handle} - inline auto handle() const noexcept -> handle_type; + constexpr auto handle() const noexcept -> handle_type; // returns the resource corresponding to this resource id - static inline auto resource(index_t) -> handle_type; + static constexpr auto resource(index_t) -> handle_type; // reset the shared pointer - inline auto reset() -> void; + constexpr auto reset() -> void; // increment the reference count - inline auto _acquire() const -> void; + constexpr auto _acquire() const -> void; // decrement the reference count - inline auto _release() const -> void; + constexpr auto _release() const -> void; // data members private: diff --git a/lib/mito/utilities/SharedPointer.icc b/lib/mito/utilities/SharedPointer.icc index b2db41614..45f933311 100644 --- a/lib/mito/utilities/SharedPointer.icc +++ b/lib/mito/utilities/SharedPointer.icc @@ -9,7 +9,7 @@ #else template -auto +constexpr auto mito::utilities::SharedPointer::id() const -> index_t { // the id is the (immutable) address of this object @@ -17,7 +17,7 @@ mito::utilities::SharedPointer::id() const -> index_t } template -auto +constexpr auto mito::utilities::SharedPointer::resource(index_t index) -> handle_type { // the id is the (immutable) address of this object @@ -26,7 +26,7 @@ mito::utilities::SharedPointer::resource(index_t index) -> // interface template -auto +constexpr auto mito::utilities::SharedPointer::handle() const noexcept -> mito::utilities::SharedPointer::handle_type { @@ -35,7 +35,7 @@ mito::utilities::SharedPointer::handle() const noexcept } template -auto +constexpr auto mito::utilities::SharedPointer::references() const -> int { // return the count of outstanding references @@ -43,7 +43,7 @@ mito::utilities::SharedPointer::references() const -> int } template -auto +constexpr auto mito::utilities::SharedPointer::reset() -> void { // release my current handle @@ -57,7 +57,7 @@ mito::utilities::SharedPointer::reset() -> void } template -auto +constexpr auto mito::utilities::SharedPointer::is_nullptr() const noexcept -> bool { // all done @@ -66,7 +66,7 @@ mito::utilities::SharedPointer::is_nullptr() const noexcept -> bool // operator-> template -auto +constexpr auto mito::utilities::SharedPointer::operator->() const noexcept -> mito::utilities::SharedPointer::handle_type { @@ -86,7 +86,7 @@ mito::utilities::SharedPointer::operator->() const noexcept // destructor template -mito::utilities::SharedPointer::~SharedPointer() +constexpr mito::utilities::SharedPointer::~SharedPointer() { // release my current handle _release(); @@ -94,12 +94,13 @@ mito::utilities::SharedPointer::~SharedPointer() // default constructor (invalid shared handle) template -mito::utilities::SharedPointer::SharedPointer() : _handle(nullptr) +constexpr mito::utilities::SharedPointer::SharedPointer() : _handle(nullptr) {} // the constructor template -mito::utilities::SharedPointer::SharedPointer(handle_type resource) : _handle(resource) +constexpr mito::utilities::SharedPointer::SharedPointer(handle_type resource) : + _handle(resource) { // grab a reference to the shared handle _acquire(); @@ -107,7 +108,7 @@ mito::utilities::SharedPointer::SharedPointer(handle_type resource) : // the copy constructor template -mito::utilities::SharedPointer::SharedPointer( +constexpr mito::utilities::SharedPointer::SharedPointer( const mito::utilities::SharedPointer & other) : _handle(other._handle) { @@ -117,7 +118,7 @@ mito::utilities::SharedPointer::SharedPointer( // the move constructor template -mito::utilities::SharedPointer::SharedPointer( +constexpr mito::utilities::SharedPointer::SharedPointer( mito::utilities::SharedPointer && other) noexcept : _handle(std::move(other._handle)) { @@ -127,7 +128,7 @@ mito::utilities::SharedPointer::SharedPointer( // assignment operator template -mito::utilities::SharedPointer & +constexpr mito::utilities::SharedPointer & mito::utilities::SharedPointer::operator=( const mito::utilities::SharedPointer & other) { @@ -147,7 +148,7 @@ mito::utilities::SharedPointer::operator=( // move assignment operator template -mito::utilities::SharedPointer & +constexpr mito::utilities::SharedPointer & mito::utilities::SharedPointer::operator=( mito::utilities::SharedPointer && other) noexcept { @@ -168,7 +169,7 @@ mito::utilities::SharedPointer::operator=( // interface template -auto +constexpr auto mito::utilities::SharedPointer::_acquire() const -> void { // acquire resource (increment reference count) @@ -178,7 +179,7 @@ mito::utilities::SharedPointer::_acquire() const -> void } template -auto +constexpr auto mito::utilities::SharedPointer::_release() const -> void { // if the handle is in valid state diff --git a/lib/mito/utilities/StdSharedPointer.h b/lib/mito/utilities/StdSharedPointer.h index 97b3666a4..e4f7c94de 100644 --- a/lib/mito/utilities/StdSharedPointer.h +++ b/lib/mito/utilities/StdSharedPointer.h @@ -22,32 +22,32 @@ namespace mito::utilities { // interface public: // returns the id of this (oriented) simplex - inline auto id() const -> index_t; + constexpr auto id() const -> index_t; // operator-> - auto operator->() const noexcept -> handle_type; + constexpr auto operator->() const noexcept -> handle_type; // meta methods public: // constructor template requires(std::is_constructible_v) - inline StdSharedPointer(Args &&... args); + constexpr StdSharedPointer(Args &&... args); // destructor - inline ~StdSharedPointer() = default; + constexpr ~StdSharedPointer() = default; // copy constructor - inline StdSharedPointer(const shared_ptr_type &) = default; + constexpr StdSharedPointer(const shared_ptr_type &) = default; // move constructor - inline StdSharedPointer(shared_ptr_type &&) noexcept = default; + constexpr StdSharedPointer(shared_ptr_type &&) noexcept = default; // assignment operator - inline StdSharedPointer & operator=(const shared_ptr_type &) = default; + constexpr StdSharedPointer & operator=(const shared_ptr_type &) = default; // move assignment operator - inline StdSharedPointer & operator=(shared_ptr_type &&) noexcept = default; + constexpr StdSharedPointer & operator=(shared_ptr_type &&) noexcept = default; // data members private: @@ -55,14 +55,14 @@ namespace mito::utilities { }; template - inline bool operator==( + constexpr bool operator==( const StdSharedPointer & lhs, const StdSharedPointer & rhs) { return lhs.id() == rhs.id(); } template - inline auto operator<=>( + constexpr auto operator<=>( const StdSharedPointer & lhs, const StdSharedPointer & rhs) { // delegate to ids diff --git a/lib/mito/utilities/StdSharedPointer.icc b/lib/mito/utilities/StdSharedPointer.icc index dccfbcc6d..e5f4cc17f 100644 --- a/lib/mito/utilities/StdSharedPointer.icc +++ b/lib/mito/utilities/StdSharedPointer.icc @@ -9,7 +9,7 @@ #else template -auto +constexpr auto mito::utilities::StdSharedPointer::id() const -> index_t { // the id is the (immutable) address of this object @@ -19,13 +19,13 @@ mito::utilities::StdSharedPointer::id() const -> index_t template template requires(std::is_constructible_v) -mito::utilities::StdSharedPointer::StdSharedPointer(Args &&... args) : +constexpr mito::utilities::StdSharedPointer::StdSharedPointer(Args &&... args) : _ptr(std::make_shared(std::forward(args)...)) {} // operator-> template -auto +constexpr auto mito::utilities::StdSharedPointer::operator->() const noexcept -> mito::utilities::StdSharedPointer::handle_type { diff --git a/lib/mito/utilities/public.h b/lib/mito/utilities/public.h index af95c6dcc..9992a7fbf 100644 --- a/lib/mito/utilities/public.h +++ b/lib/mito/utilities/public.h @@ -27,6 +27,7 @@ #include "SegmentedVector.h" #include "Repository.h" #include "SegmentedContainerIterator.h" +#include "NamedClass.h" // factories implementation #include "factories.h" diff --git a/tests/mito.lib/constraints/dirichlet.cc b/tests/mito.lib/constraints/dirichlet.cc new file mode 100644 index 000000000..871ef62a6 --- /dev/null +++ b/tests/mito.lib/constraints/dirichlet.cc @@ -0,0 +1,60 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include +#include +#include +#include + + +// the type of coordinates +using coordinates_t = mito::geometry::coordinates_t<2, mito::geometry::CARTESIAN>; +// the type of cell +using cell_t = mito::geometry::triangle_t<2>; + + +// the function extracting the x component of a 2D vector +constexpr auto x = mito::functions::component; +// the function extracting the y component of a 2D vector +constexpr auto y = mito::functions::component; + + +TEST(Constraints, Dirichlet) +{ + // make a channel + journal::info_t channel("tests.dirichlet_constraints"); + + // the coordinate system + auto coord_system = mito::geometry::coordinate_system(); + + // read the mesh of a unit disk in 2D + std::ifstream fileStream("disk_cartesian.summit"); + auto mesh = mito::io::summit::reader(fileStream, coord_system); + + // the value of the Dirichlet boundary condition + auto bc_value = mito::fields::field(mito::functions::sqrt(x * x + y * y)); + + // get all the nodes on the mesh boundary + auto boundary_mesh = mito::mesh::boundary(mesh); + + // the Dirichlet boundary condition + auto constraints = mito::constraints::dirichlet_bc(boundary_mesh, bc_value); + + // loop on all the constrained nodes + for (const auto & cell : constraints.domain().cells()) { + // get all the nodes of the cell + auto nodes = cell.nodes(); + // loop on all the nodes of the cell + for (const auto & node : nodes) { + // get the position of the node + auto position = coord_system.coordinates(node->point()); + // expect that the value of the Dirichlet boundary condition is equal to the radius of + // the disk up to a reasonable tolerance + EXPECT_NEAR(bc_value(position), 1.0, 1.e-12); + } + } +} + +// end of file diff --git a/tests/mito.lib/discretization/nodal_field.cc b/tests/mito.lib/discrete/mesh_field.cc similarity index 82% rename from tests/mito.lib/discretization/nodal_field.cc rename to tests/mito.lib/discrete/mesh_field.cc index 2e58c5aae..5eee2910d 100644 --- a/tests/mito.lib/discretization/nodal_field.cc +++ b/tests/mito.lib/discrete/mesh_field.cc @@ -4,7 +4,7 @@ // #include -#include +#include #include #include @@ -22,8 +22,8 @@ TEST(Discretization, NodalFieldSphere) std::ifstream fileStream("sphere.summit"); auto mesh = mito::io::summit::reader>(fileStream, coord_system); - // a nodal field on the mesh - auto nodal_field = mito::discretization::nodal_field>(mesh, "normal"); + // a mesh field on the mesh + auto mesh_field = mito::discrete::mesh_field>(mesh, "normal"); // the normal field to the submanifold constexpr auto normal_field = mito::fields::field([](const coordinates_t & x) -> auto { @@ -34,8 +34,8 @@ TEST(Discretization, NodalFieldSphere) + std::cos(theta) * mito::tensor::e_2<3>; }); - // fill information in nodal field - for (auto & [node, value] : nodal_field) { + // fill information in mesh field + for (auto & [node, value] : mesh_field) { // get the coordinates of the node auto & coordinates = coord_system.coordinates(node->point()); // compute the value of the normal field at those coordinates @@ -45,8 +45,8 @@ TEST(Discretization, NodalFieldSphere) #ifdef WITH_VTK // write mesh to vtk file auto writer = mito::io::vtk::field_writer("sphere_mesh_field", mesh, coord_system); - // sign {nodal_field} up with the writer - writer.record(nodal_field); + // sign {mesh_field} up with the writer + writer.record(mesh_field); // write output file writer.write(); #endif diff --git a/tests/mito.lib/discretization/quadrature_field.cc b/tests/mito.lib/discrete/quadrature_field.cc similarity index 92% rename from tests/mito.lib/discretization/quadrature_field.cc rename to tests/mito.lib/discrete/quadrature_field.cc index 696292f80..d76880dc7 100644 --- a/tests/mito.lib/discretization/quadrature_field.cc +++ b/tests/mito.lib/discrete/quadrature_field.cc @@ -4,7 +4,7 @@ // #include -#include +#include // pick a cell type @@ -14,7 +14,7 @@ constexpr int Q = 1; // a type for the field using vector_type = mito::tensor::vector_t<2>; // assemble the quadrature field -using quadrature_field_type = mito::discretization::quadrature_field_t; +using quadrature_field_type = mito::discrete::quadrature_field_t; // an empty topology auto & topology = mito::topology::topology(); diff --git a/tests/mito.lib/fem/block_grad_grad.cc b/tests/mito.lib/fem/block_grad_grad.cc new file mode 100644 index 000000000..210e9b5db --- /dev/null +++ b/tests/mito.lib/fem/block_grad_grad.cc @@ -0,0 +1,117 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include +#include + + +// the type of coordinates +using coordinates_t = mito::geometry::coordinates_t<2, mito::geometry::CARTESIAN>; +// the type of coordinate system +using coord_system_t = mito::geometry::coordinate_system_t; +// the type of discretization node +using discretization_node_t = mito::discrete::discretization_node_t; +// the type of cell +using cell_t = mito::geometry::triangle_t<2>; +// the reference simplex +using reference_simplex_t = mito::geometry::reference_triangle_t; +// Gauss quadrature on triangles with degree of exactness 4 +using quadrature_rule_t = + mito::quadrature::quadrature_rule_t; + +// instantiate the quadrature rule +constexpr auto quadrature_rule = quadrature_rule_t(); + + +TEST(Fem, IsoparametricTriangle) +{ + // the coordinate system + auto coord_system = coord_system_t(); + + // build nodes + auto node_0 = mito::geometry::node(coord_system, { 0.0, 0.0 }); + auto node_1 = mito::geometry::node(coord_system, { 1.0, 0.0 }); + auto node_2 = mito::geometry::node(coord_system, { 0.0, 1.0 }); + + // make a geometric simplex + auto geometric_simplex = mito::geometry::triangle<2>({ node_0, node_1, node_2 }); + + { + // first order isoparametric triangle + using element_p1_t = mito::fem::isoparametric_simplex_t<1, cell_t>; + + // build the discretization nodes + auto discretization_node_0 = discretization_node_t(); + auto discretization_node_1 = discretization_node_t(); + auto discretization_node_2 = discretization_node_t(); + + // a finite element + auto element_p1 = element_p1_t( + geometric_simplex, coord_system, + { discretization_node_0, discretization_node_1, discretization_node_2 }); + + // a grad-grad matrix block + auto grad_grad_block = + mito::fem::blocks::grad_grad_block(); + + // the analytical elementary stiffness matrix + auto analytical_block = 1.0 / 2.0 * mito::tensor::matrix_t<3>{ 2.0, -1.0, -1.0, -1.0, 1.0, + 0.0, -1.0, 0.0, 1.0 }; + + // compute the elementary contribution of the block + auto computed_block = grad_grad_block.compute(element_p1); + + // compute the error + auto error = mito::tensor::norm(computed_block - analytical_block); + + // check the error is zero to machine precision + EXPECT_DOUBLE_EQ(0.0, error); + } + + { + // second order isoparametric triangle + using element_p2_t = mito::fem::isoparametric_simplex_t<2, cell_t>; + + // build the discretization nodes + auto discretization_node_0 = discretization_node_t(); + auto discretization_node_1 = discretization_node_t(); + auto discretization_node_2 = discretization_node_t(); + auto discretization_node_3 = discretization_node_t(); + auto discretization_node_4 = discretization_node_t(); + auto discretization_node_5 = discretization_node_t(); + + // a finite element + auto element_p2 = element_p2_t( + geometric_simplex, coord_system, + { discretization_node_0, discretization_node_1, discretization_node_2, + discretization_node_3, discretization_node_4, discretization_node_5 }); + + // a grad-grad matrix block + auto grad_grad_block = + mito::fem::blocks::grad_grad_block(); + + // the analytical elementary stiffness matrix + auto analytical_block = mito::tensor::matrix_t<6>{ + 1.0, 1.0 / 6.0, 1.0 / 6.0, -2.0 / 3.0, 0.0, -2.0 / 3.0, + 1.0 / 6.0, 1.0 / 2.0, 0.0, -2.0 / 3.0, 0.0, 0.0, + 1.0 / 6.0, 0.0, 1.0 / 2.0, 0.0, 0.0, -2.0 / 3.0, + -2.0 / 3.0, -2.0 / 3.0, 0.0, 8.0 / 3.0, -4.0 / 3.0, 0.0, + 0.0, 0.0, 0.0, -4.0 / 3.0, 8.0 / 3.0, -4.0 / 3.0, + -2.0 / 3.0, 0.0, -2.0 / 3.0, 0.0, -4.0 / 3.0, 8.0 / 3.0 + }; + + // compute the elementary contribution of the block + auto computed_block = grad_grad_block.compute(element_p2); + + // compute the error + auto error = mito::tensor::norm(computed_block - analytical_block); + + // check the error is reasonable + EXPECT_NEAR(0.0, error, 1.e-15); + } + + // all done + return; +} \ No newline at end of file diff --git a/tests/mito.lib/fem/block_mass.cc b/tests/mito.lib/fem/block_mass.cc new file mode 100644 index 000000000..a0577cc89 --- /dev/null +++ b/tests/mito.lib/fem/block_mass.cc @@ -0,0 +1,115 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include +#include + + +// the type of coordinates +using coordinates_t = mito::geometry::coordinates_t<2, mito::geometry::CARTESIAN>; +// the type of coordinate system +using coord_system_t = mito::geometry::coordinate_system_t; +// the type of discretization node +using discretization_node_t = mito::discrete::discretization_node_t; +// the type of cell +using cell_t = mito::geometry::triangle_t<2>; +// the reference simplex +using reference_simplex_t = mito::geometry::reference_triangle_t; +// Gauss quadrature on triangles with degree of exactness 4 +using quadrature_rule_t = + mito::quadrature::quadrature_rule_t; + +// instantiate the quadrature rule +constexpr auto quadrature_rule = quadrature_rule_t(); + + +TEST(Fem, IsoparametricTriangle) +{ + // the coordinate system + auto coord_system = coord_system_t(); + + // build nodes + auto node_0 = mito::geometry::node(coord_system, { 0.0, 0.0 }); + auto node_1 = mito::geometry::node(coord_system, { 1.0, 0.0 }); + auto node_2 = mito::geometry::node(coord_system, { 0.0, 1.0 }); + + // make a geometric simplex + auto geometric_simplex = mito::geometry::triangle<2>({ node_0, node_1, node_2 }); + + { + // first order isoparametric triangle + using element_p1_t = mito::fem::isoparametric_simplex_t<1, cell_t>; + + // build the discretization nodes + auto discretization_node_0 = discretization_node_t(); + auto discretization_node_1 = discretization_node_t(); + auto discretization_node_2 = discretization_node_t(); + + // a finite element + auto element_p1 = element_p1_t( + geometric_simplex, coord_system, + { discretization_node_0, discretization_node_1, discretization_node_2 }); + + // a mass matrix block + auto mass_block = mito::fem::blocks::mass_block(); + + // the analytical elementary mass matrix + auto analytical_block = + 1.0 / 24.0 * mito::tensor::matrix_t<3>{ 2.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0, 1.0, 2.0 }; + + // compute the elementary contribution of the block + auto computed_block = mass_block.compute(element_p1); + + // compute the error + auto error = mito::tensor::norm(computed_block - analytical_block); + + // check the error is reasonable + EXPECT_NEAR(0.0, error, 1.e-16); + } + + { + // second order isoparametric triangle + using element_p2_t = mito::fem::isoparametric_simplex_t<2, cell_t>; + + // build the discretization nodes + auto discretization_node_0 = discretization_node_t(); + auto discretization_node_1 = discretization_node_t(); + auto discretization_node_2 = discretization_node_t(); + auto discretization_node_3 = discretization_node_t(); + auto discretization_node_4 = discretization_node_t(); + auto discretization_node_5 = discretization_node_t(); + + // a finite element + auto element_p2 = element_p2_t( + geometric_simplex, coord_system, + { discretization_node_0, discretization_node_1, discretization_node_2, + discretization_node_3, discretization_node_4, discretization_node_5 }); + + // a mass matrix block + auto mass_block = mito::fem::blocks::mass_block(); + + // the analytical elementary mass matrix + auto analytical_block = mito::tensor::matrix_t<6>{ + 1.0 / 60.0, -1.0 / 360.0, -1.0 / 360.0, 0.0, -1.0 / 90.0, 0.0, + -1.0 / 360.0, 1.0 / 60.0, -1.0 / 360.0, 0.0, 0.0, -1.0 / 90.0, + -1.0 / 360.0, -1.0 / 360.0, 1.0 / 60.0, -1.0 / 90.0, 0.0, 0.0, + 0.0, 0.0, -1.0 / 90.0, 4.0 / 45.0, 2.0 / 45.0, 2.0 / 45.0, + -1.0 / 90.0, 0.0, 0.0, 2.0 / 45.0, 4.0 / 45.0, 2.0 / 45.0, + 0.0, -1.0 / 90.0, 0.0, 2.0 / 45.0, 2.0 / 45.0, 4.0 / 45.0 + }; + + // compute the elementary contribution of the block + auto computed_block = mass_block.compute(element_p2); + + // compute the error + auto error = mito::tensor::norm(computed_block - analytical_block); + + // check the error is reasonable + EXPECT_NEAR(0.0, error, 1.e-16); + } + + // all done + return; +} \ No newline at end of file diff --git a/tests/mito.lib/fem/isoparametric_triangle.cc b/tests/mito.lib/fem/isoparametric_triangle.cc new file mode 100644 index 000000000..8baba4991 --- /dev/null +++ b/tests/mito.lib/fem/isoparametric_triangle.cc @@ -0,0 +1,152 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include +#include + + +// the type of coordinates +using coordinates_t = mito::geometry::coordinates_t<2, mito::geometry::CARTESIAN>; +// the type of coordinate system +using coord_system_t = mito::geometry::coordinate_system_t; +// the type of discretization node +using discretization_node_t = mito::discrete::discretization_node_t; +// the type of cell +using cell_t = mito::geometry::triangle_t<2>; +// the reference simplex +using reference_simplex_t = mito::geometry::reference_triangle_t; +// Gauss quadrature on triangles with degree of exactness 4 +using quadrature_rule_t = + mito::quadrature::quadrature_rule_t; + + +// instantiate the quadrature rule +constexpr auto quadrature_rule = quadrature_rule_t(); + + +// test that all shape functions sum to 1.0 at any quadrature point +auto +test_partition_of_unity(const auto & element) +{ + // the number of quadrature points per element + constexpr int n_quads = quadrature_rule_t::npoints; + + // the number of nodes per element + constexpr int n_nodes = mito::utilities::base_type::n_nodes; + + // loop on the quadrature points + mito::tensor::constexpr_for_1([&]() { + // the barycentric coordinates of the quadrature point + constexpr auto xi = quadrature_rule.point(q); + + // compute the sum of the shape functions at {xi} for all nodes + constexpr auto sum = + ([]( + const auto & element, const auto & xi, mito::tensor::integer_sequence) { + return ((element.template shape()(xi)) + ...); + })(element, xi, mito::tensor::make_integer_sequence{}); + + // check the sum of the shape functions + static_assert(1.0 == sum); + }); + + // all done + return; +} + +// test that the gradients of all shape functions sum to 0.0 at any quadrature point +auto +test_gradient_consistency(const auto & element) +{ + // the number of quadrature points per element + constexpr int n_quads = quadrature_rule_t::npoints; + + // the number of nodes per element + constexpr int n_nodes = mito::utilities::base_type::n_nodes; + + // loop on the quadrature points + mito::tensor::constexpr_for_1([&]() { + // the barycentric coordinates of the quadrature point + constexpr auto xi = quadrature_rule.point(q); + + // compute the sum of the shape functions at {xi} for all nodes + auto sum = + ([]( + const auto & element, const auto & xi, mito::tensor::integer_sequence) { + return ((element.template gradient()(xi)) + ...); + })(element, xi, mito::tensor::make_integer_sequence{}); + + // check the sum of the shape functions gradients + EXPECT_NEAR(0.0, sum[0], 3.0e-16); + EXPECT_NEAR(0.0, sum[1], 3.0e-16); + }); + + // all done + return; +} + + +TEST(Fem, IsoparametricTriangle) +{ + // the coordinate system + auto coord_system = coord_system_t(); + + // build nodes + auto node_0 = mito::geometry::node(coord_system, { 0.0, 0.0 }); + auto node_1 = mito::geometry::node(coord_system, { 1.0, 0.0 }); + auto node_2 = mito::geometry::node(coord_system, { 0.0, 1.0 }); + + // make a geometric simplex + auto geometric_simplex = mito::geometry::triangle<2>({ node_0, node_1, node_2 }); + + { + // first order isoparametric triangle + using element_p1_t = mito::fem::isoparametric_simplex_t<1, cell_t>; + + // build the discretization nodes + auto discretization_node_0 = discretization_node_t(); + auto discretization_node_1 = discretization_node_t(); + auto discretization_node_2 = discretization_node_t(); + + // a finite element + auto element_p1 = element_p1_t( + geometric_simplex, coord_system, + { discretization_node_0, discretization_node_1, discretization_node_2 }); + + // check that first order shape functions are a partition of unity + test_partition_of_unity(element_p1); + + // check that the gradients of first order shape functions sum to 0.0 + test_gradient_consistency(element_p1); + } + + { + // second order isoparametric triangle + using element_p2_t = mito::fem::isoparametric_simplex_t<2, cell_t>; + + // build the discretization nodes + auto discretization_node_0 = discretization_node_t(); + auto discretization_node_1 = discretization_node_t(); + auto discretization_node_2 = discretization_node_t(); + auto discretization_node_3 = discretization_node_t(); + auto discretization_node_4 = discretization_node_t(); + auto discretization_node_5 = discretization_node_t(); + + // a finite element + auto element_p2 = element_p2_t( + geometric_simplex, coord_system, + { discretization_node_0, discretization_node_1, discretization_node_2, + discretization_node_3, discretization_node_4, discretization_node_5 }); + + // check that second order shape functions are a partition of unity + test_partition_of_unity(element_p2); + + // check that the gradients of second order shape functions sum to 0.0 + test_gradient_consistency(element_p2); + } + + // all done + return; +} \ No newline at end of file diff --git a/tests/mito.lib/fem/shape_functions_triangle_construction.cc b/tests/mito.lib/fem/shape_functions_triangle_construction.cc new file mode 100644 index 000000000..4054f0277 --- /dev/null +++ b/tests/mito.lib/fem/shape_functions_triangle_construction.cc @@ -0,0 +1,125 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include +#include + + +// the parametric coordinates +using parametric_coordinates_type = mito::tensor::vector_t<2>; +constexpr auto xi_0 = mito::functions::component; +constexpr auto xi_1 = mito::functions::component; + +// the barycentric coordinates type +using barycentric_coordinates_type = mito::tensor::vector_t<3>; +constexpr auto eta_0 = mito::functions::component; +constexpr auto eta_1 = mito::functions::component; +constexpr auto eta_2 = mito::functions::component; + +// the conversion from parametric to barycentric coordinates +constexpr auto parametric_to_barycentric = xi_0 * mito::tensor::e_0<3> + xi_1 * mito::tensor::e_1<3> + + (1.0 - xi_0 - xi_1) * mito::tensor::e_2<3>; + +// the conversion from barycentric to parametric coordinates +constexpr auto barycentric_to_parametric = + eta_0 * mito::tensor::e_0<2> + eta_1 * mito::tensor::e_1<2>; + + +TEST(Fem, ShapeTriangleConstuctionP1) +{ + // create a channel + journal::info_t channel("tests.shape_triangle_construction_p1"); + + // linear shape functions on the reference triangle in barycentric coordinates + constexpr auto phi_0 = eta_0; + constexpr auto phi_1 = eta_1; + constexpr auto phi_2 = eta_2; + + // hard-coded derivatives of shape functions with respect to parametric coordinates + constexpr auto dphi_0 = + mito::functions::constant(mito::tensor::e_0<2>); + constexpr auto dphi_1 = + mito::functions::constant(mito::tensor::e_1<2>); + constexpr auto dphi_2 = mito::functions::constant( + -mito::tensor::e_0<2> - mito::tensor::e_1<2>); + + // assemble the shape functions gradients as the partial derivatives of the shape functions with + // respect to the parametric coordinates, seen as functions of barycentric coordinates + constexpr auto dN0 = mito::functions::derivative(phi_0(parametric_to_barycentric))( + barycentric_to_parametric); + constexpr auto dN1 = mito::functions::derivative(phi_1(parametric_to_barycentric))( + barycentric_to_parametric); + constexpr auto dN2 = mito::functions::derivative(phi_2(parametric_to_barycentric))( + barycentric_to_parametric); + + // barycenter in barycentric coordinates + constexpr auto barycenter = barycentric_coordinates_type{ 1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0 }; + + // check result + static_assert(mito::tensor::row<0>(dN0(barycenter)) == dphi_0(barycenter)); + static_assert(mito::tensor::row<0>(dN1(barycenter)) == dphi_1(barycenter)); + static_assert(mito::tensor::row<0>(dN2(barycenter)) == dphi_2(barycenter)); + + // all done + return; +} + + +TEST(Fem, ShapeTriangleConstuctionP2) +{ + // create a channel + journal::info_t channel("tests.shape_triangle_construction_p2"); + + // quadratic shape functions on the reference triangle in barycentric coordinates + constexpr auto phi_3 = 4.0 * eta_0 * eta_1; + constexpr auto phi_4 = 4.0 * eta_1 * eta_2; + constexpr auto phi_5 = 4.0 * eta_0 * eta_2; + constexpr auto phi_0 = eta_0 - 0.5 * phi_5 - 0.5 * phi_3; + constexpr auto phi_1 = eta_1 - 0.5 * phi_3 - 0.5 * phi_4; + constexpr auto phi_2 = eta_2 - 0.5 * phi_5 - 0.5 * phi_4; + + // hard-coded derivatives of shape functions with respect to parametric coordinates + constexpr auto dphi_0 = mito::tensor::e_0<2> * (1.0 - 2.0 * eta_1 - 2.0 * eta_2 + 2.0 * eta_0); + constexpr auto dphi_1 = mito::tensor::e_1<2> * (1.0 - 2.0 * eta_0 - 2.0 * eta_2 + 2.0 * eta_1); + constexpr auto dphi_2 = (mito::tensor::e_0<2> + mito::tensor::e_1<2>) *( + -1.0 - 2.0 * eta_2 + 2.0 * eta_0 + 2.0 * eta_1); + constexpr auto dphi_3 = + mito::tensor::e_0<2> * (4.0 * eta_1) + mito::tensor::e_1<2> * (4.0 * eta_0); + constexpr auto dphi_4 = + mito::tensor::e_0<2> * (-4.0 * eta_1) + mito::tensor::e_1<2> * (4.0 * eta_2 - 4.0 * eta_1); + constexpr auto dphi_5 = + mito::tensor::e_0<2> * (4.0 * eta_2 - 4.0 * eta_0) + mito::tensor::e_1<2> * (-4.0 * eta_0); + + // assemble the shape functions gradients as the partial derivatives of the shape functions with + // respect to the parametric coordinates, seen as functions of barycentric coordinates + constexpr auto dN0 = mito::functions::derivative(phi_0(parametric_to_barycentric))( + barycentric_to_parametric); + constexpr auto dN1 = mito::functions::derivative(phi_1(parametric_to_barycentric))( + barycentric_to_parametric); + constexpr auto dN2 = mito::functions::derivative(phi_2(parametric_to_barycentric))( + barycentric_to_parametric); + constexpr auto dN3 = mito::functions::derivative(phi_3(parametric_to_barycentric))( + barycentric_to_parametric); + constexpr auto dN4 = mito::functions::derivative(phi_4(parametric_to_barycentric))( + barycentric_to_parametric); + constexpr auto dN5 = mito::functions::derivative(phi_5(parametric_to_barycentric))( + barycentric_to_parametric); + + // barycenter in barycentric coordinates + constexpr auto barycenter = barycentric_coordinates_type{ 1.0, 0.0, 0.0 }; + + // check result + static_assert(mito::tensor::row<0>(dN0(barycenter)) == dphi_0(barycenter)); + static_assert(mito::tensor::row<0>(dN1(barycenter)) == dphi_1(barycenter)); + static_assert(mito::tensor::row<0>(dN2(barycenter)) == dphi_2(barycenter)); + static_assert(mito::tensor::row<0>(dN3(barycenter)) == dphi_3(barycenter)); + static_assert(mito::tensor::row<0>(dN4(barycenter)) == dphi_4(barycenter)); + static_assert(mito::tensor::row<0>(dN5(barycenter)) == dphi_5(barycenter)); + + // all done + return; +} + +// end of file \ No newline at end of file diff --git a/tests/mito.lib/fem/shape_functions_triangle_p1.cc b/tests/mito.lib/fem/shape_functions_triangle_p1.cc new file mode 100644 index 000000000..3253908d0 --- /dev/null +++ b/tests/mito.lib/fem/shape_functions_triangle_p1.cc @@ -0,0 +1,57 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include +#include + + +// first order shape functions type +using shape_t = mito::fem::ShapeTriangleP1; +// the barycentric coordinates type +using parametric_coordinates_type = shape_t::reference_element_type::parametric_coordinates_type; + + +TEST(Fem, ShapeTriangleP1) +{ + // first order shape functions + constexpr auto element = shape_t(); + + // node 0 in parametric coordinates + constexpr auto n0 = parametric_coordinates_type{ 1.0, 0.0 }; + // node 1 in parametric coordinates + constexpr auto n1 = parametric_coordinates_type{ 0.0, 1.0 }; + // node 2 in parametric coordinates + constexpr auto n2 = parametric_coordinates_type{ 0.0, 0.0 }; + + // the shape function associated with local node {0} + constexpr auto phi_0 = element.shape<0>(); + // check that the shape function at node 0 is 1.0 + static_assert(1.0 == phi_0(n0)); + // check that the shape function at node 1 is 0.0 + static_assert(0.0 == phi_0(n1)); + // check that the shape function at node 2 is 0.0 + static_assert(0.0 == phi_0(n2)); + + // the shape functions at node 1 + constexpr auto phi_1 = element.shape<1>(); + // check that the shape function at node 0 is 0.0 + static_assert(0.0 == phi_1(n0)); + // check that the shape function at node 1 is 1.0 + static_assert(1.0 == phi_1(n1)); + // check that the shape function at node 2 is 0.0 + static_assert(0.0 == phi_1(n2)); + + // the shape functions at node 2 + constexpr auto phi_2 = element.shape<2>(); + // check that the shape function at node 0 is 0.0 + static_assert(0.0 == phi_2(n0)); + // check that the shape function at node 1 is 0.0 + static_assert(0.0 == phi_2(n1)); + // check that the shape function at node 2 is 1.0 + static_assert(1.0 == phi_2(n2)); + + // all done + return; +} \ No newline at end of file diff --git a/tests/mito.lib/fem/shape_functions_triangle_p2.cc b/tests/mito.lib/fem/shape_functions_triangle_p2.cc new file mode 100644 index 000000000..a318571f2 --- /dev/null +++ b/tests/mito.lib/fem/shape_functions_triangle_p2.cc @@ -0,0 +1,126 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include +#include + + +// second order shape functions type +using shape_t = mito::fem::ShapeTriangleP2; +// the barycentric coordinates type +using barycentric_coordinates_t = shape_t::reference_element_type::parametric_coordinates_type; + + +TEST(Fem, ShapeTriangleP2) +{ + // second order shape functions + constexpr auto element = shape_t(); + + // node 0 in barycentric coordinates + constexpr auto n0 = barycentric_coordinates_t{ 1.0, 0.0 }; + // node 1 in barycentric coordinates + constexpr auto n1 = barycentric_coordinates_t{ 0.0, 1.0 }; + // node 2 in barycentric coordinates + constexpr auto n2 = barycentric_coordinates_t{ 0.0, 0.0 }; + // node 3 in barycentric coordinates + constexpr auto n3 = barycentric_coordinates_t{ 0.5, 0.5 }; + // node 4 in barycentric coordinates + constexpr auto n4 = barycentric_coordinates_t{ 0.0, 0.5 }; + // node 5 in barycentric coordinates + constexpr auto n5 = barycentric_coordinates_t{ 0.5, 0.0 }; + + // the shape functions at node 0 + constexpr auto phi_0 = element.shape<0>(); + // check that the shape function at node 0 is 1.0 + static_assert(1.0 == phi_0(n0)); + // check that the shape function at node 1 is 0.0 + static_assert(0.0 == phi_0(n1)); + // check that the shape function at node 2 is 0.0 + static_assert(0.0 == phi_0(n2)); + // check that the shape function at node 3 is 0.0 + static_assert(0.0 == phi_0(n3)); + // check that the shape function at node 4 is 0.0 + static_assert(0.0 == phi_0(n4)); + // check that the shape function at node 5 is 0.0 + static_assert(0.0 == phi_0(n5)); + + // the shape functions at node 1 + constexpr auto phi_1 = element.shape<1>(); + // check that the shape function at node 0 is 0.0 + static_assert(0.0 == phi_1(n0)); + // check that the shape function at node 1 is 1.0 + static_assert(1.0 == phi_1(n1)); + // check that the shape function at node 2 is 0.0 + static_assert(0.0 == phi_1(n2)); + // check that the shape function at node 3 is 0.0 + static_assert(0.0 == phi_1(n3)); + // check that the shape function at node 4 is 0.0 + static_assert(0.0 == phi_1(n4)); + // check that the shape function at node 5 is 0.0 + static_assert(0.0 == phi_1(n5)); + + // the shape functions at node 2 + constexpr auto phi_2 = element.shape<2>(); + // check that the shape function at node 0 is 0.0 + static_assert(0.0 == phi_2(n0)); + // check that the shape function at node 1 is 0.0 + static_assert(0.0 == phi_2(n1)); + // check that the shape function at node 2 is 1.0 + static_assert(1.0 == phi_2(n2)); + // check that the shape function at node 3 is 0.0 + static_assert(0.0 == phi_2(n3)); + // check that the shape function at node 4 is 0.0 + static_assert(0.0 == phi_2(n4)); + // check that the shape function at node 5 is 0.0 + static_assert(0.0 == phi_2(n5)); + + // the shape functions at node 3 + constexpr auto phi_3 = element.shape<3>(); + // check that the shape function at node 0 is 0.0 + static_assert(0.0 == phi_3(n0)); + // check that the shape function at node 1 is 0.0 + static_assert(0.0 == phi_3(n1)); + // check that the shape function at node 2 is 0.0 + static_assert(0.0 == phi_3(n2)); + // check that the shape function at node 3 is 1.0 + static_assert(1.0 == phi_3(n3)); + // check that the shape function at node 4 is 0.0 + static_assert(0.0 == phi_3(n4)); + // check that the shape function at node 5 is 0.0 + static_assert(0.0 == phi_3(n5)); + + // the shape functions at node 4 + constexpr auto phi_4 = element.shape<4>(); + // check that the shape function at node 0 is 0.0 + static_assert(0.0 == phi_4(n0)); + // check that the shape function at node 1 is 0.0 + static_assert(0.0 == phi_4(n1)); + // check that the shape function at node 2 is 0.0 + static_assert(0.0 == phi_4(n2)); + // check that the shape function at node 3 is 0.0 + static_assert(0.0 == phi_4(n3)); + // check that the shape function at node 4 is 1.0 + static_assert(1.0 == phi_4(n4)); + // check that the shape function at node 5 is 0.0 + static_assert(0.0 == phi_4(n5)); + + // the shape functions at node 5 + constexpr auto phi_5 = element.shape<5>(); + // check that the shape function at node 0 is 0.0 + static_assert(0.0 == phi_5(n0)); + // check that the shape function at node 1 is 0.0 + static_assert(0.0 == phi_5(n1)); + // check that the shape function at node 2 is 0.0 + static_assert(0.0 == phi_5(n2)); + // check that the shape function at node 3 is 0.0 + static_assert(0.0 == phi_5(n3)); + // check that the shape function at node 4 is 0.0 + static_assert(0.0 == phi_5(n4)); + // check that the shape function at node 5 is 1.0 + static_assert(1.0 == phi_5(n5)); + + // all done + return; +} \ No newline at end of file diff --git a/tests/mito.lib/fem/verification_p1.py b/tests/mito.lib/fem/verification_p1.py new file mode 100644 index 000000000..ae0ebef0d --- /dev/null +++ b/tests/mito.lib/fem/verification_p1.py @@ -0,0 +1,40 @@ +import sympy +from sympy.vector import CoordSys3D, gradient + +# define the coordinate system +C = CoordSys3D('C') + +# define the scalar field +x, y, _ = C.base_scalars() + +# define the shape functions +phi0 = (1 - x - y) +phi1 = x +phi2 = y + +# report +print("Computing the mass matrix for the 3 shape functions of a triangular element:") + +# compute the mass matrix via double integration +phis = [phi0, phi1, phi2] +print("Mass matrix:") +for i, phi_i in enumerate(phis): + for j, phi_j in enumerate(phis): + integral = sympy.integrate(phi_i * phi_j, (y, 0, 1-x), (x, 0, 1)) + print(f"M[{i},{j}] = {integral}") + +# report +print("Computing the stiffness matrix for the 3 shape functions of a triangular element:") + +# compute the shape functions gradients +dphi0 = gradient(phi0) +dphi1 = gradient(phi1) +dphi2 = gradient(phi2) + +# compute the stiffness matrix via double integration +dphis = [dphi0, dphi1, dphi2] +print("Stiffness matrix:") +for i, dphi_i in enumerate(dphis): + for j, dphi_j in enumerate(dphis): + integral = sympy.integrate(dphi_i & dphi_j, (y, 0, 1-x), (x, 0, 1)) + print(f"K[{i},{j}] = {integral}") diff --git a/tests/mito.lib/fem/verification_p2.py b/tests/mito.lib/fem/verification_p2.py new file mode 100644 index 000000000..3e997c30f --- /dev/null +++ b/tests/mito.lib/fem/verification_p2.py @@ -0,0 +1,46 @@ +import sympy +from sympy.vector import CoordSys3D, gradient + +# define the coordinate system +C = CoordSys3D('C') + +# define the scalar field +x, y, _ = C.base_scalars() + +# define the shape functions +phi0 = (1 - x - y) * (1 - 2*x - 2*y) +phi1 = x * (2*x - 1) +phi2 = y * (2*y - 1) +phi3 = 4 * x * (1 - x - y) +phi4 = 4 * x * y +phi5 = 4 * y * (1 - x - y) + +# report +print("Computing the mass matrix for the 6 shape functions of a triangular element:") + +# compute the mass matrix via double integration +phis = [phi0, phi1, phi2, phi3, phi4, phi5] +print("Mass matrix:") +for i, phi_i in enumerate(phis): + for j, phi_j in enumerate(phis): + integral = sympy.integrate(phi_i * phi_j, (y, 0, 1-x), (x, 0, 1)) + print(f"M[{i},{j}] = {integral}") + +# report +print("Computing the stiffness matrix for the 6 shape functions of a triangular element:") + +# compute the shape functions gradients +dphi0 = gradient(phi0) +dphi1 = gradient(phi1) +dphi2 = gradient(phi2) +dphi3 = gradient(phi3) +dphi4 = gradient(phi4) +dphi5 = gradient(phi5) + +# compute the stiffness matrix via double integration +dphis = [dphi0, dphi1, dphi2, dphi3, dphi4, dphi5] +print("Stiffness matrix:") +for i, dphi_i in enumerate(dphis): + for j, dphi_j in enumerate(dphis): + integral = sympy.integrate(dphi_i & dphi_j, (y, 0, 1-x), (x, 0, 1)) + print(f"K[{i},{j}] = {integral}") diff --git a/tests/mito.lib/fields/calculus_identities.cc b/tests/mito.lib/fields/calculus_identities.cc new file mode 100644 index 000000000..ba7458e20 --- /dev/null +++ b/tests/mito.lib/fields/calculus_identities.cc @@ -0,0 +1,59 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include +#include + + +// the type of coordinates +using coordinates_t = mito::geometry::coordinates_t<2, mito::geometry::CARTESIAN>; + + +// the basis for vectors in 2D +static constexpr auto e_0 = mito::tensor::e_0<2>; +static constexpr auto e_1 = mito::tensor::e_1<2>; + +// pi sixth +constexpr auto pi_sixth = std::numbers::pi / 6.0; +// pi fourth +constexpr auto pi_fourth = std::numbers::pi / 4.0; + + +TEST(Identities, DivGrad) +{ + // the field returning the constant {e_0} unit vector in 2D + constexpr auto e0 = mito::fields::uniform_field(e_0); + + // the field returning the constant {e_1} unit vector in 2D + constexpr auto e1 = mito::fields::uniform_field(e_1); + + // the sine function + constexpr auto sin = mito::functions::sin; + + // the cosine function + constexpr auto cos = mito::functions::cos; + + // the function extracting the x_0 component of a 2D vector + constexpr auto x0 = mito::functions::component; + + // the function extracting the x_1 component of a 2D vector + constexpr auto x1 = mito::functions::component; + + // a vector field + constexpr auto f = sin(x0 * x1) * e0 + cos(x0 * x1) * e1; + + // a point in space + constexpr auto x = mito::geometry::coordinates({ pi_sixth, pi_fourth }); + + // the divergence of the gradient transposed of {f} + constexpr auto div_grad_T = + mito::fields::divergence(mito::fields::transpose(mito::fields::gradient(f))); + + // the gradient of the divergence of {f} + constexpr auto grad_div = mito::fields::gradient(mito::fields::divergence(f)); + + // check result + static_assert(div_grad_T(x) == grad_div(x)); +} diff --git a/tests/mito.lib/fields/calculus_scalar_field.cc b/tests/mito.lib/fields/calculus_scalar_field.cc index ee416c81d..bd49d1852 100644 --- a/tests/mito.lib/fields/calculus_scalar_field.cc +++ b/tests/mito.lib/fields/calculus_scalar_field.cc @@ -19,7 +19,7 @@ static constexpr auto e_1 = mito::tensor::e_1<2>; constexpr auto pi_sixth = std::numbers::pi / 6.0; -TEST(Fields, Gradient) +TEST(Laplacian, ScalarFields) { // the sine function constexpr auto sin = mito::functions::sin; diff --git a/tests/mito.lib/fields/calculus_vector_field.cc b/tests/mito.lib/fields/calculus_vector_field.cc new file mode 100644 index 000000000..a31ec88c9 --- /dev/null +++ b/tests/mito.lib/fields/calculus_vector_field.cc @@ -0,0 +1,70 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include +#include + + +// the type of coordinates +using coordinates_t = mito::geometry::coordinates_t<2, mito::geometry::CARTESIAN>; + + +// the basis for vectors in 2D +static constexpr auto e_0 = mito::tensor::e_0<2>; +static constexpr auto e_1 = mito::tensor::e_1<2>; + +// the basis for tensors in 2D +static constexpr auto e_00 = mito::tensor::e_00<2>; +static constexpr auto e_01 = mito::tensor::e_01<2>; +static constexpr auto e_10 = mito::tensor::e_10<2>; +static constexpr auto e_11 = mito::tensor::e_11<2>; + +// pi sixth +constexpr auto pi_sixth = std::numbers::pi / 6.0; +// pi fourth +constexpr auto pi_fourth = std::numbers::pi / 4.0; + + +TEST(Laplacian, VectorFields) +{ + // the field returning the constant {e_0} unit vector in 2D + constexpr auto e0 = mito::fields::uniform_field(e_0); + + // the field returning the constant {e_1} unit vector in 2D + constexpr auto e1 = mito::fields::uniform_field(e_1); + + // the sine function + constexpr auto sin = mito::functions::sin; + + // the cosine function + constexpr auto cos = mito::functions::cos; + + // the function extracting the x_0 component of a 2D vector + constexpr auto x0 = mito::functions::component; + + // the function extracting the x_1 component of a 2D vector + constexpr auto x1 = mito::functions::component; + + // a vector field + constexpr auto g = sin(x0 * x1) * e0 + cos(x0 * x1) * e1; + + // a point in space + constexpr auto x = mito::geometry::coordinates({ pi_sixth, pi_fourth }); + + // the gradient of {g} + constexpr auto gradient = mito::fields::gradient(g); + + // check result + static_assert( + gradient(x) + == (cos(x0 * x1) * x1 * e_00 + cos(x0 * x1) * x0 * e_01 - sin(x0 * x1) * x1 * e_10 + - sin(x0 * x1) * x0 * e_11)(x)); + + // the laplacian (divergence of gradient) + constexpr auto laplacian = mito::fields::divergence(gradient); + + // check result + static_assert(laplacian(x) == (-(x0 * x0 + x1 * x1) * g)(x)); +} diff --git a/tests/mito.lib/fields/gradient_non_square.cc b/tests/mito.lib/fields/gradient_non_square.cc new file mode 100644 index 000000000..ef37f2845 --- /dev/null +++ b/tests/mito.lib/fields/gradient_non_square.cc @@ -0,0 +1,75 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include +#include + + +// the type of coordinates +using coordinates_t = mito::geometry::coordinates_t<3, mito::geometry::CARTESIAN>; + + +// the basis for vectors in 2D +static constexpr auto e_0 = mito::tensor::e_0<2>; +static constexpr auto e_1 = mito::tensor::e_1<2>; + +// the basis for 2x3 tensors +static constexpr auto e_00 = mito::tensor::e_00<2, 3>; +static constexpr auto e_01 = mito::tensor::e_01<2, 3>; +static constexpr auto e_02 = mito::tensor::e_02<2, 3>; +static constexpr auto e_10 = mito::tensor::e_10<2, 3>; +static constexpr auto e_11 = mito::tensor::e_11<2, 3>; +static constexpr auto e_12 = mito::tensor::e_12<2, 3>; + +// pi sixth +constexpr auto pi_sixth = std::numbers::pi / 6.0; +// pi fourth +constexpr auto pi_fourth = std::numbers::pi / 4.0; + + +TEST(Gradient, NonSquare) +{ + // the field returning the constant {e_0} unit vector in 2D + constexpr auto e0 = mito::fields::uniform_field(e_0); + + // the field returning the constant {e_1} unit vector in 2D + constexpr auto e1 = mito::fields::uniform_field(e_1); + + // the sine function + constexpr auto sin = mito::functions::sin; + + // the cosine function + constexpr auto cos = mito::functions::cos; + + // the function extracting the x_0 component of {coordinates_t} + constexpr auto x0 = mito::functions::component; + + // the function extracting the x_1 component of {coordinates_t} + constexpr auto x1 = mito::functions::component; + + // the function extracting the x_1 component of {coordinates_t} + constexpr auto x2 = mito::functions::component; + + // a vector field + constexpr auto f = (sin(x0 * x1) + x2) * e0 + (cos(x0 * x1) - x2) * e1; + + // a point in space + constexpr auto x = mito::geometry::coordinates({ pi_sixth, pi_fourth, 1.0 }); + + // the gradient of {f} + constexpr auto gradient = mito::fields::gradient(f); + + // create a channel + journal::info_t channel("tests.gradient_non_square"); + + // report on the gradient at {x} + channel << "Gradient at x = " << x << " is " << gradient(x) << journal::endl; + + // check result + static_assert( + gradient(x) + == (cos(x0 * x1) * x1 * e_00 + cos(x0 * x1) * x0 * e_01 - sin(x0 * x1) * x1 * e_10 + - sin(x0 * x1) * x0 * e_11 + e_02 - e_12)(x)); +} diff --git a/tests/mito.lib/functions/algebra.cc b/tests/mito.lib/functions/algebra.cc index d00763ff8..07f75a95a 100644 --- a/tests/mito.lib/functions/algebra.cc +++ b/tests/mito.lib/functions/algebra.cc @@ -69,7 +69,7 @@ TEST(Algebra, ScalarValuedFunctions) constexpr auto function16 = function1 * e0 + function1 * e1 + function1 * e2; // vector times scalar multiplication - constexpr auto alpha = mito::tensor::scalar_t(10); + constexpr auto alpha = mito::tensor::scalar_t{ 10 }; constexpr auto function17 = alpha * function16; static_assert(function17(x) == alpha * function16(x)); @@ -138,4 +138,19 @@ TEST(Algebra, ScalarProduct) // check result static_assert(0.1 == (x0 * x1)(x)); +} + + +TEST(Algebra, DyadicProduct) +{ + // a 2D vector + constexpr auto x = mito::tensor::vector_t<2>{ 0.1, 1.0 }; + + // the function returning the constant e0 unit vector in 2D + constexpr auto e0 = mito::functions::constant>(mito::tensor::e_0<2>); + + // check result + static_assert( + mito::tensor::dyadic(mito::tensor::e_0<2>, mito::tensor::e_0<2>) + == mito::functions::dyadic(e0, e0)(x)); } \ No newline at end of file diff --git a/tests/mito.lib/functions/derivative_chain_rule.cc b/tests/mito.lib/functions/derivative_chain_rule.cc index bae6cdbea..09c6cd9e3 100644 --- a/tests/mito.lib/functions/derivative_chain_rule.cc +++ b/tests/mito.lib/functions/derivative_chain_rule.cc @@ -13,7 +13,7 @@ using std::numbers::pi; using mito::tensor::scalar_t; -TEST(Derivatives, Composition) +TEST(Derivatives, Scalar) { // pi sixths constexpr auto pi_sixth = pi / 6.0; @@ -49,4 +49,47 @@ TEST(Derivatives, Composition) } +// the coordinates type +using coordinates_type_2D = mito::tensor::vector_t<2>; +using coordinates_type_3D = mito::tensor::vector_t<3>; + + +TEST(Derivatives, Vector) +{ + // components 2D + constexpr auto a = mito::functions::component; + constexpr auto b = mito::functions::component; + + // a vector function R^2 -> R^3 (internal in the composition) + constexpr auto g = a * b * mito::tensor::e<1, 3> + (a + b) * mito::tensor::e<2, 3>; + + // components 3D + constexpr auto x = mito::functions::component; + constexpr auto y = mito::functions::component; + constexpr auto z = mito::functions::component; + + // a vector function R^3 -> R^3 (external in the composition) + constexpr auto f = + x * mito::tensor::e<0, 3> + y * mito::tensor::e<1, 3> + z * mito::tensor::e<2, 3>; + + // the composition of f and g (R^2 -> R^3) + constexpr auto h = f(g); + + // pick a point + constexpr auto p = coordinates_type_2D{ 1.0, 2.0 }; + + // f(g) = a * b * mito::tensor::e<1, 3> + (a + b) * mito::tensor::e<2, 3> + static_assert( + h(p) == p[0] * p[1] * mito::tensor::e<1, 3> + (p[0] + p[1]) * mito::tensor::e<2, 3>); + + // the derivative of h + constexpr auto h_0 = mito::functions::derivative<0>(h); + constexpr auto h_1 = mito::functions::derivative<1>(h); + + // check the result + static_assert(h_0(p) == p[1] * mito::tensor::e<1, 3> + mito::tensor::e<2, 3>); + static_assert(h_1(p) == p[0] * mito::tensor::e<1, 3> + mito::tensor::e<2, 3>); +} + + // end of file diff --git a/tests/mito.lib/functions/derivative_product.cc b/tests/mito.lib/functions/derivative_product.cc index f6625b535..643879e8e 100644 --- a/tests/mito.lib/functions/derivative_product.cc +++ b/tests/mito.lib/functions/derivative_product.cc @@ -132,4 +132,37 @@ TEST(Derivatives, ScalarProduct) } +TEST(Derivatives, ScalarProductConstant) +{ + // a 2D vector + constexpr auto x = mito::tensor::vector_t<2>{ 0.1, 1.0 }; + + // a 2D vector + constexpr auto a = mito::tensor::vector_t<2>{ -1.0, 1.0 }; + + // the constant e0 unit vector in 2D + constexpr auto e0 = mito::tensor::e_0<2>; + + // the constant e1 unit vector in 2D + constexpr auto e1 = mito::tensor::e_1<2>; + + // the function extracting the x_0 component of a 2D vector + constexpr auto x0 = mito::functions::x<0, 2>; + + // the function extracting the x_1 component of a 2D vector + constexpr auto x1 = mito::functions::x<1, 2>; + + // a function {f} + constexpr auto f = a * (x0 * e0 + x1 * e1); + + // the derivative of f with respect to x0 + constexpr auto f_0 = mito::functions::derivative<0>(f); + constexpr auto f_1 = mito::functions::derivative<1>(f); + + // check result + static_assert(a * e0 == f_0(x)); + static_assert(a * e1 == f_1(x)); +} + + // end of file diff --git a/tests/mito.lib/functions/derivative_subscript.cc b/tests/mito.lib/functions/derivative_subscript.cc new file mode 100644 index 000000000..b4c90169b --- /dev/null +++ b/tests/mito.lib/functions/derivative_subscript.cc @@ -0,0 +1,47 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include +#include + + +// pi +using std::numbers::pi; + + +TEST(Derivatives, Subscript) +{ + // a scalar + constexpr auto x = pi / 4.0; + + // a vector function R -> R^2x3 + constexpr auto f = mito::functions::sin * mito::tensor::e_12<2, 3>; + + // get the output type of the function {f}, in this case a 2x3 tensor + using output_type = decltype(f)::output_type; + + // get the offset corresponding to the multi-index {1, 2} in a 2x3 tensor + // multi-index {1, 2} is where {f} has the only nontrivial value + constexpr int offset = output_type::getOffset<1, 2>(); + + // check that evaluating and subscripting is the same than subscripting and evaluating + static_assert(f(x)[offset] == f[offset](x)); + + // check that evaluating and subscripting is the same than subscripting and evaluating + static_assert(f(x)[{ 1, 2 }] == f[{ 1, 2 }](x)); + + // check that the derivative of the subscript is the subscript of the derivative + static_assert( + mito::functions::derivative(f[offset])(x) == mito::functions::derivative(f)(x)[offset]); + + // check that the derivative of the scalar function which extracts the {1, 2} component is equal + // to the {{1, 2}, 0} component of the total derivative + static_assert( + mito::functions::derivative(f[{ 1, 2 }])(x) + == mito::functions::derivative(f)(x)[{ offset, 0 }]); +} + + +// end of file diff --git a/tests/mito.lib/functions/derivative_sum.cc b/tests/mito.lib/functions/derivative_sum.cc index ac223d6d7..b2fbd9b3e 100644 --- a/tests/mito.lib/functions/derivative_sum.cc +++ b/tests/mito.lib/functions/derivative_sum.cc @@ -106,4 +106,26 @@ TEST(Derivatives, VectorSum) } +TEST(Derivatives, LinearCombination) +{ + // pi sixths + constexpr auto pi_sixth = pi / 6.0; + + // a sine function + constexpr auto sin = mito::functions::sin; + // a cosine function + constexpr auto cos = mito::functions::cos; + // the functions linear combination + constexpr auto combination = + mito::functions::linear_combination(std::array{ 2.0, -1.0 }, sin, cos); + // check that it is equal to {2.0 * sin + -1.0 * cos} + static_assert(2.0 * sin(pi_sixth) - cos(pi_sixth) == combination(pi_sixth)); + + // the derivative of the linear combination + constexpr auto combination_1 = mito::functions::derivative(combination); + // check that it is equal to {2.0 * cos + 1.0 * sin} + static_assert(2.0 * cos(pi_sixth) + sin(pi_sixth) == combination_1(pi_sixth)); +} + + // end of file diff --git a/tests/mito.lib/functions/partial_derivatives.cc b/tests/mito.lib/functions/partial_derivatives.cc index afe82d3ee..2fe7fa353 100644 --- a/tests/mito.lib/functions/partial_derivatives.cc +++ b/tests/mito.lib/functions/partial_derivatives.cc @@ -23,40 +23,34 @@ TEST(VectorFunctions, Components) static_assert(1.0 == x1(x)); // the partial derivatives of x0 - constexpr auto x0_0 = mito::functions::derivative<0>(x0); - constexpr auto x0_1 = mito::functions::derivative<1>(x0); + constexpr auto dx0 = mito::functions::derivative(x0); + // check result - static_assert(1.0 == x0_0(x)); - static_assert(0.0 == x0_1(x)); + static_assert(1.0 == dx0(x)[0]); + static_assert(0.0 == dx0(x)[1]); // the partial derivatives of x1 - constexpr auto x1_0 = mito::functions::derivative<0>(x1); - constexpr auto x1_1 = mito::functions::derivative<1>(x1); + constexpr auto dx1 = mito::functions::derivative(x1); // check result - static_assert(0.0 == x1_0(x)); - static_assert(1.0 == x1_1(x)); + static_assert(0.0 == dx1(x)[0]); + static_assert(1.0 == dx1(x)[1]); // the second partial derivatives of x0 - constexpr auto x0_00 = mito::functions::derivative<0>(x0_0); - constexpr auto x0_01 = mito::functions::derivative<1>(x0_0); - constexpr auto x0_10 = mito::functions::derivative<0>(x0_1); - constexpr auto x0_11 = mito::functions::derivative<1>(x0_1); + constexpr auto ddx0 = mito::functions::derivative(dx0); + // check result - static_assert(0.0 == x0_00(x)); - static_assert(0.0 == x0_01(x)); - static_assert(0.0 == x0_10(x)); - static_assert(0.0 == x0_11(x)); + static_assert(0.0 == ddx0(x)[{ 0, 0 }]); + static_assert(0.0 == ddx0(x)[{ 0, 1 }]); + static_assert(0.0 == ddx0(x)[{ 1, 0 }]); + static_assert(0.0 == ddx0(x)[{ 1, 1 }]); // the second partial derivatives of x1 - constexpr auto x1_00 = mito::functions::derivative<0>(x1_0); - constexpr auto x1_01 = mito::functions::derivative<1>(x1_0); - constexpr auto x1_10 = mito::functions::derivative<0>(x1_1); - constexpr auto x1_11 = mito::functions::derivative<1>(x1_1); + constexpr auto ddx1 = mito::functions::derivative(dx1); // check result - static_assert(0.0 == x1_00(x)); - static_assert(0.0 == x1_01(x)); - static_assert(0.0 == x1_10(x)); - static_assert(0.0 == x1_11(x)); + static_assert(0.0 == ddx1(x)[{ 0, 0 }]); + static_assert(0.0 == ddx1(x)[{ 0, 1 }]); + static_assert(0.0 == ddx1(x)[{ 1, 0 }]); + static_assert(0.0 == ddx1(x)[{ 1, 1 }]); } @@ -73,17 +67,15 @@ TEST(Derivatives, PartialDerivatives) // sin(x0 * x1) constexpr auto sin = mito::functions::sin(x0 * x1); - // the partial derivative of sin(x0 * x1) wrt to x0 - constexpr auto sin_0 = mito::functions::derivative<0>(sin); - // the partial derivative of sin(x0 * x1) wrt to x1 - constexpr auto sin_1 = mito::functions::derivative<1>(sin); + // the partial derivatives of sin(x0 * x1) wrt to x0 and x1 + constexpr auto dsin = mito::functions::derivative(sin); // cos(x0 * x1) constexpr auto cos = mito::functions::cos(x0 * x1); // check result - static_assert((cos * x1)(x) == sin_0(x)); - static_assert((cos * x0)(x) == sin_1(x)); + static_assert((cos * x1)(x) == dsin[0](x)); + static_assert((cos * x0)(x) == dsin[1](x)); } diff --git a/tests/mito.lib/functions/tensor_derivatives.cc b/tests/mito.lib/functions/tensor_derivatives.cc new file mode 100644 index 000000000..0ce8991c2 --- /dev/null +++ b/tests/mito.lib/functions/tensor_derivatives.cc @@ -0,0 +1,208 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include +#include + + +// the coordinates functions in 2D space +static constexpr auto x0 = mito::functions::x<0, 2>; +static constexpr auto x1 = mito::functions::x<1, 2>; + +// unit vectors in 3D +static constexpr auto e0 = mito::tensor::e_0<3>; +static constexpr auto e1 = mito::tensor::e_1<3>; +static constexpr auto e2 = mito::tensor::e_2<3>; + +// unit matrices in 2D +static constexpr auto e00 = mito::tensor::e_00<2>; +static constexpr auto e01 = mito::tensor::e_01<2>; +static constexpr auto e10 = mito::tensor::e_10<2>; +static constexpr auto e11 = mito::tensor::e_11<2>; + + +TEST(Derivatives, ScalarOfScalar) +{ + // a scalar function of scalar + constexpr auto f = mito::functions::sin; + // its derivative + constexpr auto df = mito::functions::derivative(f); + // a scalar input + constexpr auto x = mito::tensor::scalar_t{ 2.0 }; + // check that the derivative is correct + static_assert(df(x) == mito::functions::cos(x)); +} + + +TEST(Derivatives, ScalarOfVector) +{ + // a scalar function of a 2D vector + constexpr auto f = mito::functions::sin(x0 * x1); + // its derivative + constexpr auto df = mito::functions::derivative(f); + // a 2D vector input + constexpr auto x = mito::tensor::vector_t<2>{ 0.1, 1.0 }; + // check the derivatives + static_assert(df(x)[0] == (mito::functions::cos(x0 * x1) * x1)(x)); + static_assert(df(x)[1] == (mito::functions::cos(x0 * x1) * x0)(x)); +} + + +TEST(Derivatives, ScalarOfMatrix) +{ + // a scalar function of a 2D matrix (trace) + constexpr auto f = mito::functions::component, 0> + + mito::functions::component, 3>; + // its derivative + constexpr auto df = mito::functions::derivative(f); + // a 2D matrix input + constexpr auto A = mito::tensor::matrix_t<2, 2>{ 1.0, 2.0, 3.0, 4.0 }; + // check the derivatives + static_assert(df(A)[0] == 1.0); + static_assert(df(A)[1] == 0.0); + static_assert(df(A)[2] == 0.0); + static_assert(df(A)[3] == 1.0); +} + + +TEST(Derivatives, VectorOfScalar) +{ + // a 3D vector function of a scalar + constexpr auto f = + mito::functions::sin * e0 + mito::functions::cos * e1 + mito::functions::exp * e2; + // its derivative + constexpr auto df = mito::functions::derivative(f); + // a scalar input + constexpr auto x = mito::tensor::scalar_t{ 0.5 }; + // check the derivatives + static_assert(df(x)[{ 0, 0 }] == mito::functions::cos(x)); // d/dx sin = cos + static_assert(df(x)[{ 1, 0 }] == -mito::functions::sin(x)); // d/dx cos = -sin + static_assert(df(x)[{ 2, 0 }] == mito::functions::exp(x)); // d/dx exp = exp +} + + +TEST(Derivatives, VectorOfVector) +{ + // a 3D vector function of a 2D vector + constexpr auto f = x0 * e0 + x1 * e1 + x0 * x1 * e2; + // its derivative + constexpr auto df = mito::functions::derivative(f); + // a 2D vector input + constexpr auto x = mito::tensor::vector_t<2>{ 0.1, 0.2 }; + // check the derivatives + static_assert(df(x)[{ 0, 0 }] == 1.0); + static_assert(df(x)[{ 0, 1 }] == 0.0); + static_assert(df(x)[{ 1, 0 }] == 0.0); + static_assert(df(x)[{ 1, 1 }] == 1.0); + static_assert(df(x)[{ 2, 0 }] == x1(x)); + static_assert(df(x)[{ 2, 1 }] == x0(x)); +} + + +TEST(Derivatives, VectorOfMatrix) +{ + // component function to extract A_ij from a matrix A + constexpr auto A_00 = mito::functions::component, 0>; + constexpr auto A_01 = mito::functions::component, 1>; + constexpr auto A_10 = mito::functions::component, 2>; + constexpr auto A_11 = mito::functions::component, 3>; + + // a 3D vector function of a 2D matrix + constexpr auto f = A_00 * e0 + A_01 * e1 + (A_10 - A_11) * e2; + // its derivative + constexpr auto df = mito::functions::derivative(f); + // a 2D matrix input + constexpr auto A = mito::tensor::matrix_t<2, 2>{ 1.0, 2.0, 3.0, 4.0 }; + // check the derivatives (first component of the output vector) + static_assert(df(A)[{ 0, 0 }] == 1.0); // d/dA_00 A_00 = 1 + static_assert(df(A)[{ 0, 1 }] == 0.0); // d/dA_01 A_00 = 0 + static_assert(df(A)[{ 0, 2 }] == 0.0); // d/dA_10 A_00 = 0 + static_assert(df(A)[{ 0, 3 }] == 0.0); // d/dA_11 A_00 = 0 + // check the derivatives (second component of the output vector) + static_assert(df(A)[{ 1, 0 }] == 0.0); // d/dA_00 A_01 = 0 + static_assert(df(A)[{ 1, 1 }] == 1.0); // d/dA_01 A_01 = 1 + static_assert(df(A)[{ 1, 2 }] == 0.0); // d/dA_10 A_01 = 0 + static_assert(df(A)[{ 1, 3 }] == 0.0); // d/dA_11 A_01 = 0 + // check the derivatives (third component of the output vector) + static_assert(df(A)[{ 2, 0 }] == 0.0); // d/dA_00 (A_10 - A_11) = 0 + static_assert(df(A)[{ 2, 1 }] == 0.0); // d/dA_01 (A_10 - A_11) = 0 + static_assert(df(A)[{ 2, 2 }] == 1.0); // d/dA_10 (A_10 - A_11) = 1 + static_assert(df(A)[{ 2, 3 }] == -1.0); // d/dA_11 (A_10 - A_11) = -1 +} + + +TEST(Derivatives, MatrixOfScalar) +{ + // a 2D matrix function of a scalar + constexpr auto f = + mito::functions::sin * e00 + mito::functions::cos * e01 + mito::functions::exp * e10; + // its derivative + constexpr auto df = mito::functions::derivative(f); + // a scalar input + constexpr auto x = mito::tensor::scalar_t{ 1.0 }; + // check the derivatives + static_assert(df(x)[{ 0, 0 }] == mito::functions::cos(x)); + static_assert(df(x)[{ 1, 0 }] == -mito::functions::sin(x)); + static_assert(df(x)[{ 2, 0 }] == mito::functions::exp(x)); + static_assert(df(x)[{ 3, 0 }] == 0.0); +} + + +TEST(Derivatives, MatrixOfVector) +{ + // a 2D matrix function of a 2D vector + constexpr auto f = mito::functions::sin(x0) * e00 + mito::functions::cos(x1) * e01 + + mito::functions::exp(x0 * x1) * e11; + // its derivative + constexpr auto df = mito::functions::derivative(f); + // a 2D vector input + constexpr auto x = mito::tensor::vector_t<2>{ 1.0, -1.0 }; + // check the derivatives + static_assert(df(x)[{ 0, 0 }] == mito::functions::cos(x0)(x)); // d/dx0 (sin(x0)) = cos(x0) + static_assert(df(x)[{ 0, 1 }] == 0.0); // d/dx1 (sin(x0)) = 0 + static_assert(df(x)[{ 1, 0 }] == 0.0); // d/dx0 (cos(x1)) = 0 + static_assert(df(x)[{ 1, 1 }] == -mito::functions::sin(x1)(x)); // d/dx1 (cos(x1)) = -sin(x1) + static_assert(df(x)[{ 2, 0 }] == 0.0); // d/dx0 (0) = 0 + static_assert(df(x)[{ 2, 1 }] == 0.0); // d/dx1 (0) = 0 + static_assert( + df(x)[{ 3, 0 }] + == (mito::functions::exp(x0 * x1) * x1)(x)); // d/dx0 (exp(x0 * x1)) = exp(x0 * x1) * x1 + static_assert( + df(x)[{ 3, 1 }] + == (mito::functions::exp(x0 * x1) * x0)(x)); // d/dx1 (exp(x0 * x1)) = exp(x0 * x1) * x0 +} + + +TEST(Derivatives, MatrixOfMatrix) +{ + // a 2D matrix function of a 2D matrix + constexpr auto f = + (mito::functions::component, 0> + + mito::functions::component, 3>) *(e00 + e10); + // its derivative + constexpr auto df = mito::functions::derivative(f); + // a 2D matrix input + constexpr auto A = mito::tensor::matrix_t<2, 2>{ 1.0, 2.0, 3.0, 4.0 }; + // check the derivatives + static_assert(df(A)[{ 0, 0 }] == 1.0); + static_assert(df(A)[{ 0, 1 }] == 0.0); + static_assert(df(A)[{ 0, 2 }] == 0.0); + static_assert(df(A)[{ 0, 3 }] == 1.0); + static_assert(df(A)[{ 1, 0 }] == 0.0); + static_assert(df(A)[{ 1, 1 }] == 0.0); + static_assert(df(A)[{ 1, 2 }] == 0.0); + static_assert(df(A)[{ 1, 3 }] == 0.0); + static_assert(df(A)[{ 2, 0 }] == 1.0); + static_assert(df(A)[{ 2, 1 }] == 0.0); + static_assert(df(A)[{ 2, 2 }] == 0.0); + static_assert(df(A)[{ 2, 3 }] == 1.0); + static_assert(df(A)[{ 3, 0 }] == 0.0); + static_assert(df(A)[{ 3, 1 }] == 0.0); + static_assert(df(A)[{ 3, 2 }] == 0.0); + static_assert(df(A)[{ 3, 3 }] == 0.0); +} + + +// end of file diff --git a/tests/mito.lib/integration/divergence_theorem.cc b/tests/mito.lib/integration/divergence_theorem.cc index 460fe3be5..36d3cb105 100644 --- a/tests/mito.lib/integration/divergence_theorem.cc +++ b/tests/mito.lib/integration/divergence_theorem.cc @@ -28,7 +28,7 @@ TEST(DivergenceTheorem, Mesh2D) // make a channel journal::info_t channel("tests.divergence_theorem"); - // a scalar field + // a vector field constexpr auto f = x0 * x1 * e0 + x0 * x0 * e1; // build a scalar field with divergence of field diff --git a/tests/mito.lib/io/parallel_vtk_cloud_field_writer.cc b/tests/mito.lib/io/parallel_vtk_cloud_field_writer.cc index 7803486e1..4043095e0 100644 --- a/tests/mito.lib/io/parallel_vtk_cloud_field_writer.cc +++ b/tests/mito.lib/io/parallel_vtk_cloud_field_writer.cc @@ -59,8 +59,7 @@ TEST(ParallelVtkWriter, CloudField) } // a point field on the cloud - auto point_field = - mito::discretization::point_field>(cloud, "normal"); + auto point_field = mito::discrete::point_field>(cloud, "normal"); // the normal field to the submanifold constexpr auto normal_field = mito::fields::field([](const coordinates_t & x) -> auto { diff --git a/tests/mito.lib/io/parallel_vtk_mesh_field_writer.cc b/tests/mito.lib/io/parallel_vtk_mesh_field_writer.cc index a33115c7f..bc2ee42af 100644 --- a/tests/mito.lib/io/parallel_vtk_mesh_field_writer.cc +++ b/tests/mito.lib/io/parallel_vtk_mesh_field_writer.cc @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include @@ -38,8 +38,8 @@ TEST(ParallelVtkWriter, MeshField) std::ifstream fileStream(mesh_file); auto mesh = mito::io::summit::reader>(fileStream, coord_system); - // a nodal field on the mesh - auto nodal_field = mito::discretization::nodal_field>(mesh, "normal"); + // a mesh field on the mesh + auto mesh_field = mito::discrete::mesh_field>(mesh, "normal"); // the normal field to the ball constexpr auto normal_field = mito::fields::field([](const coordinates_t & x) -> auto { @@ -50,8 +50,8 @@ TEST(ParallelVtkWriter, MeshField) + std::cos(theta) * mito::tensor::e_2<3>; }); - // fill information in nodal field - for (auto & [node, value] : nodal_field) { + // fill information in mesh field + for (auto & [node, value] : mesh_field) { // get the coordinates of the node auto & coordinates = coord_system.coordinates(node->point()); // compute the value of the normal field at those coordinates @@ -60,8 +60,8 @@ TEST(ParallelVtkWriter, MeshField) // write mesh to vtk file auto writer = mito::io::vtk::parallel_field_writer("sphere_mesh_field", mesh, coord_system); - // sign {nodal_field} up with the writer - writer.record(nodal_field); + // sign {mesh_field} up with the writer + writer.record(mesh_field); // write output file writer.write(); diff --git a/tests/mito.lib/io/parallel_vtk_mesh_writer.cc b/tests/mito.lib/io/parallel_vtk_mesh_writer.cc index 2998f611f..6908aa24e 100644 --- a/tests/mito.lib/io/parallel_vtk_mesh_writer.cc +++ b/tests/mito.lib/io/parallel_vtk_mesh_writer.cc @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include diff --git a/tests/mito.lib/io/summit_to_summit_mesh_2D.cc b/tests/mito.lib/io/summit_to_summit_mesh_2D.cc index 5de5bce28..cc79599fc 100644 --- a/tests/mito.lib/io/summit_to_summit_mesh_2D.cc +++ b/tests/mito.lib/io/summit_to_summit_mesh_2D.cc @@ -33,9 +33,9 @@ test() // get the original number of mesh cells original_mesh_cells = mesh.nCells(); - // get the original number of mesh nodes by counting the nodes of a nodal field built on it + // get the original number of mesh nodes by counting the nodes of a mesh field built on it original_mesh_nodes = - mito::discretization::nodal_field(mesh, "field").size(); + mito::discrete::mesh_field(mesh, "field").size(); // write summit mesh mito::io::summit::writer("rectangle_copy", mesh, coord_system); @@ -52,9 +52,9 @@ test() // get the reread number of mesh cells reread_mesh_cells = mesh.nCells(); - // get the reread number of mesh nodes by counting the nodes of a nodal field built on it + // get the reread number of mesh nodes by counting the nodes of a mesh field built on it reread_mesh_nodes = - mito::discretization::nodal_field(mesh, "field").size(); + mito::discrete::mesh_field(mesh, "field").size(); #ifdef WITH_VTK // write mesh to vtk file diff --git a/tests/mito.lib/matrix_solvers/petsc_initialize_finalize.cc b/tests/mito.lib/matrix_solvers/petsc_initialize_finalize.cc new file mode 100644 index 000000000..e11b518c4 --- /dev/null +++ b/tests/mito.lib/matrix_solvers/petsc_initialize_finalize.cc @@ -0,0 +1,21 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + + +#include +#include + + +TEST(Solvers, PETScInitializeFinalize) +{ + // initialize PETSc + mito::petsc::initialize(); + + // finalize PETSc + mito::petsc::finalize(); +} + + +// end of file diff --git a/tests/mito.lib/solvers/petsc_solve_linear_system.cc b/tests/mito.lib/matrix_solvers/petsc_ksp.cc similarity index 55% rename from tests/mito.lib/solvers/petsc_solve_linear_system.cc rename to tests/mito.lib/matrix_solvers/petsc_ksp.cc index bbd7a6e05..96090eea5 100644 --- a/tests/mito.lib/solvers/petsc_solve_linear_system.cc +++ b/tests/mito.lib/matrix_solvers/petsc_ksp.cc @@ -13,21 +13,28 @@ TEST(Solvers, PETScKSPSolver) // the size of the linear system int N = 10; - // instantiate a PETSc Krylov solver for a linear system of size {N} - auto solver = mito::solvers::petsc::ksp("mysolver"); - solver.initialize(N); + // instantiate a PETSc linear system of size {N} + auto linear_system = mito::matrix_solvers::petsc::linear_system("mysystem"); + // create the linear system and allocate the memory + linear_system.create(N); + + // instantiate a PETSc Krylov solver for the linear system + auto solver = mito::matrix_solvers::petsc::ksp(linear_system); + // create the Krylov solver and allocate the memory + solver.create(); + // set options for the petsc Krylov solver solver.set_options("-ksp_monitor"); // set matrix and right-hand side entries for (int i = 0; i < N; i++) { - solver.insert_matrix_value(i, i, 2.0); + linear_system.insert_matrix_value(i, i, 2.0); if (i > 0) { - solver.insert_matrix_value(i, i - 1, -1.0); + linear_system.insert_matrix_value(i, i - 1, -1.0); } if (i < N - 1) { - solver.insert_matrix_value(i, i + 1, -1.0); + linear_system.insert_matrix_value(i, i + 1, -1.0); } - solver.insert_rhs_value(i, 1.0); + linear_system.insert_rhs_value(i, 1.0); } // solve the linear system @@ -35,7 +42,7 @@ TEST(Solvers, PETScKSPSolver) // read the solution auto x = std::vector(N); - solver.get_solution(x); + linear_system.get_solution(x); // check the solution EXPECT_DOUBLE_EQ(x[0], 5.0); @@ -49,8 +56,8 @@ TEST(Solvers, PETScKSPSolver) EXPECT_DOUBLE_EQ(x[8], 9.0); EXPECT_DOUBLE_EQ(x[9], 5.0); - // finalize the solver - solver.finalize(); + // destroy the solver + solver.destroy(); // all done return; @@ -60,14 +67,14 @@ TEST(Solvers, PETScKSPSolver) int main(int argc, char ** argv) { - // initialize petsc - PetscInitialize(&argc, &argv, PETSC_NULLPTR, PETSC_NULLPTR); + // initialize PETSc + mito::petsc::initialize(); ::testing::InitGoogleTest(&argc, argv); auto result = RUN_ALL_TESTS(); - // finalize petsc - PetscFinalize(); + // finalize PETSc + mito::petsc::finalize(); // all done return result; diff --git a/tests/mito.lib/quadrature/quadrature_parametric_segment.cc b/tests/mito.lib/quadrature/quadrature_parametric_segment.cc index dd14ca2fe..50b468953 100644 --- a/tests/mito.lib/quadrature/quadrature_parametric_segment.cc +++ b/tests/mito.lib/quadrature/quadrature_parametric_segment.cc @@ -4,15 +4,20 @@ // #include -#include #include +// the type of quadrature +using mito::quadrature::GAUSS; +// the reference segment type +using reference_segment_t = mito::geometry::reference_segment_t; + + TEST(ParametricSegment, Order1) { // a Gauss quadrature rule on segments with degree of exactness 1 constexpr auto quadrature_rule = - mito::quadrature::quadrature_rule(); + mito::quadrature::quadrature_rule(); // the parametric point type using point_t = decltype(quadrature_rule)::quadrature_point_type; @@ -36,7 +41,7 @@ TEST(ParametricSegment, Order2) { // a Gauss quadrature rule on segments with degree of exactness 2 constexpr auto quadrature_rule = - mito::quadrature::quadrature_rule(); + mito::quadrature::quadrature_rule(); // the parametric point type using point_t = decltype(quadrature_rule)::quadrature_point_type; diff --git a/tests/mito.lib/quadrature/quadrature_parametric_tetrahedron.cc b/tests/mito.lib/quadrature/quadrature_parametric_tetrahedron.cc index 3e6a6b7f0..c91dd2a32 100644 --- a/tests/mito.lib/quadrature/quadrature_parametric_tetrahedron.cc +++ b/tests/mito.lib/quadrature/quadrature_parametric_tetrahedron.cc @@ -8,11 +8,17 @@ #include +// the type of quadrature +using mito::quadrature::GAUSS; +// the reference tetrahedron type +using reference_tetrahedron_t = mito::geometry::reference_tetrahedron_t; + + TEST(ParametricTetrahedron, Order1) { // a Gauss quadrature rule on tetrahedrons with degree of exactness 1 - constexpr auto quadrature_rule = mito::quadrature::quadrature_rule< - mito::quadrature::GAUSS, mito::topology::tetrahedron_t, 1>(); + constexpr auto quadrature_rule = + mito::quadrature::quadrature_rule(); // the parametric point type using point_t = decltype(quadrature_rule)::quadrature_point_type; @@ -23,13 +29,13 @@ TEST(ParametricTetrahedron, Order1) }; // area of the parametric tetrahedron - constexpr auto area = 1.0 / 3.0; + constexpr auto area = 1.0 / 6.0; // integral of f on the parametric tetrahedron constexpr auto integral = area * quadrature_rule.weight(0) * f(quadrature_rule.point(0)); // exact solution - constexpr auto exact = 1.0 / 12.0; + constexpr auto exact = 1.0 / 24.0; // check result static_assert(std::fabs(integral - exact) < 1.e-16); @@ -38,8 +44,8 @@ TEST(ParametricTetrahedron, Order1) TEST(ParametricTetrahedron, Order2) { // a Gauss quadrature rule on tetrahedrons with degree of exactness 2 - constexpr auto quadrature_rule = mito::quadrature::quadrature_rule< - mito::quadrature::GAUSS, mito::topology::tetrahedron_t, 2>(); + constexpr auto quadrature_rule = + mito::quadrature::quadrature_rule(); // the parametric point type using point_t = decltype(quadrature_rule)::quadrature_point_type; @@ -50,7 +56,7 @@ TEST(ParametricTetrahedron, Order2) }; // area of the parametric tetrahedron - constexpr auto area = 1.0 / 3.0; + constexpr auto area = 1.0 / 6.0; // integral of f on the parametric tetrahedron constexpr auto integral = area @@ -60,7 +66,7 @@ TEST(ParametricTetrahedron, Order2) + quadrature_rule.weight(3) * f(quadrature_rule.point(3))); // exact solution - constexpr auto exact = 1.0 / 30.0; + constexpr auto exact = 1.0 / 60.0; // check result static_assert(std::fabs(integral - exact) < 1.e-16); diff --git a/tests/mito.lib/quadrature/quadrature_parametric_triangle.cc b/tests/mito.lib/quadrature/quadrature_parametric_triangle.cc index 355ca474e..ce2f9e0a7 100644 --- a/tests/mito.lib/quadrature/quadrature_parametric_triangle.cc +++ b/tests/mito.lib/quadrature/quadrature_parametric_triangle.cc @@ -8,11 +8,17 @@ #include +// the type of quadrature +using mito::quadrature::GAUSS; +// the reference triangle type +using reference_triangle_t = mito::geometry::reference_triangle_t; + + TEST(ParametricTriangle, Order1) { // a Gauss quadrature rule on triangles with degree of exactness 1 constexpr auto quadrature_rule = - mito::quadrature::quadrature_rule(); + mito::quadrature::quadrature_rule(); // the parametric point type using point_t = decltype(quadrature_rule)::quadrature_point_type; @@ -39,7 +45,7 @@ TEST(ParametricTriangle, Order2) { // a Gauss quadrature rule on triangles with degree of exactness 2 constexpr auto quadrature_rule = - mito::quadrature::quadrature_rule(); + mito::quadrature::quadrature_rule(); // the parametric point type using point_t = decltype(quadrature_rule)::quadrature_point_type; @@ -69,12 +75,12 @@ TEST(ParametricTriangle, Order3) { // a Gauss quadrature rule on triangles with degree of exactness 3 constexpr auto quadrature_rule = - mito::quadrature::quadrature_rule(); + mito::quadrature::quadrature_rule(); // the parametric point type using point_t = decltype(quadrature_rule)::quadrature_point_type; - // a quadratic function of the parametric coordinates x_0^2 + // a quadratic function of the parametric coordinates x_0^3 constexpr auto f = [](const point_t & x) -> mito::tensor::scalar_t { return x[0] * x[0] * x[0]; }; @@ -82,7 +88,7 @@ TEST(ParametricTriangle, Order3) // area of the parametric triangle constexpr auto area = 0.5; - // integral of f on the parametric triangle [0, 1] + // integral of f on the parametric triangle constexpr auto integral = area * (quadrature_rule.weight(0) * f(quadrature_rule.point(0)) + quadrature_rule.weight(1) * f(quadrature_rule.point(1)) @@ -91,11 +97,44 @@ TEST(ParametricTriangle, Order3) + quadrature_rule.weight(4) * f(quadrature_rule.point(4)) + quadrature_rule.weight(5) * f(quadrature_rule.point(5))); - // exact solution (1/3 x^3) at x = 1 + // exact solution constexpr auto exact = 1.0 / 20.0; // check result static_assert(std::fabs(integral - exact) < 1.e-16); } +TEST(ParametricTriangle, Order4) +{ + // a Gauss quadrature rule on triangles with degree of exactness 4 + constexpr auto quadrature_rule = + mito::quadrature::quadrature_rule(); + + // the parametric point type + using point_t = decltype(quadrature_rule)::quadrature_point_type; + + // a polynomial of degree 4 in parametric coordinates + constexpr auto f = [](const point_t & x) -> mito::tensor::scalar_t { + return x[0] * x[0] * x[0] * x[0]; + }; + + // area of the parametric triangle + constexpr auto area = 0.5; + + // integral of f on the parametric triangle + constexpr auto integral = area + * (quadrature_rule.weight(0) * f(quadrature_rule.point(0)) + + quadrature_rule.weight(1) * f(quadrature_rule.point(1)) + + quadrature_rule.weight(2) * f(quadrature_rule.point(2)) + + quadrature_rule.weight(3) * f(quadrature_rule.point(3)) + + quadrature_rule.weight(4) * f(quadrature_rule.point(4)) + + quadrature_rule.weight(5) * f(quadrature_rule.point(5))); + + // exact solution + constexpr auto exact = 1.0 / 30.0; + + // check result + static_assert(std::fabs(integral - exact) < 1.e-16); +} + // end of file diff --git a/tests/mito.lib/solvers/petsc_external_initialize.cc b/tests/mito.lib/solvers/petsc_external_initialize.cc deleted file mode 100644 index 31ce390b2..000000000 --- a/tests/mito.lib/solvers/petsc_external_initialize.cc +++ /dev/null @@ -1,31 +0,0 @@ -// -*- c++ -*- -// -// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved -// - - -#include -#include - - -TEST(Solvers, PETScKSPExternalInitialize) -{ - // initialize PETSc - PetscInitializeNoArguments(); - - // the size of the linear system - int N = 10; - - // instantiate a PETSc Krylov solver for a linear system of size {N} - auto solver = mito::solvers::petsc::ksp("mysolver"); - solver.initialize(N); - - // finalize the solver - solver.finalize(); - - // finalize PETSc - PetscFinalize(); -} - - -// end of file diff --git a/tests/mito.lib/solvers/petsc_internal_initialize.cc b/tests/mito.lib/solvers/petsc_internal_initialize.cc deleted file mode 100644 index 9930302dd..000000000 --- a/tests/mito.lib/solvers/petsc_internal_initialize.cc +++ /dev/null @@ -1,25 +0,0 @@ -// -*- c++ -*- -// -// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved -// - - -#include -#include - - -TEST(Solvers, PETScKSPInternalInitialize) -{ - // the size of the linear system - int N = 10; - - // instantiate a PETSc Krylov solver for a linear system of size {N} - auto solver = mito::solvers::petsc::ksp("mysolver"); - solver.initialize(N); - - // finalize the solver - solver.finalize(); -} - - -// end of file diff --git a/tests/mito.lib/utilities/named_class.cc b/tests/mito.lib/utilities/named_class.cc new file mode 100644 index 000000000..d24c6c855 --- /dev/null +++ b/tests/mito.lib/utilities/named_class.cc @@ -0,0 +1,47 @@ +// -*- c++ -*- +// +// Copyright (c) 2020-2024, the MiTo Authors, all rights reserved +// + +#include +#include + + +using namespace mito::utilities; + + +// a simple class to test NamedClass +class Foo { + public: + // my type + using foo_type = Foo; + + public: + // constructor + Foo(int foo) : _foo(foo) {} + + // accessor + int foo() const { return _foo; } + + private: + // an attribute + int _foo; +}; + + +// the resource type +using foo_t = NamedClass; + + +TEST(Utilities, NamedClass) +{ + // instantiate a named class + auto foo = foo_t(42); + + // assert that the name is "unnamed" + EXPECT_EQ(foo.name(), "unnamed"); + + // instantiate a named class with a name + auto bar = foo_t("bar", 43); + EXPECT_EQ(bar.name(), "bar"); +}