diff --git a/cpp/modmesh/view/RManager.cpp b/cpp/modmesh/view/RManager.cpp index 42f50e41..a309e7fb 100644 --- a/cpp/modmesh/view/RManager.cpp +++ b/cpp/modmesh/view/RManager.cpp @@ -259,6 +259,10 @@ void RManager::setUpMenu() QString("Sample: mesh of \"solvcon\" text in 2D"), QString("Create a sample mesh drawing a text string of \"solvcon\""), QString("modmesh.gui.sample_mesh.mesh_solvcon_2dtext"))); + m_meshMenu->addAction(new RPythonAction( + QString("Sample: 2D mesh in a rectangle"), + QString("Triangular mesh in a rectangle"), + QString("modmesh.gui.sample_mesh.mesh_rectangle"))); m_meshMenu->addAction(new RPythonAction( QString("Sample: 3D mesh of mixed elements"), QString("Create a very simple sample mesh of mixed elements in 3D"), diff --git a/modmesh/gui/sample_mesh.py b/modmesh/gui/sample_mesh.py index c395bac7..a4db86c1 100644 --- a/modmesh/gui/sample_mesh.py +++ b/modmesh/gui/sample_mesh.py @@ -29,6 +29,9 @@ Show example meshes. """ +import os + +import modmesh as mm from .. import core from .. import view @@ -201,4 +204,25 @@ def mesh_3dmix(): w_3dmix.showMark() view.mgr.pycon.writeToHistory(f"3dmix nedge: {mh.nedge}\n") + +def mesh_rectangle(): + fn = os.path.join(os.path.dirname(mm.__file__), '..') + fn = os.path.abspath(fn) + fn = os.path.join(fn, "tests", "data", "rectangle.msh") + if not os.path.exists(fn): + view.mgr.pycon.writeToHistory(f"{fn} does not exist\n") + return + + with open(fn, 'rb') as fobj: + data = fobj.read() + view.mgr.pycon.writeToHistory(f"gmsh mesh file {fn} is read\n") + gmsh = mm.Gmsh(data) + mh = gmsh.to_block() + view.mgr.pycon.writeToHistory("StaticMesh object created from gmsh\n") + # Open a sub window for triangles and quadrilaterals: + w = view.mgr.add3DWidget() + w.updateMesh(mh) + w.showMark() + view.mgr.pycon.writeToHistory(f"nedge: {mh.nedge}\n") + # vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: diff --git a/tests/data/rectangle.geo b/tests/data/rectangle.geo new file mode 100644 index 00000000..dc784b65 --- /dev/null +++ b/tests/data/rectangle.geo @@ -0,0 +1,27 @@ +/* + * A Gmsh template file for a rectangle domain. + */ +lc = 0.25; +// vertices. +Point(1) = {0,0,0,lc}; +Point(2) = {4,0,0,lc}; +Point(3) = {4,1,0,lc}; +Point(4) = {0,1,0,lc}; +// lines. +Line(1) = {1,2}; +Line(2) = {2,3}; +Line(3) = {3,4}; +Line(4) = {4,1}; +// surface. +Line Loop(1) = {1,2,3,4}; +Plane Surface(1) = {1}; +// physics. +Physical Line("lower") = {1}; +Physical Line("right") = {2}; +Physical Line("upper") = {3}; +Physical Line("left") = {4}; +Physical Surface("domain") = {1}; +// mesh +Mesh.MshFileVersion = 2.2; +Mesh.ALgorithm = 6; // Frontal-Delaunay for 2D mesh. +Mesh 2; \ No newline at end of file diff --git a/tests/data/rectangle.msh b/tests/data/rectangle.msh new file mode 100644 index 00000000..95262e21 --- /dev/null +++ b/tests/data/rectangle.msh @@ -0,0 +1,327 @@ +$MeshFormat +2.2 0 8 +$EndMeshFormat +$PhysicalNames +5 +1 1 "lower" +1 2 "right" +1 3 "upper" +1 4 "left" +2 5 "domain" +$EndPhysicalNames +$Nodes +104 +1 0 0 0 +2 4 0 0 +3 4 1 0 +4 0 1 0 +5 0.2499999999994931 0 0 +6 0.4999999999990952 0 0 +7 0.7499999999983772 0 0 +8 0.9999999999976482 0 0 +9 1.249999999996919 0 0 +10 1.49999999999619 0 0 +11 1.749999999995461 0 0 +12 1.999999999994776 0 0 +13 2.249999999995391 0 0 +14 2.49999999999605 0 0 +15 2.749999999996708 0 0 +16 2.999999999997367 0 0 +17 3.249999999998025 0 0 +18 3.499999999998683 0 0 +19 3.749999999999341 0 0 +20 4 0.2499999999994121 0 +21 4 0.499999999998694 0 +22 4 0.7499999999993416 0 +23 3.749999999998959 1 0 +24 3.499999999998004 1 0 +25 3.249999999999652 1 0 +26 3.000000000001386 1 0 +27 2.75000000000312 1 0 +28 2.500000000004855 1 0 +29 2.250000000006589 1 0 +30 2.000000000008237 1 0 +31 1.750000000007283 1 0 +32 1.500000000006242 1 0 +33 1.250000000005202 1 0 +34 1.000000000004162 1 0 +35 0.7500000000031211 1 0 +36 0.500000000002081 1 0 +37 0.2500000000010405 1 0 +38 0 0.7500000000003465 0 +39 0 0.5000000000020591 0 +40 0 0.2500000000010404 0 +41 3.374999999998829 0.7834936490553547 0 +42 2.875000000002253 0.7834936490553923 0 +43 2.375000000005722 0.7834936490553923 0 +44 0.6249999999987362 0.2165063509454831 0 +45 1.87500000000751 0.7834936490538524 0 +46 1.124999999997284 0.2165063509454783 0 +47 1.624999999995826 0.2165063509454783 0 +48 3.124999999998038 0.2165063509466417 0 +49 2.624999999997214 0.2165063509473532 0 +50 0.8750000000030514 0.7834936490527512 0 +51 1.375000000006442 0.7834936490524679 0 +52 2.099006801572487 0.2209535815613942 0 +53 0.3760593423216918 0.7883069887354637 0 +54 3.623940657678794 0.2116930112643635 0 +55 3.802323440804014 0.6408098265826621 0 +56 0.1976765591957199 0.3591901734176817 0 +57 1.625000000007084 0.7834936490532454 0 +58 1.75000000000809 0.5775800773368169 0 +59 1.994576912405159 0.5710274781530857 0 +60 1.875000000008835 0.350480947161931 0 +61 1.50000000000774 0.5793455405407392 0 +62 1.250000000005847 0.5796397844076417 0 +63 1.375000000008514 0.3504809471593137 0 +64 1.125000000004507 0.7856023967696628 0 +65 1.000000000003361 0.5800402830058129 0 +66 0.7500000000020213 0.5797555748195334 0 +67 0.8750000000001612 0.3504809471598768 0 +68 3.374823442944999 0.2157041276661154 0 +69 3.249970573822698 0.4222862187833804 0 +70 2.999995095635665 0.4206321754788162 0 +71 3.124999999998464 0.6495190528387536 0 +72 2.874999182603891 0.2144429298777462 0 +73 2.749999046372003 0.4200125980834724 0 +74 2.499999841060236 0.4202532386965472 0 +75 2.624999999998587 0.6495190528396859 0 +76 2.374999999996376 0.2165063509489645 0 +77 2.266501107105585 0.4072792581122316 0 +78 3.501324591263483 0.4038242844391853 0 +79 2.112754883569934 0.7907148968186483 0 +80 0.6251765570556744 0.7864239184525472 0 +81 0.4986705043741907 0.5968706888478856 0 +82 3.639658653685201 0.806888536464238 0 +83 0.360341346313638 0.1931114635360595 0 +84 0.2236979714458561 0.6070649188441418 0 +85 3.776301671093385 0.3929857351857724 0 +86 3.370705192611999 0.5758697721546162 0 +87 3.125000000000517 0.8470503244304236 0 +88 2.874998828401395 0.5846353056592241 0 +89 1.874999999995119 0.1529496755697758 0 +90 2.625000000003988 0.8470503244309491 0 +91 0.6292899030249653 0.4245211945440384 0 +92 1.625000000005801 0.4148787726288559 0 +93 1.125000000003033 0.4154296625356247 0 +94 2.385493333879267 0.5769635083258657 0 +95 0.8749999999980125 0.1529496755686255 0 +96 1.374999999996555 0.152949675568308 0 +97 0.1830127018932165 0.816987298107155 0 +98 0.1830127018927053 0.1830127018931199 0 +99 3.816987298107088 0.1830127018925805 0 +100 3.816987298107323 0.8169872981074244 0 +101 0.4122215101620063 0.3998081616397891 0 +102 3.589453104028406 0.6001978939172735 0 +103 2.073645217067261 0.4073834779239984 0 +104 2.191838198519946 0.5977044210179152 0 +$EndNodes +$Elements +206 +1 1 2 1 1 1 5 +2 1 2 1 1 5 6 +3 1 2 1 1 6 7 +4 1 2 1 1 7 8 +5 1 2 1 1 8 9 +6 1 2 1 1 9 10 +7 1 2 1 1 10 11 +8 1 2 1 1 11 12 +9 1 2 1 1 12 13 +10 1 2 1 1 13 14 +11 1 2 1 1 14 15 +12 1 2 1 1 15 16 +13 1 2 1 1 16 17 +14 1 2 1 1 17 18 +15 1 2 1 1 18 19 +16 1 2 1 1 19 2 +17 1 2 2 2 2 20 +18 1 2 2 2 20 21 +19 1 2 2 2 21 22 +20 1 2 2 2 22 3 +21 1 2 3 3 3 23 +22 1 2 3 3 23 24 +23 1 2 3 3 24 25 +24 1 2 3 3 25 26 +25 1 2 3 3 26 27 +26 1 2 3 3 27 28 +27 1 2 3 3 28 29 +28 1 2 3 3 29 30 +29 1 2 3 3 30 31 +30 1 2 3 3 31 32 +31 1 2 3 3 32 33 +32 1 2 3 3 33 34 +33 1 2 3 3 34 35 +34 1 2 3 3 35 36 +35 1 2 3 3 36 37 +36 1 2 3 3 37 4 +37 1 2 4 4 4 38 +38 1 2 4 4 38 39 +39 1 2 4 4 39 40 +40 1 2 4 4 40 1 +41 2 2 5 1 81 84 101 +42 2 2 5 1 78 85 102 +43 2 2 5 1 78 54 85 +44 2 2 5 1 81 53 84 +45 2 2 5 1 47 60 92 +46 2 2 5 1 60 47 89 +47 2 2 5 1 46 63 93 +48 2 2 5 1 63 46 96 +49 2 2 5 1 42 75 88 +50 2 2 5 1 75 42 90 +51 2 2 5 1 41 71 86 +52 2 2 5 1 71 41 87 +53 2 2 5 1 67 44 95 +54 2 2 5 1 44 67 91 +55 2 2 5 1 67 46 93 +56 2 2 5 1 71 42 88 +57 2 2 5 1 42 71 87 +58 2 2 5 1 46 67 95 +59 2 2 5 1 75 43 94 +60 2 2 5 1 43 75 90 +61 2 2 5 1 63 47 92 +62 2 2 5 1 47 63 96 +63 2 2 5 1 52 60 89 +64 2 2 5 1 84 56 101 +65 2 2 5 1 85 55 102 +66 2 2 5 1 52 76 77 +67 2 2 5 1 62 64 65 +68 2 2 5 1 33 34 64 +69 2 2 5 1 13 14 76 +70 2 2 5 1 34 50 64 +71 2 2 5 1 52 13 76 +72 2 2 5 1 49 72 73 +73 2 2 5 1 49 73 74 +74 2 2 5 1 49 15 72 +75 2 2 5 1 39 40 56 +76 2 2 5 1 61 51 62 +77 2 2 5 1 51 33 64 +78 2 2 5 1 45 57 58 +79 2 2 5 1 14 49 76 +80 2 2 5 1 57 51 61 +81 2 2 5 1 64 50 65 +82 2 2 5 1 30 31 45 +83 2 2 5 1 21 22 55 +84 2 2 5 1 50 35 80 +85 2 2 5 1 15 16 72 +86 2 2 5 1 48 68 69 +87 2 2 5 1 74 73 75 +88 2 2 5 1 48 17 68 +89 2 2 5 1 61 62 63 +90 2 2 5 1 45 31 57 +91 2 2 5 1 66 50 80 +92 2 2 5 1 70 69 71 +93 2 2 5 1 29 30 79 +94 2 2 5 1 68 54 78 +95 2 2 5 1 32 51 57 +96 2 2 5 1 48 69 70 +97 2 2 5 1 36 37 53 +98 2 2 5 1 69 68 78 +99 2 2 5 1 65 66 67 +100 2 2 5 1 65 50 66 +101 2 2 5 1 31 32 57 +102 2 2 5 1 18 54 68 +103 2 2 5 1 12 13 52 +104 2 2 5 1 45 58 59 +105 2 2 5 1 18 19 54 +106 2 2 5 1 43 29 79 +107 2 2 5 1 14 15 49 +108 2 2 5 1 16 17 48 +109 2 2 5 1 16 48 72 +110 2 2 5 1 32 33 51 +111 2 2 5 1 30 45 79 +112 2 2 5 1 58 57 61 +113 2 2 5 1 34 35 50 +114 2 2 5 1 49 74 76 +115 2 2 5 1 17 18 68 +116 2 2 5 1 72 70 73 +117 2 2 5 1 62 51 64 +118 2 2 5 1 76 74 77 +119 2 2 5 1 59 58 60 +120 2 2 5 1 48 70 72 +121 2 2 5 1 10 11 47 +122 2 2 5 1 6 7 44 +123 2 2 5 1 8 9 46 +124 2 2 5 1 24 25 41 +125 2 2 5 1 35 36 80 +126 2 2 5 1 26 27 42 +127 2 2 5 1 28 29 43 +128 2 2 5 1 36 53 80 +129 2 2 5 1 45 59 79 +130 2 2 5 1 80 53 81 +131 2 2 5 1 66 80 81 +132 2 2 5 1 82 41 102 +133 2 2 5 1 83 44 101 +134 2 2 5 1 41 86 102 +135 2 2 5 1 44 91 101 +136 2 2 5 1 60 52 103 +137 2 2 5 1 23 24 82 +138 2 2 5 1 38 39 84 +139 2 2 5 1 5 6 83 +140 2 2 5 1 20 21 85 +141 2 2 5 1 24 41 82 +142 2 2 5 1 39 56 84 +143 2 2 5 1 6 44 83 +144 2 2 5 1 21 55 85 +145 2 2 5 1 65 67 93 +146 2 2 5 1 63 62 93 +147 2 2 5 1 75 73 88 +148 2 2 5 1 61 63 92 +149 2 2 5 1 71 69 86 +150 2 2 5 1 70 71 88 +151 2 2 5 1 12 52 89 +152 2 2 5 1 74 75 94 +153 2 2 5 1 60 58 92 +154 2 2 5 1 47 11 89 +155 2 2 5 1 10 47 96 +156 2 2 5 1 41 25 87 +157 2 2 5 1 26 42 87 +158 2 2 5 1 44 7 95 +159 2 2 5 1 8 46 95 +160 2 2 5 1 46 9 96 +161 2 2 5 1 28 43 90 +162 2 2 5 1 42 27 90 +163 2 2 5 1 67 66 91 +164 2 2 5 1 4 38 97 +165 2 2 5 1 37 4 97 +166 2 2 5 1 40 1 98 +167 2 2 5 1 1 5 98 +168 2 2 5 1 2 20 99 +169 2 2 5 1 19 2 99 +170 2 2 5 1 3 23 100 +171 2 2 5 1 22 3 100 +172 2 2 5 1 43 79 104 +173 2 2 5 1 94 43 104 +174 2 2 5 1 38 84 97 +175 2 2 5 1 20 85 99 +176 2 2 5 1 23 82 100 +177 2 2 5 1 5 83 98 +178 2 2 5 1 62 65 93 +179 2 2 5 1 69 78 86 +180 2 2 5 1 73 70 88 +181 2 2 5 1 25 26 87 +182 2 2 5 1 11 12 89 +183 2 2 5 1 58 61 92 +184 2 2 5 1 77 74 94 +185 2 2 5 1 27 28 90 +186 2 2 5 1 66 81 91 +187 2 2 5 1 7 8 95 +188 2 2 5 1 9 10 96 +189 2 2 5 1 53 37 97 +190 2 2 5 1 54 19 99 +191 2 2 5 1 56 40 98 +192 2 2 5 1 55 22 100 +193 2 2 5 1 59 60 103 +194 2 2 5 1 52 77 103 +195 2 2 5 1 79 59 104 +196 2 2 5 1 82 55 100 +197 2 2 5 1 83 56 98 +198 2 2 5 1 85 54 99 +199 2 2 5 1 84 53 97 +200 2 2 5 1 103 77 104 +201 2 2 5 1 59 103 104 +202 2 2 5 1 55 82 102 +203 2 2 5 1 56 83 101 +204 2 2 5 1 86 78 102 +205 2 2 5 1 91 81 101 +206 2 2 5 1 77 94 104 +$EndElements diff --git a/tests/test_gmsh.py b/tests/test_gmsh.py index 2e95c34a..1f69b7a7 100644 --- a/tests/test_gmsh.py +++ b/tests/test_gmsh.py @@ -1,35 +1,99 @@ +# Copyright (c) 2024, Yung-Yu Chen +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# - Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# - Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# - Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + import os import unittest import numpy as np -import modmesh +import modmesh as mm + +class GmshTB(unittest.TestCase): + TESTDIR = os.path.abspath(os.path.dirname(__file__)) + DATADIR = os.path.join(TESTDIR, "data") -class GmshTC(unittest.TestCase): +class GmshTriangleTC(GmshTB): def test_gmsh_parsing(self): - path = os.path.join(os.path.abspath(os.path.dirname(__file__)), - "data", "gmsh_triangle.msh") + path = os.path.join(self.DATADIR, "gmsh_triangle.msh") data = open(path, 'rb').read() - gmsh_instance = modmesh.core.Gmsh(data) - blk = gmsh_instance.to_block() + gmsh = mm.Gmsh(data) + blk = gmsh.to_block() # Check nodes information self.assertEqual(blk.nnode, 4) # Due to ghost cell and ghost node had been created, the real body - # had been shifted and start with index 3 - np.testing.assert_almost_equal(blk.ndcrd.ndarray[3:, :].tolist(), + # had been shifted and start with index 3 (number of ghost) + ngst = blk.ngstcell + self.assertEqual(ngst, 3) + np.testing.assert_almost_equal(blk.ndcrd.ndarray[ngst:, :].tolist(), [[0.0, 0.0], [-1.0, -1.0], [1.0, -1.0], [0.0, 1.0]]) # Check cells information self.assertEqual(blk.ncell, 3) - self.assertEqual(blk.cltpn.ndarray[3:].tolist(), [4, 4, 4]) - self.assertEqual(blk.clnds.ndarray[3:, :4].tolist(), [[3, 0, 1, 2], - [3, 0, 2, 3], - [3, 0, 3, 1]]) + self.assertEqual(blk.cltpn.ndarray[ngst:].tolist(), [4, 4, 4]) + self.assertEqual(blk.clnds.ndarray[ngst:, :4].tolist(), + [[3, 0, 1, 2], + [3, 0, 2, 3], + [3, 0, 3, 1]]) + + +class GmshRectangularTC(GmshTB): + def setUp(self): + # Read the Gmsh mesh file. + path = os.path.join(self.DATADIR, "rectangle.msh") + with open(path, 'rb') as fobj: + data = fobj.read() + # Create the Gmsh object. + gmsh = mm.Gmsh(data) + # Convert the Gmsh object to StaticMesh object. + self.blk = gmsh.to_block() + + def test_shape(self): + blk = self.blk + # Test for shape data. + self.assertEqual(blk.ndim, 2) + self.assertEqual(blk.nbound, 40) + self.assertEqual(blk.nnode, 104) + self.assertEqual(blk.nface, 309) + self.assertEqual(blk.ncell, 206) + self.assertEqual(blk.ngstnode, 40) + self.assertEqual(blk.ngstface, 80) + self.assertEqual(blk.ngstcell, 40) + + @unittest.expectedFailure + def test_type(self): + blk = self.blk + # TODO: all cell should be triangles including ghost cells. + np.testing.assert_equal(blk.cltpn, 4) + +# vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: