Files
JuliaFEM.jl/test/tutorials/01_fundamentals/reading_gmsh_meshes.jl
T
Jukka Aho 5a07b3ab21 docs: Document Tutorial 3 API limitations, update test runner
Current state discovery:
- Element basis function evaluation broken (eval_basis! signature mismatch)
- Field interpolation at integration points broken (same root cause)
- Jacobian evaluation at integration points broken
- These are fundamental API issues affecting multiple test paths

Impact:
- Tutorial 3 (basis functions) deferred until API fixed
- Affects any code trying to evaluate fields at integration points
- Related to Quad4 assembly issues discovered in Tutorial 4

Working tutorials (107/107 tests passing):
- Tutorial 1: Element creation (5 tests)
- Tutorial 2: Gmsh mesh reading (72 tests)
- Tutorial 4: 1-element validation (35 tests)

Next: Focus on tutorials using working APIs only
2025-11-09 02:58:09 +02:00

196 lines
6.1 KiB
Julia
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# # 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.