mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-18 17:47:29 +00:00
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:
@@ -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.
|
||||
Reference in New Issue
Block a user