Files
JuliaFEM.jl/test/tutorials/01_fundamentals/creating_elements.jl
T
Jukka Aho 61218f8fd3 test: Phase 1 - Educational test infrastructure with Literate.jl
Implement testing philosophy (see docs/TESTING_PHILOSOPHY.md):

New test structure:
- test/tutorials/ - Educational tests (generate documentation)
- test/unit/ - Fast focused tests
- test/verification/ - Known analytical solutions
- test/runtests_new.jl - New test runner with env var control

First tutorial: Creating Elements and Fields
- Teaches node/element creation
- Explains field concept (geometry, materials, loads)
- 5 tests, all passing 

Test runner features:
- JULIAFEM_TEST_TUTORIALS=true/false (default: true)
- JULIAFEM_TEST_UNIT=true/false (default: false)
- JULIAFEM_TEST_OLD=true/false (default: false)
- Clear output with test categories
- Preserved old test suite as runtests.jl.old

Results: 5/5 tests passing in <2 seconds

Next: Write 2-3 more fundamental tutorials (mesh reading, 1D elasticity)
2025-11-09 01:51:17 +02:00

160 lines
4.5 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 1: Creating Elements and Fields
#
# This tutorial introduces the fundamental concepts of JuliaFEM:
# - Creating nodes and elements
# - Updating element fields
# - Understanding the field concept
#
# ## Prerequisites
#
# Basic Julia knowledge. This is the first tutorial - no prior JuliaFEM experience needed!
using JuliaFEM
using Test
# ## Nodes and Coordinates
#
# In finite element analysis, everything starts with **nodes** - points in space
# where we'll compute displacements, temperatures, or other field values.
#
# In JuliaFEM, nodes are simply vectors of coordinates. Let's create a few nodes
# for a 2D square:
node1 = [0.0, 0.0] # Bottom-left corner
node2 = [1.0, 0.0] # Bottom-right corner
node3 = [1.0, 1.0] # Top-right corner
node4 = [0.0, 1.0] # Top-left corner
# We typically store nodes in a dictionary for easy access by node ID:
X = Dict(
1 => node1,
2 => node2,
3 => node3,
4 => node4
)
# ## Elements
#
# An **element** connects nodes and defines:
# - The shape (linear, quadratic, etc.)
# - The interpolation (how fields vary within the element)
# - The physics (what equations to solve)
#
# Let's create a 4-node quadrilateral element (`Quad4`):
element = Element(Quad4, (1, 2, 3, 4))
# The tuple `(1, 2, 3, 4)` specifies which nodes this element connects.
# Node numbering follows a counter-clockwise convention:
#
# ```
# 4 ---- 3
# | |
# | |
# 1 ---- 2
# ```
# ## The Field Concept
#
# In JuliaFEM, everything is a **field**. A field is data associated with an element
# that can vary in space and time:
#
# - **Geometry field:** Node coordinates
# - **Material fields:** Young's modulus, Poisson's ratio
# - **Load fields:** Body forces, surface tractions
# - **Solution fields:** Displacements, temperatures
#
# Fields are updated using the `update!` function.
# ### Geometry Field
#
# First, we tell the element where its nodes are located:
update!(element, "geometry", X)
# Now the element knows its shape and can compute things like its area,
# jacobian, etc.
# ### Material Fields
#
# Let's add material properties (for an elasticity problem):
update!(element, "youngs modulus", 200.0e3) # 200 GPa steel
update!(element, "poissons ratio", 0.3)
# ### Load Fields
#
# We can apply loads as fields too. For example, a body force in the y-direction:
update!(element, "displacement load 2", 10.0) # 10 N/m³ in y-direction
# The naming convention:
# - `"displacement load 1"` = body force in x-direction
# - `"displacement load 2"` = body force in y-direction
# - `"displacement load 3"` = body force in z-direction (3D)
# ## Field Access
#
# We can retrieve fields using function call syntax:
geom = element("geometry", 0.0) # Get geometry at time t=0.0
E = element("youngs modulus", 0.0)
ν = element("poissons ratio", 0.0)
# Fields can be time-dependent! The second argument is the time value.
# For static fields (like geometry), the time doesn't matter.
# ## Testing Our Understanding
#
# Let's verify everything works as expected:
@testset "Element Creation" begin
# Element should have 4 nodes
@test length(element.connectivity) == 4
# Geometry field returns a tuple of node coordinates
@test length(geom) == 4 # 4 nodes
@test geom[1] == [0.0, 0.0] # First node
# Material properties should be retrievable
@test E == 200.0e3
@test ν == 0.3
end
# ## Multiple Elements
#
# Real problems have many elements. Let's create a vector of elements:
elements = [
Element(Quad4, (1, 2, 3, 4)),
Element(Quad4, (2, 5, 6, 3)) # Adjacent element (assuming nodes 5, 6 exist)
]
# We can update fields for all elements at once:
update!(elements, "youngs modulus", 200.0e3)
update!(elements, "poissons ratio", 0.3)
# This is more efficient than updating each element individually.
# ## What We Learned
#
# ✅ Nodes are just coordinate vectors
# ✅ Elements connect nodes using connectivity tuples
# ✅ Everything is a field (geometry, materials, loads, solutions)
# ✅ Fields are updated with `update!(element, "field_name", value)`
# ✅ Fields are accessed with `element("field_name", time)`
# ✅ Multiple elements can be updated together
#
# ## Next Steps
#
# - **Tutorial 2:** Reading meshes from files (ABAQUS .inp format)
# - **Tutorial 3:** Basis functions and shape function evaluation
# - **Tutorial 4:** Solving your first problem (1D elasticity)
#
# ## Further Reading
#
# - JuliaFEM paper: [Frondelius & Aho (2017)](https://doi.org/10.23998/rm.64224)
# - Field concept: Based on Abaqus field definitions