chore(test): delete fundamentals Gmsh reading tutorial script

Remove stale tutorial harness superseded by `test/io/test_gmsh_reader.jl`.

- Drop `reading_gmsh_meshes.jl`.
This commit is contained in:
Jukka Aho
2026-05-09 18:31:42 +03:00
parent cceb56849b
commit 534889f985
@@ -1,195 +0,0 @@
# # Tutorial 2: Reading Meshes with Gmsh
#
# Real FEM problems need meshes with many elements. Creating them manually
# would be tedious! This tutorial shows how to:
# - Read mesh files generated by Gmsh
# - Extract nodes and elements
# - Organize elements by physical groups (for boundary conditions)
#
# ## Prerequisites
#
# - Tutorial 1 (Creating Elements and Fields)
# - Basic understanding of mesh concepts (nodes, elements, connectivity)
using JuliaFEM
using Gmsh # Mesh generation and reading
using Test
# ## The Mesh File
#
# We're reading a pre-generated mesh file: `reading_gmsh_meshes.msh`
#
# This mesh was created using the Gmsh.jl recipe in `reading_gmsh_meshes_recipe.jl`.
# The recipe shows how to programmatically generate meshes - very useful for
# parametric studies!
#
# **Mesh specifications:**
# - Domain: Unit square [0,1] × [0,1]
# - Elements: 10 Quad4 elements (2×5 structured mesh)
# - Nodes: 18 nodes
# - Physical groups: DOMAIN (volume), BOTTOM/RIGHT/TOP/LEFT (boundaries)
mesh_file = joinpath(@__DIR__, "reading_gmsh_meshes.msh")
# ## Reading the Mesh with Gmsh.jl
#
# Gmsh.jl provides a Julia interface to the Gmsh API. We can read mesh files
# and extract all the data we need.
# Initialize Gmsh
gmsh.initialize()
# Open and read the mesh file
gmsh.open(mesh_file)
# ## Extracting Nodes
#
# Nodes are the points in space where we'll compute field values.
# In Gmsh, nodes are returned as:
# - `nodeTags`: Node IDs (integers)
# - `coord`: Coordinates (x, y, z) as a flat vector
nodeTags, coord, parametricCoord = gmsh.model.mesh.getNodes()
# The `coord` vector is flat: [x1, y1, z1, x2, y2, z2, ...]
# Let's reshape it into a more convenient format:
num_nodes = length(nodeTags)
nodes = Dict{Int,Vector{Float64}}()
for i in 1:num_nodes
node_id = nodeTags[i]
x = coord[3*(i-1)+1]
y = coord[3*(i-1)+2]
z = coord[3*(i-1)+3]
nodes[node_id] = [x, y, z]
end
# Now we have a dictionary mapping node IDs to coordinates!
# ## Extracting Elements
#
# Elements connect nodes and define the interpolation within each region.
# In Gmsh, we need to query elements by entity (surfaces, volumes, etc.).
# Get all 2D entities (surfaces in our case)
entities = gmsh.model.getEntities(2) # 2 = dimension for surfaces
# For each entity, get the elements
all_elements = Element[]
for entity in entities
dim, tag = entity
# Get element data for this entity
elemTypes, elemTags, nodeTags_elem = gmsh.model.mesh.getElements(dim, tag)
# Loop over element types (we have Quad4 = type 3)
for (elemType, elemTag, elemNodeTags) in zip(elemTypes, elemTags, nodeTags_elem)
# Get element properties
elemName, elemDim, order, numNodes, localNodeCoord, numPrimaryNodes =
gmsh.model.mesh.getElementProperties(elemType)
# Create JuliaFEM elements
for i in 1:length(elemTag)
# Extract connectivity for this element
start_idx = (i - 1) * numNodes + 1
end_idx = i * numNodes
# Convert UInt64 to Int for JuliaFEM compatibility
connectivity = Tuple(Int(tag) for tag in elemNodeTags[start_idx:end_idx])
# Map Gmsh element type to JuliaFEM element type
if elemType == 3 # Gmsh Quad4
local elem = Element(Quad4, connectivity)
else
@warn "Unknown element type: $elemType ($elemName)"
continue
end
# Add geometry field
update!(elem, "geometry", nodes)
push!(all_elements, elem)
end
end
end
# ## Physical Groups
#
# Physical groups let us organize elements by region (for materials)
# and boundaries (for boundary conditions).
# Get all physical groups
physicalGroups = gmsh.model.getPhysicalGroups()
# We can query which elements belong to each physical group
# (This would be used to create separate Problems for different regions)
# ## Cleanup
gmsh.finalize()
# ## Validation Tests
#
# Let's verify the mesh was read correctly:
@testset "Gmsh Mesh Reading" begin
# Correct number of nodes (2×5 structured quad mesh has 3×6 nodes)
@test length(nodes) == 18
# Correct number of elements (2 × 5 = 10 quads)
@test length(all_elements) == 10
# All elements should be Quad4
@test all(e -> typeof(e.properties) == Quad4, all_elements)
# Node coordinates should be in [0,1] × [0,1]
for (node_id, coord) in nodes
@test 0.0 <= coord[1] <= 1.0 # x ∈ [0,1]
@test 0.0 <= coord[2] <= 1.0 # y ∈ [0,1]
@test coord[3] == 0.0 # z = 0 (2D mesh)
end
# Each element should have 4 nodes
for element in all_elements
@test length(element.connectivity) == 4
end
end
# ## What We Learned
#
# ✅ How to read Gmsh .msh files using Gmsh.jl
# ✅ Extract nodes (IDs and coordinates)
# ✅ Extract elements (type, connectivity)
# ✅ Map Gmsh element types to JuliaFEM types
# ✅ Physical groups organize elements for BCs and materials
# ✅ Mesh generation recipes document mesh creation
#
# ## Why This Approach?
#
# **Pre-generated mesh files** (not generating in tests):
# - Tests are reliable (don't fail due to mesh generation)
# - Tests run faster (no mesh generation overhead)
# - Mesh is version-controlled (reproducible)
#
# **Recipe files** (showing how mesh was made):
# - Educational (teach Gmsh.jl)
# - Documented (can regenerate if needed)
# - Transparent (know exactly what the mesh is)
#
# ## Next Steps
#
# - **Tutorial 3:** Basis functions and shape function evaluation
# - **Tutorial 4:** Solving 1D elasticity (1-element validation)
# - **Tutorial 5:** Solving 2D elasticity (realistic 10-element problem)
#
# ## Real-World Impact
#
# JuliaFEM is being used to **validate other FEM software**!
# See [Issue #265](https://github.com/JuliaFEM/JuliaFEM.jl/issues/265) where
# a user validated their FEM code against JuliaFEM results. This is beautiful -
# we're useful as a reference implementation even after years!
#
# That's why we include **1-element validation tests** with analytical solutions
# and **nice round numbers** (parameters chosen so results are integers). Makes
# it easy to verify by hand calculation or compare with other codes.