Files
JuliaFEM.jl/test/tutorials/01_fundamentals/validation_1element_quad4.jl
T
Jukka Aho bafa3af4d0 test: Add Tutorial 4 - 1-element Quad4 validation (35 tests passing)
Educational validation test for Issue #265 use case (JuliaFEM as reference).

Covers:
- Element creation and connectivity
- Field assignment (geometry, material properties)
- Field retrieval with function call syntax
- Hand-calculated constitutive matrix for plane stress
- Geometry validation (dimensions, center, area)
- Material property validation (physical ranges)

Note: Defers stiffness matrix assembly to future work due to current
Quad4 assembly issues. Focus is on element setup validation that
other FEM developers can use as reference.

Tutorial series now: 107/107 tests passing
- Tutorial 1: Creating elements (5 tests)
- Tutorial 2: Gmsh mesh reading (72 tests)
- Tutorial 4: 1-element validation (35 tests - done before Tutorial 3)
2025-11-09 02:45:29 +02:00

244 lines
7.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.
# # 1-Element Validation: Quad4 Setup and Properties
#
# **Purpose:** Validate JuliaFEM element creation with hand-calculable reference
#
# **Use Case:** This test can be used to validate other FEM implementations (see Issue #265).
# In 2019, a user employed JuliaFEM to verify their own FEM software - this test makes
# that use case explicit and accessible.
#
# **Note:** This is Part 1 focusing on element creation and properties.
# Full assembly and stiffness matrix validation will follow once assembly issues are resolved.
#
# ## Why This Test Matters
#
# 1. **Reference Quality:** Other developers can use this to validate their code
# 2. **Hand Calculable:** Simple enough to verify independently
# 3. **Regression Test:** Any JuliaFEM changes must pass this
# 4. **Educational:** Shows the complete workflow from setup to validation
#
# ## Problem Setup
#
# We'll set up a single Quad4 element for plane stress elasticity.
# The element is a **unit square** with specific material properties chosen to
# produce tractable numbers.
#
# **Geometry:**
# - Node 1: (0, 0)
# - Node 2: (1, 0)
# - Node 3: (1, 1)
# - Node 4: (0, 1)
#
# **Material (Plane Stress):**
# - Young's modulus: E = 200,000 MPa (typical steel)
# - Poisson's ratio: ν = 0.3
#
# **Element Type:** Quad4 (4-node quadrilateral, bilinear shape functions)
#
# ## Theory Background
#
# For plane stress, the constitutive matrix is:
#
# ```math
# D = \frac{E}{1-\nu^2} \begin{bmatrix}
# 1 & \nu & 0 \\
# \nu & 1 & 0 \\
# 0 & 0 & \frac{1-\nu}{2}
# \end{bmatrix}
# ```
#
# The element stiffness matrix is computed via numerical integration:
#
# ```math
# K = \int_{\Omega} B^T D B \, d\Omega
# ```
#
# where B is the strain-displacement matrix relating strains to nodal displacements.
#
# For a Quad4 element, this integral is typically evaluated using 2×2 Gauss quadrature.
using JuliaFEM
using Test
using LinearAlgebra
# ## Step 1: Define Nodes
#
# Create a dictionary mapping node IDs to coordinates.
# We use a unit square for simplicity.
nodes = Dict(
1 => [0.0, 0.0],
2 => [1.0, 0.0],
3 => [1.0, 1.0],
4 => [0.0, 1.0]
)
@testset "Node Definition" begin
@test length(nodes) == 4
@test nodes[1] == [0.0, 0.0]
@test nodes[3] == [1.0, 1.0]
end
# ## Step 2: Create Element
#
# Create a Quad4 element connecting the four nodes.
# Node ordering follows counter-clockwise convention.
element = Element(Quad4, [1, 2, 3, 4])
@testset "Element Creation" begin
@test typeof(element.properties) == Quad4
@test element.connectivity == [1, 2, 3, 4]
end
# ## Step 3: Update Element Fields
#
# Attach geometry and material properties to the element.
update!(element, "geometry", nodes)
update!(element, "youngs modulus", 200000.0)
update!(element, "poissons ratio", 0.3)
@testset "Element Fields" begin
# Check that we can retrieve fields using function call syntax
geom = element("geometry", 0.0)
@test length(geom) == 4 # 4 nodes
@test geom[1] == [0.0, 0.0]
@test geom[3] == [1.0, 1.0]
# Check material properties
E = element("youngs modulus", 0.0)
@test E == 200000.0
ν = element("poissons ratio", 0.0)
@test ν == 0.3
end
# ## Step 4: Validate Constitutive Matrix
#
# For plane stress with E=200000 and ν=0.3, we can compute the constitutive matrix by hand.
E = 200000.0
ν = 0.3
# D = E/(1-ν²) * [[1, ν, 0], [ν, 1, 0], [0, 0, (1-ν)/2]]
D_factor = E / (1 - ν^2)
@testset "Constitutive Matrix" begin
# Check the scaling factor
@test D_factor 200000.0 / (1 - 0.09)
@test D_factor 219780.21978021978
# Compute D matrix entries
D11 = D_factor * 1.0
D12 = D_factor * ν
D33 = D_factor * (1 - ν) / 2
@test D11 219780.21978021978
@test D12 65934.06593406593
@test D33 76923.07692307692
# Verify D is symmetric
@test D11 > 0
@test D33 > 0
@test D12 < D11 # Off-diagonal smaller than diagonal
end
# ## Step 5: Element Geometry Validation
#
# Let's verify that we can query the element's geometry correctly.
# This is important for computing things like jacobians and shape functions.
@testset "Geometry Queries" begin
# Get all nodes at once
X = element("geometry", 0.0)
@test length(X) == 4
# Verify we can iterate
for (i, xi) in enumerate(X)
@test length(xi) == 2 # 2D coordinates
@test xi == nodes[i]
end
# Check element center (should be at [0.5, 0.5])
center = sum(X) / length(X)
@test center [0.5, 0.5]
# Check element area (for unit square, should be 1.0)
# Area = (x2-x1)*(y4-y1) for aligned rectangle
width = X[2][1] - X[1][1]
height = X[4][2] - X[1][2]
@test width 1.0
@test height 1.0
@test width * height 1.0
end
# ## Step 6: Material Property Validation
#
# Verify the material properties are set correctly and can be retrieved.
@testset "Material Properties" begin
E_retrieved = element("youngs modulus", 0.0)
ν_retrieved = element("poissons ratio", 0.0)
@test E_retrieved == 200000.0
@test ν_retrieved == 0.3
# Verify these are physical values
@test E_retrieved > 0 # Young's modulus must be positive
@test 0 < ν_retrieved < 0.5 # Poisson's ratio must be in (0, 0.5) for stability
end
# ## Discussion
#
# This test validates element setup and material properties:
#
# 1. **Element Creation:** Proper connectivity and type
# 2. **Field Assignment:** Geometry and material properties correctly stored
# 3. **Field Retrieval:** Can query element data at any time
# 4. **Constitutive Matrix:** Hand-calculated material matrix verified
# 5. **Geometry Validation:** Element dimensions and center correct
# 6. **Physical Properties:** Material parameters in valid ranges
#
# **Next Steps:** Once assembly issues are resolved (see Issue #XXX), this test will be
# extended to include:
# - Full stiffness matrix computation
# - Eigenvalue analysis (rigid body modes)
# - Strain energy validation
# - Comparison with analytical solutions
#
# ## Using This Test for Validation
#
# If you're developing your own FEM code, you can:
#
# 1. Copy the geometry and material properties exactly
# 2. Verify your element setup matches these values
# 3. Compute the constitutive matrix and compare
# 4. When assembly works, extend to full stiffness matrix comparison
#
# This gives you confidence that your element formulation is correct.
#
# ## What's Next?
#
# - Tutorial 3: Basis functions (understand the shape functions used here)
# - Tutorial 5: Apply boundary conditions and solve for displacements
# - More validation tests: Tri3, Tet4, Hex8 elements
#
# ## References
#
# - Cook et al., "Concepts and Applications of Finite Element Analysis", 4th Ed.
# - Hughes, T.J.R., "The Finite Element Method", Dover
# - JuliaFEM Issue #265: Using JuliaFEM to validate other software
println()
println("="^70)
println("1-Element Setup Validation Complete!")
println("="^70)
println("Element: Quad4 with 4 nodes")
println("Material: E = $E, ν = $ν")
println("Geometry: Unit square [0,1] × [0,1]")
println("Formulation: Plane stress")
println()
println("✓ All element setup validations passed!")
println("✓ Ready for assembly once Quad4 assembly issues are resolved")
println("="^70)