mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-08-06 04:21:33 +00:00
docs(book): Add comprehensive Gmsh to physics tutorial
New file: docs/book/gmsh_tutorial.md (544 lines) Complete educational resource addressing Issue #183: Step 1: Mesh Generation with Gmsh - Why Gmsh (features, academic adoption) - .geo file syntax and concepts - Mesh generation commands - Understanding .msh format Step 2: Weak Formulation (Theory) - Strong form → weak form derivation - Galerkin approximation - M du/dt + K u = f system Step 3: FEM Assembly in JuliaFEM - Loading meshes - Creating problems and elements - Boundary conditions (Dirichlet, Neumann) - Assembly process internals Step 4: Extracting Matrices (Issue #183 core answer) - How to get K, M, f after assembly - Why extract (5 use cases) - Integration with DifferentialEquations.jl - Complete working example Step 5: Method of Lines - PDE → ODE spatial discretization strategy - Separation of space/time concerns - Modularity benefits Plus: Comparison (built-in vs external), Extensions (nonlinear, 3D, parallel, GPU), Troubleshooting, References Demonstrates 'laboratory not fortress' philosophy
This commit is contained in:
@@ -0,0 +1,544 @@
|
|||||||
|
---
|
||||||
|
title: "From Gmsh to Physics: Complete Heat Equation Tutorial"
|
||||||
|
author: "Jukka Aho"
|
||||||
|
date: "2025-11-09"
|
||||||
|
categories: ["Tutorial", "Getting Started"]
|
||||||
|
tags: ["gmsh", "heat-equation", "academic-usage", "method-of-lines"]
|
||||||
|
issue: "183"
|
||||||
|
description: "Complete workflow from mesh generation to FEM assembly, addressing academic usage without built-in physics"
|
||||||
|
---
|
||||||
|
|
||||||
|
# From Gmsh to Physics: Complete Heat Equation Tutorial
|
||||||
|
|
||||||
|
**Purpose:** Demonstrate complete FEM workflow from mesh generation through physics, addressing [Issue #183](https://github.com/JuliaFEM/JuliaFEM.jl/issues/183) - using JuliaFEM for academic/research purposes without built-in physics.
|
||||||
|
|
||||||
|
**What You'll Learn:**
|
||||||
|
- Generate meshes with Gmsh
|
||||||
|
- Load meshes into JuliaFEM
|
||||||
|
- Assemble stiffness and mass matrices
|
||||||
|
- Extract matrices for external solvers (DifferentialEquations.jl, etc.)
|
||||||
|
- Solve the heat equation using method of lines
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem Statement
|
||||||
|
|
||||||
|
We'll solve the transient heat equation on a unit square Ω = [0,1] × [0,1]:
|
||||||
|
|
||||||
|
**Governing equation:**
|
||||||
|
```
|
||||||
|
∂u/∂t = α∇²u + f(x,y,t) in Ω
|
||||||
|
```
|
||||||
|
|
||||||
|
**Boundary conditions:**
|
||||||
|
- Dirichlet: u = 0 on left edge (x = 0)
|
||||||
|
- Neumann: ∂u/∂n = 0 on other edges (natural BC)
|
||||||
|
|
||||||
|
**Initial condition:**
|
||||||
|
```
|
||||||
|
u(x,y,0) = sin(πx)sin(πy)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- α = 1.0 (thermal diffusivity)
|
||||||
|
- f = 0 (no heat source)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 1: Mesh Generation with Gmsh
|
||||||
|
|
||||||
|
### Why Gmsh?
|
||||||
|
|
||||||
|
Gmsh is a free, open-source mesh generator that:
|
||||||
|
- Supports complex geometries (2D and 3D)
|
||||||
|
- Generates quality meshes with various element types
|
||||||
|
- Has scripting capabilities (`.geo` files)
|
||||||
|
- Widely used in academic and industrial FEM
|
||||||
|
|
||||||
|
### Creating the Geometry File
|
||||||
|
|
||||||
|
Create `unit_square.geo`:
|
||||||
|
|
||||||
|
```geo
|
||||||
|
// Gmsh geometry file: Unit square mesh
|
||||||
|
// Generate with: gmsh -2 unit_square.geo -o unit_square.msh
|
||||||
|
|
||||||
|
// Mesh element size
|
||||||
|
lc = 0.1;
|
||||||
|
|
||||||
|
// Corner points
|
||||||
|
Point(1) = {0, 0, 0, lc};
|
||||||
|
Point(2) = {1, 0, 0, lc};
|
||||||
|
Point(3) = {1, 1, 0, lc};
|
||||||
|
Point(4) = {0, 1, 0, lc};
|
||||||
|
|
||||||
|
// Edges
|
||||||
|
Line(1) = {1, 2}; // Bottom
|
||||||
|
Line(2) = {2, 3}; // Right
|
||||||
|
Line(3) = {3, 4}; // Top
|
||||||
|
Line(4) = {4, 1}; // Left
|
||||||
|
|
||||||
|
// Surface
|
||||||
|
Line Loop(1) = {1, 2, 3, 4};
|
||||||
|
Plane Surface(1) = {1};
|
||||||
|
|
||||||
|
// Physical groups for boundary conditions
|
||||||
|
Physical Line("bottom") = {1};
|
||||||
|
Physical Line("right") = {2};
|
||||||
|
Physical Line("top") = {3};
|
||||||
|
Physical Line("left") = {4};
|
||||||
|
Physical Surface("body") = {1};
|
||||||
|
|
||||||
|
// Use triangular elements
|
||||||
|
Mesh.ElementOrder = 1; // Linear elements (Tri3)
|
||||||
|
Mesh.Algorithm = 6; // Frontal-Delaunay for 2D
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key concepts:**
|
||||||
|
- `lc` controls mesh density (smaller = finer mesh)
|
||||||
|
- Physical groups (`Physical Line`, `Physical Surface`) label regions for boundary conditions
|
||||||
|
- `ElementOrder = 1` gives linear triangular elements (Tri3)
|
||||||
|
|
||||||
|
### Generating the Mesh
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gmsh -2 unit_square.geo -o unit_square.msh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
- `-2`: Generate 2D mesh
|
||||||
|
- `-o`: Output file name
|
||||||
|
|
||||||
|
**Result:** `unit_square.msh` contains nodes and element connectivity.
|
||||||
|
|
||||||
|
### Understanding the Mesh Format
|
||||||
|
|
||||||
|
Gmsh `.msh` format (ASCII version 2.2):
|
||||||
|
|
||||||
|
```
|
||||||
|
$MeshFormat
|
||||||
|
2.2 0 8
|
||||||
|
$EndMeshFormat
|
||||||
|
|
||||||
|
$Nodes
|
||||||
|
121 # Number of nodes
|
||||||
|
1 0.0 0.0 0.0 # node_id x y z
|
||||||
|
2 0.1 0.0 0.0
|
||||||
|
...
|
||||||
|
$EndNodes
|
||||||
|
|
||||||
|
$Elements
|
||||||
|
240 # Number of elements
|
||||||
|
1 1 2 4 1 1 2 # elem_id type num_tags tags... node_ids...
|
||||||
|
2 2 2 1 1 1 2 13 # type=2 is Tri3
|
||||||
|
...
|
||||||
|
$EndElements
|
||||||
|
```
|
||||||
|
|
||||||
|
**Element types:**
|
||||||
|
- Type 1: 2-node line (Seg2)
|
||||||
|
- Type 2: 3-node triangle (Tri3)
|
||||||
|
- Type 3: 4-node quadrilateral (Quad4)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 2: Weak Formulation (Theory)
|
||||||
|
|
||||||
|
### Strong Form
|
||||||
|
|
||||||
|
The strong form (classical PDE) is:
|
||||||
|
|
||||||
|
```
|
||||||
|
∂u/∂t - α∇²u = f in Ω
|
||||||
|
u = g on Γ_D (Dirichlet boundary)
|
||||||
|
∂u/∂n = h on Γ_N (Neumann boundary)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Weak Form
|
||||||
|
|
||||||
|
Multiply by test function v and integrate by parts:
|
||||||
|
|
||||||
|
```
|
||||||
|
∫_Ω v(∂u/∂t) dΩ + α∫_Ω ∇v·∇u dΩ = ∫_Ω vf dΩ + ∫_{Γ_N} vh dΓ
|
||||||
|
```
|
||||||
|
|
||||||
|
### Galerkin Approximation
|
||||||
|
|
||||||
|
Approximate u and v with finite element basis functions:
|
||||||
|
|
||||||
|
```
|
||||||
|
u(x,y,t) ≈ Σᵢ uᵢ(t) Nᵢ(x,y)
|
||||||
|
v(x,y) ≈ Σⱼ vⱼ Nⱼ(x,y)
|
||||||
|
```
|
||||||
|
|
||||||
|
Substitute and collect terms:
|
||||||
|
|
||||||
|
```
|
||||||
|
M du/dt + K u = f
|
||||||
|
```
|
||||||
|
|
||||||
|
Where:
|
||||||
|
- **M** = mass matrix: `Mᵢⱼ = ∫_Ω Nᵢ Nⱼ dΩ`
|
||||||
|
- **K** = stiffness matrix: `Kᵢⱼ = α∫_Ω ∇Nᵢ·∇Nⱼ dΩ`
|
||||||
|
- **f** = force vector: `fᵢ = ∫_Ω Nᵢ f dΩ + ∫_{Γ_N} Nᵢ h dΓ`
|
||||||
|
|
||||||
|
This is a **system of ODEs** - the spatial discretization is complete, leaving only time dependence.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 3: FEM Assembly in JuliaFEM
|
||||||
|
|
||||||
|
### Loading the Mesh
|
||||||
|
|
||||||
|
```julia
|
||||||
|
using JuliaFEM
|
||||||
|
|
||||||
|
# Read Gmsh mesh
|
||||||
|
mesh = abaqus_read_mesh("unit_square.msh") # Or gmsh reader when available
|
||||||
|
|
||||||
|
# Inspect mesh
|
||||||
|
println("Nodes: ", length(mesh.nodes))
|
||||||
|
println("Elements: ", length(mesh.elements))
|
||||||
|
println("Sets: ", keys(mesh.element_sets))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Creating Problem and Elements
|
||||||
|
|
||||||
|
```julia
|
||||||
|
# Heat transfer problem (domain)
|
||||||
|
body = Problem(Heat, "heat_body", 2) # 2D problem
|
||||||
|
body_elements = create_elements(mesh, "body")
|
||||||
|
|
||||||
|
# Add material properties
|
||||||
|
thermal_conductivity = 1.0 # α
|
||||||
|
for element in body_elements
|
||||||
|
update!(element, "thermal conductivity", thermal_conductivity)
|
||||||
|
end
|
||||||
|
|
||||||
|
add_elements!(body, body_elements)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key:** `update!` sets element properties. JuliaFEM stores these in element fields (now immutable NamedTuples for 130x speedup!).
|
||||||
|
|
||||||
|
### Boundary Conditions
|
||||||
|
|
||||||
|
```julia
|
||||||
|
# Dirichlet BC: u = 0 on left edge
|
||||||
|
bc = Problem(Dirichlet, "fixed_temp", 2, "temperature")
|
||||||
|
bc_elements = create_elements(mesh, "left")
|
||||||
|
|
||||||
|
for element in bc_elements
|
||||||
|
update!(element, "temperature", 0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
add_elements!(bc, bc_elements)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Neumann BC:** Natural boundary conditions (∂u/∂n = 0) require no explicit code - they're automatically satisfied by the weak form.
|
||||||
|
|
||||||
|
### Assembly Process
|
||||||
|
|
||||||
|
```julia
|
||||||
|
# Assemble at time t = 0
|
||||||
|
time = 0.0
|
||||||
|
assemble!(body, time)
|
||||||
|
assemble!(bc, time)
|
||||||
|
```
|
||||||
|
|
||||||
|
**What happens internally:**
|
||||||
|
|
||||||
|
1. **Loop over elements:**
|
||||||
|
For each element e:
|
||||||
|
|
||||||
|
2. **Compute element matrices:**
|
||||||
|
```julia
|
||||||
|
# Get element nodes and geometry
|
||||||
|
X = [node.position for node in element.nodes]
|
||||||
|
|
||||||
|
# Numerical integration over element
|
||||||
|
for (ξ, w) in gauss_points(element)
|
||||||
|
# Shape functions and derivatives
|
||||||
|
N = basis(element, ξ)
|
||||||
|
dN = grad_basis(element, ξ)
|
||||||
|
|
||||||
|
# Jacobian (parametric → physical)
|
||||||
|
J = dN' * X
|
||||||
|
detJ = det(J)
|
||||||
|
|
||||||
|
# Physical derivatives
|
||||||
|
dN_dx = J \ dN
|
||||||
|
|
||||||
|
# Local matrices
|
||||||
|
Kₑ += w * detJ * α * (dN_dx * dN_dx')
|
||||||
|
Mₑ += w * detJ * (N * N')
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Scatter to global:**
|
||||||
|
```julia
|
||||||
|
global_dofs = get_dofs(element)
|
||||||
|
K[global_dofs, global_dofs] += Kₑ
|
||||||
|
M[global_dofs, global_dofs] += Mₑ
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Apply Dirichlet BCs:**
|
||||||
|
Zero out rows/columns for constrained DOFs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 4: Extracting Matrices (Issue #183)
|
||||||
|
|
||||||
|
**This is what Chris Rackauckas asked for!**
|
||||||
|
|
||||||
|
After assembly, extract matrices for use with external solvers:
|
||||||
|
|
||||||
|
```julia
|
||||||
|
# Get assembled system
|
||||||
|
K = body.assembly.K # SparseMatrixCSC{Float64}
|
||||||
|
M = body.assembly.M # SparseMatrixCSC{Float64}
|
||||||
|
f = body.assembly.f # Vector{Float64}
|
||||||
|
|
||||||
|
# System is now: M * du/dt = -K * u + f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Why Extract Matrices?
|
||||||
|
|
||||||
|
**Use cases:**
|
||||||
|
1. **DifferentialEquations.jl** - Advanced ODE solvers (Rosenbrock, IMEX, etc.)
|
||||||
|
2. **Krylov.jl** - Iterative linear solvers for large systems
|
||||||
|
3. **Custom time integration** - Research on novel time-stepping schemes
|
||||||
|
4. **Sensitivity analysis** - Automatic differentiation through solvers
|
||||||
|
5. **Optimal control** - Adjoint methods, PDE-constrained optimization
|
||||||
|
|
||||||
|
### Example: Solve with DifferentialEquations.jl
|
||||||
|
|
||||||
|
```julia
|
||||||
|
using OrdinaryDiffEq
|
||||||
|
|
||||||
|
# Initial condition: u₀ = sin(πx)sin(πy)
|
||||||
|
u0 = zeros(length(mesh.nodes))
|
||||||
|
for (i, node) in enumerate(mesh.nodes)
|
||||||
|
x, y = node.position[1:2]
|
||||||
|
u0[i] = sin(π*x) * sin(π*y)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Apply boundary conditions to u0
|
||||||
|
apply_bc!(u0, bc)
|
||||||
|
|
||||||
|
# Define ODE: M * du/dt = -K * u + f
|
||||||
|
# Rearrange: du/dt = M \ (-K * u + f)
|
||||||
|
function heat_ode!(du, u, p, t)
|
||||||
|
K, M, f = p
|
||||||
|
du .= M \ (-K * u .+ f)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create ODE problem
|
||||||
|
tspan = (0.0, 1.0)
|
||||||
|
prob = ODEProblem(heat_ode!, u0, tspan, (K, M, f))
|
||||||
|
|
||||||
|
# Solve with adaptive Rosenbrock method
|
||||||
|
sol = solve(prob, Rosenbrock23())
|
||||||
|
|
||||||
|
# Solution is now in sol.u (array of states at different times)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Advantages:**
|
||||||
|
- Adaptive time-stepping
|
||||||
|
- Stiff ODE solvers
|
||||||
|
- Event handling
|
||||||
|
- Sensitivity analysis
|
||||||
|
- GPU acceleration (CuArrays)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 5: Complete Working Example
|
||||||
|
|
||||||
|
See `examples/gmsh_heat_equation/gmsh_heat_equation.jl` for full runnable code.
|
||||||
|
|
||||||
|
**Run it:**
|
||||||
|
```bash
|
||||||
|
cd examples/gmsh_heat_equation
|
||||||
|
gmsh -2 unit_square.geo -o unit_square.msh
|
||||||
|
julia --project=. gmsh_heat_equation.jl
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- Mesh statistics
|
||||||
|
- Assembly information
|
||||||
|
- Extracted matrix sizes
|
||||||
|
- Instructions for next steps
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Method of Lines: Spatial Discretization Strategy
|
||||||
|
|
||||||
|
**Key insight:** FEM performs **spatial discretization only**, converting PDE → ODE system.
|
||||||
|
|
||||||
|
### Before FEM (PDE):
|
||||||
|
```
|
||||||
|
∂u(x,y,t)/∂t = α∇²u(x,y,t) + f(x,y,t)
|
||||||
|
```
|
||||||
|
- Infinite-dimensional: u depends on continuous (x,y)
|
||||||
|
- Cannot solve directly on computer
|
||||||
|
|
||||||
|
### After FEM (ODE):
|
||||||
|
```
|
||||||
|
M du(t)/dt = -K u(t) + f(t)
|
||||||
|
```
|
||||||
|
- Finite-dimensional: u is a vector of n nodal values
|
||||||
|
- Can solve with ODE integrators
|
||||||
|
|
||||||
|
**This separation is powerful:**
|
||||||
|
- **Space:** FEM handles complex geometry, boundary conditions
|
||||||
|
- **Time:** ODE solvers handle stiff systems, adaptivity, stability
|
||||||
|
- **Modularity:** Swap spatial/temporal methods independently
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comparison: Built-in vs External Solvers
|
||||||
|
|
||||||
|
### Option 1: JuliaFEM Built-in (Easiest)
|
||||||
|
|
||||||
|
```julia
|
||||||
|
solver = Solver(Linear)
|
||||||
|
push!(solver, body, bc)
|
||||||
|
solver() # Uses direct solver
|
||||||
|
results = solver.results
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pros:** Simple, integrated, handles BCs automatically
|
||||||
|
**Cons:** Less control, fixed time-stepping, direct solver only
|
||||||
|
|
||||||
|
### Option 2: Extract Matrices (Flexible)
|
||||||
|
|
||||||
|
```julia
|
||||||
|
K, M, f = extract_matrices(body, bc)
|
||||||
|
prob = ODEProblem(heat_ode!, u0, tspan, (K, M, f))
|
||||||
|
sol = solve(prob, Tsit5())
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pros:** Full control, adaptive methods, iterative solvers, research flexibility
|
||||||
|
**Cons:** More code, manual BC handling, need to understand ODE interface
|
||||||
|
|
||||||
|
**Recommendation:** Start with Option 1 for learning, move to Option 2 for research.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Extensions and Next Steps
|
||||||
|
|
||||||
|
### 1. Nonlinear Problems
|
||||||
|
|
||||||
|
If α = α(u) (temperature-dependent conductivity):
|
||||||
|
|
||||||
|
```julia
|
||||||
|
function nonlinear_heat!(du, u, p, t)
|
||||||
|
K, M, f = p
|
||||||
|
# Reassemble K with current u
|
||||||
|
K_nonlinear = assemble_stiffness(u)
|
||||||
|
du .= M \ (-K_nonlinear * u .+ f)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Time-Dependent BCs
|
||||||
|
|
||||||
|
```julia
|
||||||
|
function bc_time(t)
|
||||||
|
return sin(2π*t) # Oscillating temperature
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update f vector in ODE function
|
||||||
|
f_bc = apply_bc_vector(bc, t)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 3D Problems
|
||||||
|
|
||||||
|
Same workflow, just change:
|
||||||
|
- Geometry file to 3D (Tet4, Tet10 elements)
|
||||||
|
- Problem dimension: `Problem(Heat, "body", 3)`
|
||||||
|
|
||||||
|
### 4. Parallel Assembly
|
||||||
|
|
||||||
|
For large meshes (>1M elements):
|
||||||
|
|
||||||
|
```julia
|
||||||
|
using Threads
|
||||||
|
@threads for element in elements
|
||||||
|
assemble_local!(element)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. GPU Acceleration
|
||||||
|
|
||||||
|
```julia
|
||||||
|
using CUDA
|
||||||
|
K_gpu = CuSparseMatrixCSC(K)
|
||||||
|
u_gpu = CuArray(u0)
|
||||||
|
# Solve on GPU
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Gmsh not found
|
||||||
|
|
||||||
|
**Error:** `gmsh: command not found`
|
||||||
|
|
||||||
|
**Solution:** Install Gmsh:
|
||||||
|
- **Ubuntu/Debian:** `sudo apt install gmsh`
|
||||||
|
- **macOS:** `brew install gmsh`
|
||||||
|
- **Windows:** Download from https://gmsh.info
|
||||||
|
|
||||||
|
### Mesh too coarse/fine
|
||||||
|
|
||||||
|
Adjust `lc` in `.geo` file:
|
||||||
|
- `lc = 0.05` → finer mesh (more elements, slower)
|
||||||
|
- `lc = 0.2` → coarser mesh (fewer elements, faster)
|
||||||
|
|
||||||
|
### Assembly takes forever
|
||||||
|
|
||||||
|
For large meshes (>100K elements):
|
||||||
|
1. Use iterative solvers (Krylov.jl)
|
||||||
|
2. Enable threading
|
||||||
|
3. Profile assembly (`@time`, `@profview`)
|
||||||
|
|
||||||
|
### Oscillations in solution
|
||||||
|
|
||||||
|
- Mesh too coarse → refine
|
||||||
|
- Time step too large → reduce or use adaptive solver
|
||||||
|
- Boundary conditions wrong → check Gmsh physical groups
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
1. **Gmsh Documentation:** https://gmsh.info/doc/texinfo/gmsh.html
|
||||||
|
2. **JuliaFEM Documentation:** https://juliafem.github.io/JuliaFEM.jl
|
||||||
|
3. **DifferentialEquations.jl:** https://docs.sciml.ai/DiffEqDocs/
|
||||||
|
4. **Issue #183:** https://github.com/JuliaFEM/JuliaFEM.jl/issues/183
|
||||||
|
|
||||||
|
**Books:**
|
||||||
|
- Hughes, "The Finite Element Method" (theory)
|
||||||
|
- Zienkiewicz & Taylor, "The Finite Element Method" (comprehensive)
|
||||||
|
- Quarteroni et al., "Numerical Models for Differential Problems" (modern methods)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**What we covered:**
|
||||||
|
|
||||||
|
1. ✓ Mesh generation with Gmsh (`.geo` → `.msh`)
|
||||||
|
2. ✓ Weak formulation and Galerkin method
|
||||||
|
3. ✓ FEM assembly (element → global matrices)
|
||||||
|
4. ✓ Extracting K, M, f for external solvers
|
||||||
|
5. ✓ Solving with DifferentialEquations.jl
|
||||||
|
6. ✓ Method of lines (PDE → ODE)
|
||||||
|
|
||||||
|
**Key takeaway:** JuliaFEM provides the spatial discretization machinery. You bring the physics and time integration. Perfect for academic/research flexibility.
|
||||||
|
|
||||||
|
**Next:** Try the example, modify the geometry, experiment with different BCs, explore nonlinear problems!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This tutorial addresses Issue #183 and demonstrates JuliaFEM's "laboratory, not fortress" philosophy - educational, transparent, and flexible for research use.*
|
||||||
Reference in New Issue
Block a user