dualmesh-multiphysics 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. dualmesh_multiphysics-0.1.0/.clang-format +13 -0
  2. dualmesh_multiphysics-0.1.0/.github/workflows/ci.yml +116 -0
  3. dualmesh_multiphysics-0.1.0/.github/workflows/wheels.yml +63 -0
  4. dualmesh_multiphysics-0.1.0/.gitignore +44 -0
  5. dualmesh_multiphysics-0.1.0/.readthedocs.yaml +28 -0
  6. dualmesh_multiphysics-0.1.0/CHANGELOG.md +167 -0
  7. dualmesh_multiphysics-0.1.0/CITATION.cff +37 -0
  8. dualmesh_multiphysics-0.1.0/CMakeLists.txt +141 -0
  9. dualmesh_multiphysics-0.1.0/CONTRIBUTING.md +62 -0
  10. dualmesh_multiphysics-0.1.0/LICENSE +502 -0
  11. dualmesh_multiphysics-0.1.0/PKG-INFO +198 -0
  12. dualmesh_multiphysics-0.1.0/README.md +150 -0
  13. dualmesh_multiphysics-0.1.0/docs/_generate_syntax.py +333 -0
  14. dualmesh_multiphysics-0.1.0/docs/_syntax_examples.py +818 -0
  15. dualmesh_multiphysics-0.1.0/docs/api.rst +108 -0
  16. dualmesh_multiphysics-0.1.0/docs/conf.py +78 -0
  17. dualmesh_multiphysics-0.1.0/docs/developing.rst +122 -0
  18. dualmesh_multiphysics-0.1.0/docs/getting_started.rst +159 -0
  19. dualmesh_multiphysics-0.1.0/docs/index.rst +183 -0
  20. dualmesh_multiphysics-0.1.0/docs/input_files.rst +117 -0
  21. dualmesh_multiphysics-0.1.0/docs/installation.rst +67 -0
  22. dualmesh_multiphysics-0.1.0/docs/openfoam.rst +157 -0
  23. dualmesh_multiphysics-0.1.0/docs/references.rst +540 -0
  24. dualmesh_multiphysics-0.1.0/docs/scope.rst +176 -0
  25. dualmesh_multiphysics-0.1.0/docs/theory/adaptivity.rst +522 -0
  26. dualmesh_multiphysics-0.1.0/docs/theory/elements.rst +745 -0
  27. dualmesh_multiphysics-0.1.0/docs/theory/finite_volume.rst +589 -0
  28. dualmesh_multiphysics-0.1.0/docs/theory/foundations.rst +239 -0
  29. dualmesh_multiphysics-0.1.0/docs/theory/heat_and_fluids.rst +671 -0
  30. dualmesh_multiphysics-0.1.0/docs/theory/index.rst +36 -0
  31. dualmesh_multiphysics-0.1.0/docs/theory/nonlinear_and_time.rst +88 -0
  32. dualmesh_multiphysics-0.1.0/docs/theory/parallel.rst +604 -0
  33. dualmesh_multiphysics-0.1.0/docs/theory/solid_mechanics.rst +576 -0
  34. dualmesh_multiphysics-0.1.0/docs/tutorials/custom_kernel.rst +38 -0
  35. dualmesh_multiphysics-0.1.0/docs/tutorials/elasticity.rst +25 -0
  36. dualmesh_multiphysics-0.1.0/docs/tutorials/flow.rst +21 -0
  37. dualmesh_multiphysics-0.1.0/docs/tutorials/heat_conduction.rst +47 -0
  38. dualmesh_multiphysics-0.1.0/docs/tutorials/index.rst +15 -0
  39. dualmesh_multiphysics-0.1.0/docs/tutorials/structures.rst +33 -0
  40. dualmesh_multiphysics-0.1.0/docs/tutorials/transient.rst +21 -0
  41. dualmesh_multiphysics-0.1.0/docs/user_guide/index.rst +19 -0
  42. dualmesh_multiphysics-0.1.0/docs/user_guide/meshes.rst +539 -0
  43. dualmesh_multiphysics-0.1.0/docs/user_guide/output.rst +212 -0
  44. dualmesh_multiphysics-0.1.0/docs/user_guide/problem_setup.rst +333 -0
  45. dualmesh_multiphysics-0.1.0/docs/user_guide/solving.rst +388 -0
  46. dualmesh_multiphysics-0.1.0/docs/verification.rst +296 -0
  47. dualmesh_multiphysics-0.1.0/examples/bus_bar.yaml +52 -0
  48. dualmesh_multiphysics-0.1.0/examples/cooling_fin.py +35 -0
  49. dualmesh_multiphysics-0.1.0/examples/custom_kernel.py +45 -0
  50. dualmesh_multiphysics-0.1.0/examples/functionally_graded_beam.py +69 -0
  51. dualmesh_multiphysics-0.1.0/examples/lid_driven_cavity.py +93 -0
  52. dualmesh_multiphysics-0.1.0/examples/natural_convection.py +117 -0
  53. dualmesh_multiphysics-0.1.0/examples/plate_with_hole.py +63 -0
  54. dualmesh_multiphysics-0.1.0/examples/transient_slab.py +52 -0
  55. dualmesh_multiphysics-0.1.0/include/dualmesh/base/Factory.h +77 -0
  56. dualmesh_multiphysics-0.1.0/include/dualmesh/base/Kernel.h +110 -0
  57. dualmesh_multiphysics-0.1.0/include/dualmesh/base/Material.h +58 -0
  58. dualmesh_multiphysics-0.1.0/include/dualmesh/base/Object.h +98 -0
  59. dualmesh_multiphysics-0.1.0/include/dualmesh/base/Problem.h +478 -0
  60. dualmesh_multiphysics-0.1.0/include/dualmesh/base/QpContext.h +71 -0
  61. dualmesh_multiphysics-0.1.0/include/dualmesh/core/ADReal.h +387 -0
  62. dualmesh_multiphysics-0.1.0/include/dualmesh/core/Function.h +69 -0
  63. dualmesh_multiphysics-0.1.0/include/dualmesh/core/InputParameters.h +123 -0
  64. dualmesh_multiphysics-0.1.0/include/dualmesh/core/ParsedFunction.h +91 -0
  65. dualmesh_multiphysics-0.1.0/include/dualmesh/core/Types.h +80 -0
  66. dualmesh_multiphysics-0.1.0/include/dualmesh/fe/Assembly.h +95 -0
  67. dualmesh_multiphysics-0.1.0/include/dualmesh/fe/ReferenceElement.h +223 -0
  68. dualmesh_multiphysics-0.1.0/include/dualmesh/fv/CellMesh.h +90 -0
  69. dualmesh_multiphysics-0.1.0/include/dualmesh/linalg/IncompleteLU.h +83 -0
  70. dualmesh_multiphysics-0.1.0/include/dualmesh/mesh/Adapt.h +57 -0
  71. dualmesh_multiphysics-0.1.0/include/dualmesh/mesh/Mesh.h +196 -0
  72. dualmesh_multiphysics-0.1.0/include/dualmesh/modules/Framework.h +177 -0
  73. dualmesh_multiphysics-0.1.0/include/dualmesh/parallel/Communicator.h +68 -0
  74. dualmesh_multiphysics-0.1.0/include/dualmesh/parallel/DistributedProblem.h +205 -0
  75. dualmesh_multiphysics-0.1.0/include/dualmesh/parallel/Partition.h +80 -0
  76. dualmesh_multiphysics-0.1.0/pyproject.toml +72 -0
  77. dualmesh_multiphysics-0.1.0/python/bindings/module.cpp +1064 -0
  78. dualmesh_multiphysics-0.1.0/python/dualmesh/__init__.py +162 -0
  79. dualmesh_multiphysics-0.1.0/python/dualmesh/ad.py +60 -0
  80. dualmesh_multiphysics-0.1.0/python/dualmesh/adaptivity.py +164 -0
  81. dualmesh_multiphysics-0.1.0/python/dualmesh/cli.py +348 -0
  82. dualmesh_multiphysics-0.1.0/python/dualmesh/expressions.py +50 -0
  83. dualmesh_multiphysics-0.1.0/python/dualmesh/fgm.py +127 -0
  84. dualmesh_multiphysics-0.1.0/python/dualmesh/meshing.py +417 -0
  85. dualmesh_multiphysics-0.1.0/python/dualmesh/mms.py +542 -0
  86. dualmesh_multiphysics-0.1.0/python/dualmesh/objects.py +234 -0
  87. dualmesh_multiphysics-0.1.0/python/dualmesh/parallel.py +331 -0
  88. dualmesh_multiphysics-0.1.0/python/dualmesh/physics.py +338 -0
  89. dualmesh_multiphysics-0.1.0/python/dualmesh/postprocess.py +64 -0
  90. dualmesh_multiphysics-0.1.0/python/dualmesh/problem.py +552 -0
  91. dualmesh_multiphysics-0.1.0/src/base/Objects.cpp +478 -0
  92. dualmesh_multiphysics-0.1.0/src/base/Problem.cpp +589 -0
  93. dualmesh_multiphysics-0.1.0/src/base/ProblemAssembly.cpp +1082 -0
  94. dualmesh_multiphysics-0.1.0/src/base/ProblemFiniteVolume.cpp +651 -0
  95. dualmesh_multiphysics-0.1.0/src/base/ProblemPostprocess.cpp +549 -0
  96. dualmesh_multiphysics-0.1.0/src/core/Function.cpp +37 -0
  97. dualmesh_multiphysics-0.1.0/src/core/InputParameters.cpp +332 -0
  98. dualmesh_multiphysics-0.1.0/src/core/ParsedFunction.cpp +459 -0
  99. dualmesh_multiphysics-0.1.0/src/fe/Assembly.cpp +842 -0
  100. dualmesh_multiphysics-0.1.0/src/fe/ReferenceElement.cpp +1268 -0
  101. dualmesh_multiphysics-0.1.0/src/fv/CellMesh.cpp +237 -0
  102. dualmesh_multiphysics-0.1.0/src/linalg/IncompleteLU.cpp +97 -0
  103. dualmesh_multiphysics-0.1.0/src/mesh/Adapt.cpp +271 -0
  104. dualmesh_multiphysics-0.1.0/src/mesh/Mesh.cpp +1235 -0
  105. dualmesh_multiphysics-0.1.0/src/modules/Fluids.cpp +382 -0
  106. dualmesh_multiphysics-0.1.0/src/modules/Framework.cpp +632 -0
  107. dualmesh_multiphysics-0.1.0/src/modules/HeatTransfer.cpp +393 -0
  108. dualmesh_multiphysics-0.1.0/src/modules/SolidMechanics.cpp +511 -0
  109. dualmesh_multiphysics-0.1.0/src/modules/SolidMechanicsStructures.cpp +978 -0
  110. dualmesh_multiphysics-0.1.0/src/parallel/Communicator.cpp +254 -0
  111. dualmesh_multiphysics-0.1.0/src/parallel/DistributedProblem.cpp +1033 -0
  112. dualmesh_multiphysics-0.1.0/src/parallel/Partition.cpp +375 -0
  113. dualmesh_multiphysics-0.1.0/tests/cpp/benchmark.cpp +134 -0
  114. dualmesh_multiphysics-0.1.0/tests/cpp/parallel_tests.cpp +435 -0
  115. dualmesh_multiphysics-0.1.0/tests/cpp/unit_tests.cpp +568 -0
  116. dualmesh_multiphysics-0.1.0/tests/python/conftest.py +30 -0
  117. dualmesh_multiphysics-0.1.0/tests/python/test_adaptivity.py +394 -0
  118. dualmesh_multiphysics-0.1.0/tests/python/test_beams.py +313 -0
  119. dualmesh_multiphysics-0.1.0/tests/python/test_element_types.py +289 -0
  120. dualmesh_multiphysics-0.1.0/tests/python/test_finite_volume.py +396 -0
  121. dualmesh_multiphysics-0.1.0/tests/python/test_fluids.py +288 -0
  122. dualmesh_multiphysics-0.1.0/tests/python/test_framework.py +848 -0
  123. dualmesh_multiphysics-0.1.0/tests/python/test_heat_transfer.py +457 -0
  124. dualmesh_multiphysics-0.1.0/tests/python/test_mms.py +423 -0
  125. dualmesh_multiphysics-0.1.0/tests/python/test_multiphysics.py +160 -0
  126. dualmesh_multiphysics-0.1.0/tests/python/test_parallel.py +263 -0
  127. dualmesh_multiphysics-0.1.0/tests/python/test_plates.py +463 -0
  128. dualmesh_multiphysics-0.1.0/tests/python/test_quadratic_elements.py +460 -0
  129. dualmesh_multiphysics-0.1.0/tests/python/test_solid_mechanics.py +527 -0
  130. dualmesh_multiphysics-0.1.0/tests/python/test_time_stepping.py +284 -0
  131. dualmesh_multiphysics-0.1.0/verification/openfoam/README.md +25 -0
  132. dualmesh_multiphysics-0.1.0/verification/openfoam/cavity_openfoam.sh +70 -0
  133. dualmesh_multiphysics-0.1.0/verification/openfoam/compare_cavity.py +229 -0
  134. dualmesh_multiphysics-0.1.0/verification/openfoam/compare_conduction.py +107 -0
  135. dualmesh_multiphysics-0.1.0/verification/openfoam/compare_plate_hole.py +168 -0
  136. dualmesh_multiphysics-0.1.0/verification/openfoam/conduction_openfoam.sh +137 -0
  137. dualmesh_multiphysics-0.1.0/verification/openfoam/plate_hole_openfoam.sh +37 -0
  138. dualmesh_multiphysics-0.1.0/verification/openfoam/run_all.sh +9 -0
@@ -0,0 +1,13 @@
1
+ # Based on the MOOSE framework style.
2
+ BasedOnStyle: LLVM
3
+ AlwaysBreakAfterReturnType: TopLevelDefinitions
4
+ AlignAfterOpenBracket: Align
5
+ AllowShortFunctionsOnASingleLine: Inline
6
+ BinPackArguments: false
7
+ BinPackParameters: false
8
+ BreakBeforeBraces: Allman
9
+ ColumnLimit: 100
10
+ IndentWidth: 2
11
+ PointerAlignment: Middle
12
+ SpaceAfterCStyleCast: true
13
+ Standard: c++17
@@ -0,0 +1,116 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ cpp:
15
+ name: C++ (${{ matrix.os }})
16
+ runs-on: ${{ matrix.os }}
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ os: [ubuntu-latest, macos-latest, windows-latest]
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - uses: actions/setup-python@v5
24
+ with:
25
+ python-version: "3.11"
26
+ - name: Configure
27
+ run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DDUALMESH_BUILD_PYTHON=OFF
28
+ - name: Build
29
+ run: cmake --build build --config Release -j
30
+ - name: Test
31
+ run: ctest --test-dir build --build-config Release --output-on-failure
32
+
33
+ mpi:
34
+ name: MPI (1, 2, 3 and 4 processes)
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+ - name: Install MPI and METIS
39
+ run: sudo apt-get update && sudo apt-get install -y libopenmpi-dev openmpi-bin libmetis-dev libeigen3-dev
40
+ - name: Configure
41
+ run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DDUALMESH_BUILD_PYTHON=OFF -DDUALMESH_ENABLE_MPI=ON
42
+ - name: Build
43
+ run: cmake --build build -j
44
+ - name: Distributed tests
45
+ run: |
46
+ for n in 1 2 3 4; do
47
+ mpirun --oversubscribe -n $n ./build/dualmesh_parallel_tests
48
+ done
49
+
50
+ python:
51
+ name: Python ${{ matrix.python-version }} (${{ matrix.os }})
52
+ runs-on: ${{ matrix.os }}
53
+ strategy:
54
+ fail-fast: false
55
+ matrix:
56
+ os: [ubuntu-latest, macos-latest]
57
+ python-version: ["3.9", "3.11", "3.12"]
58
+ steps:
59
+ - uses: actions/checkout@v4
60
+ - uses: actions/setup-python@v5
61
+ with:
62
+ python-version: ${{ matrix.python-version }}
63
+ - name: Install
64
+ run: pip install .[all,test]
65
+ - name: Verification suite
66
+ run: pytest -q
67
+ - name: Examples
68
+ run: |
69
+ python examples/cooling_fin.py
70
+ python examples/custom_kernel.py
71
+ python examples/functionally_graded_beam.py
72
+ python examples/transient_slab.py
73
+ python examples/natural_convection.py
74
+ - name: Command line
75
+ run: |
76
+ dualmesh --version
77
+ dualmesh list --module heat_transfer
78
+ dualmesh describe HeatConduction
79
+ cd examples && dualmesh run bus_bar.yaml
80
+
81
+ lint:
82
+ name: Lint
83
+ runs-on: ubuntu-latest
84
+ steps:
85
+ - uses: actions/checkout@v4
86
+ - uses: actions/setup-python@v5
87
+ with:
88
+ python-version: "3.11"
89
+ - name: Install tools
90
+ run: pip install ruff
91
+ - name: Ruff
92
+ run: ruff check python tests examples
93
+ - name: clang-format
94
+ run: |
95
+ sudo apt-get update && sudo apt-get install -y clang-format
96
+ find include src python/bindings tests/cpp -name '*.h' -o -name '*.cpp' \
97
+ | xargs clang-format --dry-run --Werror
98
+
99
+ docs:
100
+ name: Documentation
101
+ runs-on: ubuntu-latest
102
+ steps:
103
+ - uses: actions/checkout@v4
104
+ - uses: actions/setup-python@v5
105
+ with:
106
+ python-version: "3.11"
107
+ - name: Install
108
+ run: |
109
+ sudo apt-get update && sudo apt-get install -y libeigen3-dev
110
+ pip install .[docs,all]
111
+ - name: Build documentation
112
+ run: sphinx-build -b html docs docs/_build/html
113
+ - uses: actions/upload-artifact@v4
114
+ with:
115
+ name: documentation
116
+ path: docs/_build/html
@@ -0,0 +1,63 @@
1
+ name: Wheels
2
+
3
+ # Binary wheels, so that `pip install dualmesh-multiphysics` needs no compiler.
4
+ #
5
+ # The wheels are built on every tag that starts with "v" and on demand, and are
6
+ # kept as workflow artifacts. Publishing to PyPI is the last job; it runs only
7
+ # for a tag, and uses PyPI's trusted publishing, which has to be enabled once
8
+ # for the repository on pypi.org (Publishing -> Add a new publisher) before the
9
+ # first release.
10
+
11
+ on:
12
+ push:
13
+ tags: ["v*"]
14
+ workflow_dispatch:
15
+
16
+ jobs:
17
+ wheels:
18
+ name: Wheels (${{ matrix.os }})
19
+ runs-on: ${{ matrix.os }}
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ os: [ubuntu-latest, macos-15-intel, macos-14, windows-latest]
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+ - uses: pypa/cibuildwheel@v4.2.1
27
+ env:
28
+ CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-*"
29
+ CIBW_SKIP: "*-musllinux_* *-win32 *-manylinux_i686"
30
+ CIBW_TEST_EXTRAS: "test"
31
+ CIBW_TEST_COMMAND: >-
32
+ pytest {project}/tests/python -q -x
33
+ --ignore={project}/tests/python/test_mms.py
34
+ - uses: actions/upload-artifact@v4
35
+ with:
36
+ name: wheels-${{ matrix.os }}
37
+ path: wheelhouse/*.whl
38
+
39
+ sdist:
40
+ name: Source distribution
41
+ runs-on: ubuntu-latest
42
+ steps:
43
+ - uses: actions/checkout@v4
44
+ - run: pipx run build --sdist
45
+ - uses: actions/upload-artifact@v4
46
+ with:
47
+ name: sdist
48
+ path: dist/*.tar.gz
49
+
50
+ publish:
51
+ name: Publish to PyPI
52
+ if: startsWith(github.ref, 'refs/tags/v')
53
+ needs: [wheels, sdist]
54
+ runs-on: ubuntu-latest
55
+ environment: pypi
56
+ permissions:
57
+ id-token: write
58
+ steps:
59
+ - uses: actions/download-artifact@v4
60
+ with:
61
+ path: dist
62
+ merge-multiple: true
63
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,44 @@
1
+ # build artefacts
2
+ build/
3
+ build-*/
4
+ _build/
5
+ dist/
6
+ *.egg-info/
7
+ *.so
8
+ *.pyd
9
+ *.dylib
10
+ *.o
11
+ *.a
12
+ CMakeCache.txt
13
+ CMakeFiles/
14
+
15
+ # python
16
+ __pycache__/
17
+ *.py[cod]
18
+ .pytest_cache/
19
+ .ruff_cache/
20
+ .venv/
21
+ venv/
22
+
23
+ # results
24
+ *.vtu
25
+ *.pvtu
26
+ *.pvd
27
+ *.csv
28
+ *.e
29
+ *.exo
30
+ !examples/**/*.csv
31
+
32
+ # editors and systems
33
+ .vscode/
34
+ .idea/
35
+ .DS_Store
36
+
37
+ # OpenFOAM cases
38
+ verification/openfoam/run/
39
+
40
+ # generated by the documentation build
41
+ docs/objects.rst
42
+
43
+ # generated when the documentation is built
44
+ docs/syntax/
@@ -0,0 +1,28 @@
1
+ version: 2
2
+
3
+ build:
4
+ os: ubuntu-22.04
5
+ tools:
6
+ python: "3.11"
7
+ apt_packages:
8
+ - cmake
9
+ - libeigen3-dev
10
+ jobs:
11
+ post_checkout:
12
+ - git fetch --unshallow || true
13
+
14
+ python:
15
+ install:
16
+ - method: pip
17
+ path: .
18
+ extra_requirements:
19
+ - docs
20
+ - all
21
+
22
+ sphinx:
23
+ configuration: docs/conf.py
24
+ fail_on_warning: false
25
+
26
+ formats:
27
+ - pdf
28
+ - htmlzip
@@ -0,0 +1,167 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project uses
5
+ [semantic versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ dualmesh is now described, and organised, as what it is: a multiphysics
10
+ framework for heat transfer, solid mechanics and fluid dynamics, in which the
11
+ finite element method, two finite volume methods and the dual mesh control
12
+ domain method are four discretisations of one problem description.
13
+
14
+ ### Added
15
+
16
+ - **Four new element types**: the serendipity `Quad8` and `Hex20`, the
17
+ triangular prism `Wedge6` and the pyramid `Pyramid5` (with Bedrosian's
18
+ rational basis). Meshes may mix types freely. Every type works with the
19
+ finite element and cell-centred finite volume methods; the dual mesh and
20
+ vertex-centred methods, which need node-centred control domains, accept the
21
+ prism (whose dual is the triangle's median dual times a half-segment) and
22
+ refuse the serendipity elements and the pyramid with the reason and the
23
+ methods that do accept them. `Mesh.second_order(serendipity=True)`,
24
+ generators for all four, uniform refinement of prisms and pyramids, and
25
+ meshio reading and writing.
26
+ - **Systematic verification by manufactured solutions**: `dualmesh.mms`
27
+ derives the forcing of a chosen solution symbolically (diffusion,
28
+ advection, reaction, time derivative, linear elasticity; Cartesian,
29
+ axisymmetric and spherical) and runs convergence studies;
30
+ `Problem.error_norms` measures the L2 error and the H1 seminorm. The suite
31
+ runs 161 studies and every observed order matches the theory of its method.
32
+ - **Coupled heat transfer and flow**: `HeatConvection` (the flow carries the
33
+ heat) and `BoussinesqBuoyancy` (the temperature drives the flow), with
34
+ `physics.add_boussinesq_buoyancy`. The natural convection benchmark of de
35
+ Vahl Davis is reproduced to within 0.2 % in the Nusselt number
36
+ (`examples/natural_convection.py`).
37
+ - **Overlapping Schwarz**: the distributed preconditioners now work on
38
+ subdomains that overlap by `overlap` layers of elements (default 1), with
39
+ fully assembled subdomain matrices exchanged between ranks, an exact or
40
+ incomplete subdomain solve (`subdomain_solver`), and the two levels combined
41
+ multiplicatively. The iteration count of the default no longer grows with
42
+ the number of processes: 23 to 25 iterations from 1 to 16 ranks on a 48 by 48
43
+ Poisson problem. With `linear_solver="cg"` the symmetric (classical) forms
44
+ are used.
45
+ - **Automatic linear solver** (`linear_solver="automatic"`, the new default):
46
+ a direct factorisation where it is cheap and BiCGSTAB with a new ILU(0)
47
+ preconditioner where it is not, falling back to the direct solver if the
48
+ iteration fails. Also `"gmres"` and the `preconditioner` option (`ilu`,
49
+ `ilut`, `jacobi`, `none`). On a three-dimensional elasticity system of 36 000
50
+ unknowns the solve fell from 18 s to 0.3 s.
51
+ - `Problem.linear_system()` returns the residual and the Jacobian as NumPy and
52
+ SciPy objects, for use with other solvers.
53
+ - Expressions are compiled by a C++ parser (`ParsedFunction`), and a string
54
+ that is not the name of a registered function is compiled as an expression.
55
+ - The command-line driver runs an input file under `mpirun` with the
56
+ distributed solver (configured by a `parallel` block), accepts `threads`, and
57
+ refuses an unknown block or setting with a suggestion.
58
+ - "Did you mean" suggestions for misspelled object types, parameters,
59
+ variables, boundaries, blocks and solver options.
60
+ - A continuous-integration job that runs the distributed tests on 1 to 4
61
+ processes, and a workflow that builds binary wheels.
62
+ - Documentation: a scope chapter that defines the package as a multiphysics
63
+ framework and compares it honestly with MOOSE and COMSOL; a
64
+ manufactured-solutions chapter; the new elements, solvers and
65
+ preconditioners; the current MOOSE citation (MOOSE 4.0, 2025).
66
+
67
+ ### Changed
68
+
69
+ - **The structural module is merged into solid mechanics.** Beams and plates
70
+ are the reduced theories of solid mechanics and are now registered in the
71
+ `solid_mechanics` module, next to continuum elasticity.
72
+ - **The default distributed preconditioner is `two_level_schwarz` with an
73
+ overlap of one element layer** (it was `jacobi`).
74
+ - **The cell-centred finite volume method has an exact Jacobian**: the
75
+ non-orthogonal correction is differentiated through the least-squares
76
+ stencils instead of being lagged, so Newton's method converges
77
+ quadratically on skewed meshes.
78
+ - **Efficiency**: element geometry is mapped once per integration point and
79
+ reference-element data are cached; the derivative arrays of the automatic
80
+ differentiation are copied only as far as they are used and their loops
81
+ vectorise; symmetric quadrature rules on triangles, tetrahedra and prisms
82
+ replace the collapsed tensor rules for the finite element method (8 points
83
+ instead of 27 on a tetrahedron); Gauss rules are tabulated. On the bundled
84
+ benchmark (three-dimensional elasticity on 8000 `Hex8` elements) the
85
+ assembly of every method is about three times faster on one thread; the
86
+ Python test suite runs in about 60 % of its former time.
87
+ - All element types use the VTK node numbering, so files need no permutation.
88
+ - Package metadata points to the GitHub repository.
89
+
90
+ ### Fixed
91
+
92
+ - **The θ-method evaluated the old part of the residual at the new time**,
93
+ which cost Crank-Nicolson its second order whenever a coefficient or a
94
+ source depends on time (serial and distributed).
95
+ - **The Schwarz preconditioners factorised each rank's partial local matrix**
96
+ instead of the fully assembled subdomain matrix, which is why they stopped
97
+ converging at eight ranks.
98
+ - **The penalty formulation of incompressible flow locked on the
99
+ vertex-centred finite volume method**: the two-point gradient correction was
100
+ applied to the reduced-integration term. It is no longer, and the
101
+ cell-centred method, on which the formulation cannot work, now refuses it.
102
+ - **A compiled expression passed as a parameter was evaluated through
103
+ Python**, because pybind11's function test is Python's `callable()`, which
104
+ also serialised the assembly onto one thread.
105
+ - **Thermal stress in plane strain was wrong by a factor of `1 + nu`.** The
106
+ stiffness matrix left the out-of-plane column empty, so the thermal strain
107
+ subtracted from the out-of-plane normal strain never reached the in-plane
108
+ rows. For a fully restrained body the code gave
109
+ `E alpha dT / [(1 + nu)(1 - 2 nu)]` where the correct value is
110
+ `E alpha dT / (1 - 2 nu)`. Plane stress was unaffected.
111
+ - **A Python callable passed directly to a parameter no longer deadlocks the
112
+ threaded assembly.**
113
+ - **The cell-centred finite volume method is refused by the distributed
114
+ solver** instead of returning a wrong answer.
115
+ - **The distributed BiCGSTAB detects near-breakdown and restarts.**
116
+ - The Python test suite no longer imports the source tree instead of the
117
+ installed package when the extension has not been built in place.
118
+
119
+ ### Earlier unreleased work
120
+
121
+ - **Quadratic elements** `Edge3`, `Tri6`, `Quad9`, `Tet10` and `Hex27`, with
122
+ the dual mesh built for each; **adaptive mesh refinement** by gradient
123
+ recovery, marking and longest-edge bisection; the automatic quadrature
124
+ order; `DUALMESH_MAX_AD_DERIVATIVES`; the manual reorganised into a user
125
+ guide and a theory manual with every method cited to its primary source.
126
+
127
+ ## [0.1.0] - 2026-09-18
128
+
129
+ The first release: the framework, five physics modules, and a verification
130
+ suite that reproduces the published results of Reddy's book.
131
+
132
+ ### Added
133
+
134
+ - **Framework**: the canonical conservation form `-div F + S = 0`; the median
135
+ dual mesh for `Edge2`, `Tri3`, `Quad4`, `Tet4` and `Hex8`; assembly for the
136
+ dual mesh control domain method and for the Galerkin finite element method
137
+ from the same kernels; forward-mode automatic differentiation for exact
138
+ Jacobians; per-kernel quadrature (Gauss, midpoint, trapezoid, Simpson,
139
+ nodal, interface, control-domain trapezoid) and selective reduced
140
+ integration; Cartesian, axisymmetric and spherical coordinates; blocks
141
+ (subdomains), side sets and node sets.
142
+ - **Solvers**: Newton's method, direct (Picard) iteration with relaxation,
143
+ load stepping, steady and transient (θ-method) executioners, sparse LU,
144
+ BiCGSTAB and conjugate gradient linear solvers.
145
+ - **Heat transfer module**: conduction with temperature-dependent
146
+ conductivity, volumetric heating, heat capacity, convective, radiative and
147
+ prescribed-flux boundary conditions.
148
+ - **Solid mechanics module**: linear elasticity (plane stress, plane strain,
149
+ axisymmetric, three-dimensional), traction and pressure boundary conditions,
150
+ stress and strain as material properties.
151
+ - **Structural module**: mixed Euler–Bernoulli beam, displacement and mixed
152
+ Timoshenko beams, first-order axisymmetric circular plate, first-order
153
+ rectangular plate, functionally graded sections, von Kármán nonlinearity.
154
+ - **Fluids module**: penalty formulation of the Stokes and Navier–Stokes
155
+ equations with recovered pressure.
156
+ - **Meshing**: line, rectangle, box and annulus generators with graded
157
+ spacing, uniform refinement, geometric side sets and node sets, and readers
158
+ and writers for Gmsh, Exodus, VTK, Abaqus and the other formats meshio
159
+ supports.
160
+ - **Python interface**: `Problem`, mesh generators, physics helpers,
161
+ post-processing, functionally graded stiffness formulas, and base classes
162
+ for kernels, boundary conditions and materials written in Python.
163
+ - **Command line**: `dualmesh run input.yaml`, `dualmesh list`,
164
+ `dualmesh describe <type>`.
165
+ - **Verification**: the published dual mesh results of Chapters 3, 5, 6, 7, 8,
166
+ 9 and 10 of Reddy's book, plus patch tests, conservation tests and
167
+ cross-verification against OpenFOAM.
@@ -0,0 +1,37 @@
1
+ cff-version: 1.2.0
2
+ title: "dualmesh: a multiphysics framework for heat transfer, solid mechanics and fluid dynamics"
3
+ message: "If you use this software, please cite both the software and the method (see references)."
4
+ type: software
5
+ version: 0.1.0
6
+ license: LGPL-2.1-or-later
7
+ repository-code: "https://github.com/MohamedAbdulHameed/dualmesh"
8
+ url: "https://dualmesh.readthedocs.io"
9
+ abstract: >-
10
+ dualmesh is a multiphysics framework for heat transfer, solid mechanics and
11
+ fluid dynamics. Coupled problems are solved monolithically by Newton's method
12
+ with exact Jacobians from automatic differentiation, and the same problem
13
+ definition is discretised by the Galerkin finite element method, the
14
+ vertex-centred or cell-centred finite volume method, or the dual mesh control
15
+ domain method (DMCDM) of J. N. Reddy. The library is written in
16
+ C++17 with Python bindings, is organized as a modular framework of kernels,
17
+ boundary conditions and materials, and is verified against the published
18
+ results of Reddy's book and against OpenFOAM.
19
+ keywords:
20
+ - dual mesh control domain method
21
+ - finite element method
22
+ - finite volume method
23
+ - fluid dynamics
24
+ - solid mechanics
25
+ - heat transfer
26
+ - multiphysics
27
+ authors:
28
+ - name: "The dualmesh developers"
29
+ references:
30
+ - type: book
31
+ title: "Computational Methods in Engineering: Finite Difference, Finite Volume, Finite Element, and Dual Mesh Control Domain Methods"
32
+ authors:
33
+ - family-names: Reddy
34
+ given-names: "J. N."
35
+ publisher:
36
+ name: "CRC Press"
37
+ year: 2024
@@ -0,0 +1,141 @@
1
+ cmake_minimum_required(VERSION 3.18)
2
+ project(dualmesh VERSION 0.1.0 LANGUAGES CXX)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
7
+ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
8
+ set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
9
+ endif()
10
+
11
+ option(DUALMESH_BUILD_PYTHON "Build the Python extension module" ON)
12
+ option(DUALMESH_BUILD_TESTS "Build the C++ unit tests" ON)
13
+ option(DUALMESH_ENABLE_OPENMP "Thread the assembly loops with OpenMP" ON)
14
+ option(DUALMESH_ENABLE_MPI "Build the distributed (MPI) solver" OFF)
15
+ option(DUALMESH_ENABLE_METIS "Use METIS for mesh partitioning when available" ON)
16
+ # Automatic differentiation seeds one derivative slot per element node and
17
+ # variable, so the default of 48 covers, for example, Quad9 with five fields,
18
+ # Hex8 with six and Hex27 with one. Raise it for a multi-field problem on a
19
+ # Hex27 mesh; every ADReal grows with it, so raising it costs memory and
20
+ # assembly time everywhere.
21
+ set(DUALMESH_MAX_AD_DERIVATIVES 48 CACHE STRING
22
+ "Maximum seeded derivatives per element (nodes per element x variables)")
23
+
24
+ # ---- dependencies ---------------------------------------------------------
25
+ # Eigen is header-only. A system installation is used when there is one
26
+ # (macOS: `brew install eigen`; Debian/Ubuntu: `apt install libeigen3-dev`);
27
+ # otherwise the headers are downloaded into the build tree.
28
+ find_package(Eigen3 3.3 QUIET NO_MODULE)
29
+ if(NOT Eigen3_FOUND)
30
+ set(DUALMESH_EIGEN_VERSION 3.4.0)
31
+ set(DUALMESH_EIGEN_DIR ${CMAKE_BINARY_DIR}/_deps/eigen-${DUALMESH_EIGEN_VERSION})
32
+ if(NOT EXISTS ${DUALMESH_EIGEN_DIR}/Eigen/Dense)
33
+ set(DUALMESH_EIGEN_ARCHIVE ${CMAKE_BINARY_DIR}/_deps/eigen.tar.gz)
34
+ message(STATUS "Eigen not found; downloading Eigen ${DUALMESH_EIGEN_VERSION}")
35
+ file(DOWNLOAD
36
+ https://gitlab.com/libeigen/eigen/-/archive/${DUALMESH_EIGEN_VERSION}/eigen-${DUALMESH_EIGEN_VERSION}.tar.gz
37
+ ${DUALMESH_EIGEN_ARCHIVE}
38
+ EXPECTED_HASH SHA256=8586084f71f9bde545ee7fa6d00288b264a2b7ac3607b974e54d13e7162c1c72
39
+ STATUS DUALMESH_EIGEN_DOWNLOAD)
40
+ list(GET DUALMESH_EIGEN_DOWNLOAD 0 DUALMESH_EIGEN_DOWNLOAD_CODE)
41
+ if(NOT DUALMESH_EIGEN_DOWNLOAD_CODE EQUAL 0)
42
+ list(GET DUALMESH_EIGEN_DOWNLOAD 1 DUALMESH_EIGEN_DOWNLOAD_MESSAGE)
43
+ message(FATAL_ERROR
44
+ "Could not download Eigen (${DUALMESH_EIGEN_DOWNLOAD_MESSAGE}).\n"
45
+ "Install it instead — 'brew install eigen' on macOS, "
46
+ "'apt install libeigen3-dev' on Debian or Ubuntu — or point CMake at an "
47
+ "existing copy with -DEigen3_DIR=<path to the Eigen3 CMake package>.")
48
+ endif()
49
+ file(ARCHIVE_EXTRACT INPUT ${DUALMESH_EIGEN_ARCHIVE}
50
+ DESTINATION ${CMAKE_BINARY_DIR}/_deps)
51
+ endif()
52
+ add_library(Eigen3::Eigen INTERFACE IMPORTED)
53
+ set_target_properties(Eigen3::Eigen PROPERTIES
54
+ INTERFACE_INCLUDE_DIRECTORIES ${DUALMESH_EIGEN_DIR})
55
+ endif()
56
+
57
+ # ---- optional parallel dependencies ------------------------------------------
58
+ # OpenMP threads the element assembly loop; it is optional, and without it the
59
+ # library builds and runs exactly as before, on one thread.
60
+ if(DUALMESH_ENABLE_OPENMP)
61
+ find_package(OpenMP COMPONENTS CXX QUIET)
62
+ if(NOT OpenMP_CXX_FOUND)
63
+ message(STATUS "OpenMP not found; the assembly will run on one thread")
64
+ endif()
65
+ endif()
66
+
67
+ # METIS gives better partitions than the built-in geometric partitioner for
68
+ # unstructured meshes. It is what libMesh, and therefore MOOSE, uses.
69
+ if(DUALMESH_ENABLE_METIS)
70
+ find_path(METIS_INCLUDE_DIR metis.h)
71
+ find_library(METIS_LIBRARY NAMES metis)
72
+ if(METIS_INCLUDE_DIR AND METIS_LIBRARY)
73
+ message(STATUS "METIS found: ${METIS_LIBRARY}")
74
+ else()
75
+ message(STATUS "METIS not found; using the built-in mesh partitioners")
76
+ endif()
77
+ endif()
78
+
79
+ if(DUALMESH_ENABLE_MPI)
80
+ find_package(MPI COMPONENTS CXX REQUIRED)
81
+ endif()
82
+
83
+ # ---- core library ------------------------------------------------------------
84
+ file(GLOB_RECURSE DUALMESH_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.cpp)
85
+ add_library(dualmesh_core STATIC ${DUALMESH_SOURCES})
86
+ target_include_directories(dualmesh_core PUBLIC ${PROJECT_SOURCE_DIR}/include)
87
+ target_link_libraries(dualmesh_core PUBLIC Eigen3::Eigen)
88
+ target_compile_definitions(dualmesh_core
89
+ PUBLIC DUALMESH_MAX_AD_DERIVATIVES=${DUALMESH_MAX_AD_DERIVATIVES})
90
+ if(MSVC)
91
+ target_compile_options(dualmesh_core PRIVATE /W3 /bigobj)
92
+ target_compile_definitions(dualmesh_core PUBLIC _USE_MATH_DEFINES)
93
+ else()
94
+ target_compile_options(dualmesh_core PRIVATE -Wall -Wextra -Wno-unused-parameter)
95
+ endif()
96
+ if(DUALMESH_ENABLE_OPENMP AND OpenMP_CXX_FOUND)
97
+ target_link_libraries(dualmesh_core PUBLIC OpenMP::OpenMP_CXX)
98
+ endif()
99
+ if(DUALMESH_ENABLE_METIS AND METIS_INCLUDE_DIR AND METIS_LIBRARY)
100
+ target_include_directories(dualmesh_core PRIVATE ${METIS_INCLUDE_DIR})
101
+ target_link_libraries(dualmesh_core PRIVATE ${METIS_LIBRARY})
102
+ target_compile_definitions(dualmesh_core PRIVATE DUALMESH_HAVE_METIS)
103
+ endif()
104
+ if(DUALMESH_ENABLE_MPI)
105
+ target_link_libraries(dualmesh_core PUBLIC MPI::MPI_CXX)
106
+ target_compile_definitions(dualmesh_core PUBLIC DUALMESH_HAVE_MPI)
107
+ endif()
108
+
109
+ # ---- command-line driver --------------------------------------------------------
110
+ # (the Python package provides the full input-file driver: `dualmesh run input.yaml`)
111
+
112
+ # ---- tests ---------------------------------------------------------------------
113
+ if(DUALMESH_BUILD_TESTS)
114
+ enable_testing()
115
+ add_executable(dualmesh_unit_tests tests/cpp/unit_tests.cpp)
116
+ target_link_libraries(dualmesh_unit_tests PRIVATE dualmesh_core)
117
+ add_test(NAME unit_tests COMMAND dualmesh_unit_tests)
118
+ add_executable(dualmesh_benchmark tests/cpp/benchmark.cpp)
119
+ target_link_libraries(dualmesh_benchmark PRIVATE dualmesh_core)
120
+ add_executable(dualmesh_parallel_tests tests/cpp/parallel_tests.cpp)
121
+ target_link_libraries(dualmesh_parallel_tests PRIVATE dualmesh_core)
122
+ add_test(NAME parallel_tests COMMAND dualmesh_parallel_tests)
123
+ endif()
124
+
125
+ # ---- Python module ---------------------------------------------------------------
126
+ if(DUALMESH_BUILD_PYTHON)
127
+ find_package(Python 3.8 COMPONENTS Interpreter Development.Module REQUIRED)
128
+ find_package(pybind11 CONFIG QUIET)
129
+ if(NOT pybind11_FOUND)
130
+ execute_process(COMMAND ${Python_EXECUTABLE} -m pybind11 --cmakedir
131
+ OUTPUT_VARIABLE pybind11_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
132
+ find_package(pybind11 CONFIG REQUIRED)
133
+ endif()
134
+ pybind11_add_module(_core MODULE python/bindings/module.cpp)
135
+ target_link_libraries(_core PRIVATE dualmesh_core)
136
+ if(SKBUILD)
137
+ install(TARGETS _core LIBRARY DESTINATION dualmesh)
138
+ else()
139
+ set_target_properties(_core PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/python/dualmesh)
140
+ endif()
141
+ endif()
@@ -0,0 +1,62 @@
1
+ # Contributing to dualmesh
2
+
3
+ Thank you for considering a contribution. This document explains how to build
4
+ the project, what is expected of a change, and how the code is organized.
5
+
6
+ ## Building and testing
7
+
8
+ ```console
9
+ git clone https://github.com/MohamedAbdulHameed/dualmesh.git
10
+ cd dualmesh
11
+ pip install -e .[all,test]
12
+
13
+ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
14
+ cmake --build build -j
15
+ ctest --test-dir build --output-on-failure # C++ unit tests
16
+ pytest # verification suite
17
+ ```
18
+
19
+ `cmake --build` also places the Python extension in `python/dualmesh`, so
20
+ `PYTHONPATH=$PWD/python` is enough to use the package from a source tree.
21
+
22
+ ## What a change should include
23
+
24
+ - **Tests.** New physics needs a verification case with a reference value that
25
+ comes from somewhere trustworthy: a published table (cite it in a comment,
26
+ as the existing tests do), an analytical solution, or a convergence study.
27
+ Bug fixes need a test that fails before the fix.
28
+ - **Documentation.** New objects are documented by their own parameter
29
+ descriptions (`addRequired`/`addOptional` and `setClassDescription`), which
30
+ is what `dualmesh describe` and the object reference in the documentation
31
+ print. Anything conceptually new belongs in `docs/theory.rst` as well.
32
+ - **A note in `CHANGELOG.md`** under "Unreleased".
33
+
34
+ ## Style
35
+
36
+ - C++ follows the MOOSE conventions: two-space indentation, `_member`
37
+ variables, `camelCase` functions, `PascalCase` types, `.clang-format` in the
38
+ repository root (`clang-format -i` before committing).
39
+ - Python follows PEP 8 with descriptive, unabbreviated names: `num_elements`,
40
+ `thermal_conductivity`, `transverse_displacement` — not `ne`, `k`, `w`.
41
+ `ruff check python tests` and `ruff format` are configured in
42
+ `pyproject.toml`.
43
+ - Parameter names in `validParams` are the user interface: spell them out, and
44
+ give each one a sentence that explains what it means physically.
45
+
46
+ ## Adding physics
47
+
48
+ A kernel supplies a flux, a source, or both, for the canonical form
49
+ `-div F + S = 0`; see `docs/developing.rst` for a worked example and for how
50
+ registration works. Prototyping in Python (`dm.PythonKernel`) is encouraged:
51
+ the interface is the same, so a model that works can be moved to C++ almost
52
+ unchanged.
53
+
54
+ ## Reporting problems
55
+
56
+ Please open an issue with the smallest input that reproduces the behaviour, the
57
+ version (`python -c "import dualmesh; print(dualmesh.__version__)"`), and the
58
+ platform. A failing test case is the most useful bug report of all.
59
+
60
+ ## Licence
61
+
62
+ Contributions are accepted under the LGPL-2.1-or-later licence of the project.