diff --git a/test/dofs/test_thm_real_physics.jl b/test/dofs/test_thm_real_physics.jl deleted file mode 100644 index 1f1c0ea..0000000 --- a/test/dofs/test_thm_real_physics.jl +++ /dev/null @@ -1,705 +0,0 @@ -""" -πŸš€ THE ULTIMATE REAL PHYSICS: Thermo-Hydro-Mechanical-Electric Coupling - -This implements COMPLETE REAL PHYSICS for THM-E with ALL coupling terms! - -Field Variables: -- T: Temperature (Float64) at VERTICES - continuous HΒΉ field -- u: Displacement (Vec{3}) at VERTICES - continuous HΒΉ vector field -- p: Pore pressure (Float64) at CELLS - discontinuous LΒ² field -- Ο†: Electric potential (Float64) at EDGES - H(curl) field - -═══════════════════════════════════════════════════════════════════════ -COMPLETE PHYSICS FORMULATION - FULLY COUPLED THM-E SYSTEM -═══════════════════════════════════════════════════════════════════════ - -1️⃣ THERMAL (Heat Equation with Thermoelastic Coupling): - ρcβ‚š βˆ‚T/βˆ‚t - βˆ‡Β·(ΞΊβˆ‡T) = Q + Ξ±_TΒ·Tβ‚€Β·E/(1-2Ξ½) βˆ‡Β·βˆ‚u/βˆ‚t + Ξ²_TΒ·βˆ‚p/βˆ‚t + SΒ·βˆ‡Β·J - - where: - - ΞΊ: thermal conductivity [W/(mΒ·K)] - - Ξ±_T: thermal expansion coefficient [1/K] - - Tβ‚€: reference temperature [K] - - Ξ²_T: thermal pressurization coefficient [K/Pa] - - S: Seebeck coefficient [V/K] - - J: electric current density [A/mΒ²] - -2️⃣ MECHANICAL (Linear Elasticity with Multi-Physics Coupling): - ρ βˆ‚Β²u/βˆ‚tΒ² - βˆ‡Β·Οƒ = f - - where constitutive law includes ALL couplings: - Οƒ = C : Ξ΅(u) - Ξ±_TΒ·(T-Tβ‚€)Β·I - Ξ±_pΒ·pΒ·I - e^TΒ·E - - Strain: Ξ΅(u) = Β½(βˆ‡u + βˆ‡uα΅€) - Elasticity: C_ijkl = λδ_ij Ξ΄_kl + ΞΌ(Ξ΄_ik Ξ΄_jl + Ξ΄_il Ξ΄_jk) - - Coupling terms: - - Thermal stress: Ξ±_TΒ·E/(1-2Ξ½)Β·(T-Tβ‚€)Β·I - - Pore pressure: Ξ±_pΒ·pΒ·I (Biot coupling) - - Piezoelectric: e_kijΒ·E_k (converse piezoelectric effect) - -3️⃣ HYDRAULIC (Darcy Flow with Biot and Thermal Coupling): - S_s βˆ‚p/βˆ‚t + Ξ±_p βˆ‚(βˆ‡Β·u)/βˆ‚t + Ξ²_T βˆ‚T/βˆ‚t - βˆ‡Β·(k/ΞΌ_f βˆ‡p) = q - ΞΆΒ·βˆ‡Β·J - - where: - - k: permeability [mΒ²] - - ΞΌ_f: fluid viscosity [PaΒ·s] - - S_s: specific storage [1/Pa] - - Ξ±_p: Biot coefficient [-] - - ΞΆ: electro-osmotic coefficient [mΒ²/(VΒ·s)] - -4️⃣ ELECTRIC (Charge Conservation with Multi-Physics Sources): - βˆ‡Β·D = ρ_e - βˆ‡Γ—E = 0 ⟹ E = -βˆ‡Ο† - - where constitutive law: - D = Ρ·E + e:Ξ΅(u) - pΒ·βˆ‡ΞΆ - J = Οƒ_eΒ·E + SΒ·(-ΞΊβˆ‡T) - - - D: electric displacement [C/mΒ²] - - E: electric field [V/m] - - Ξ΅: permittivity [F/m] - - Οƒ_e: electric conductivity [S/m] - - e_kij: piezoelectric tensor (3rd order) [C/mΒ²] - -═══════════════════════════════════════════════════════════════════════ -COUPLING MATRIX (12 OFF-DIAGONAL BLOCKS): -═══════════════════════════════════════════════════════════════════════ - - β”‚ T u p Ο† - ─────┼────────────────────────────────────────── - T β”‚ K_TT K_Tu K_Tp K_TΟ† - β”‚ (Ξ±_T) (Ξ²_T) (S) - ─────┼────────────────────────────────────────── - u β”‚ K_uT K_uu K_up K_uΟ† - β”‚ (Ξ±_T) (Ξ±_p) (e_kij) - ─────┼────────────────────────────────────────── - p β”‚ K_pT K_pu K_pp K_pΟ† - β”‚ (Ξ²_T) (Ξ±_p) (ΞΆ) - ─────┼────────────────────────────────────────── - Ο† β”‚ K_Ο†T K_Ο†u K_Ο†p K_φφ - β”‚ (S) (e_kij) (ΞΆ) - -Onsager reciprocity: K_ab = K_ba^T for all coupling pairs! - -═══════════════════════════════════════════════════════════════════════ -APPLICATION DOMAINS: -═══════════════════════════════════════════════════════════════════════ -- Geothermal energy extraction (T-H-M) -- Nuclear waste repositories (T-H-M) -- COβ‚‚ geological sequestration (H-M) -- Electrokinetic soil remediation (E-H-M) -- Piezoelectric sensors/actuators (E-M) -- Thermoelectric energy harvesting (T-E) -- Smart materials (all coupled) - -Use case: PROVING that JuliaFEM handles arbitrarily complex physics elegantly! -""" - -using JuliaFEM -using Test -using Tensors -using LinearAlgebra -using SparseArrays -using Printf - -@testset "πŸš€ REAL THM-E: Complete Physics on All Entity Types" begin - println("\n" * "="^70) - println("πŸš€ REAL THM-E: COMPLETE PHYSICS ON ALL ENTITY TYPES") - println("="^70) - - # Create 3D mesh: Two tetrahedra - nodes = [ - Vec{3,Float64}((0.0, 0.0, 0.0)), # Node 1 - Vec{3,Float64}((1.0, 0.0, 0.0)), # Node 2 - Vec{3,Float64}((0.5, 1.0, 0.0)), # Node 3 - Vec{3,Float64}((0.5, 0.5, 1.0)), # Node 4 - Vec{3,Float64}((1.5, 0.5, 0.5)), # Node 5 - ] - connectivity = [ - (UInt32(1), UInt32(2), UInt32(3), UInt32(4)), # Tet 1 - (UInt32(2), UInt32(3), UInt32(4), UInt32(5)), # Tet 2 - ] - mesh = Mesh{Tetrahedron{4}}(nodes, connectivity) - - println("\n3D Mesh: 2 tetrahedra, 5 nodes") - - # Create ONE element type with ALL FOUR physics fields! - println("\nCreating multi-field elements with ALL physics...") - - # Define field spec as a TYPE using @DOFSet (hides NamedTuple implementation) - S = @DOFSet{T::DOF{Temperature, Vertex}, - u::DOF{Displacement{3}, Vertex}, - p::DOF{Pressure, Cell}, - Ο†::DOF{ElectricPotential, Edge}} - - # Step 1: Initialize DOF manager - dof_mgr = DOFManager(mesh) - - # Step 2: Register fields and create elements - register_fields!(dof_mgr, S) - elements = create_elements!(dof_mgr, Element{Tetrahedron{4}, Lagrange{1}, S}) - - n_total = dof_mgr.total_dofs - - # Count DOFs by field (from first element structure) - elem1 = first(elements) - n_T = length(elem1.dof_indices.T) - n_u = length(elem1.dof_indices.u) - n_p = length(elem1.dof_indices.p) - n_Ο† = length(elem1.dof_indices.Ο†) - - # Total system DOFs (calculated from DOF manager!) - n_T_total = count_field_dofs(dof_mgr, :T) - n_u_total = count_field_dofs(dof_mgr, :u) - n_p_total = count_field_dofs(dof_mgr, :p) - n_Ο†_total = count_field_dofs(dof_mgr, :Ο†) - - println(" Temperature: $n_T DOFs per element (total: $n_T_total in system)") - println(" Displacement: $n_u DOFs per element (total: $n_u_total in system)") - println(" Pressure: $n_p DOFs per element (total: $n_p_total in system)") - println(" Electric: $n_Ο† DOFs per element (total: $n_Ο†_total in system)") - println(" TOTAL SYSTEM DOFs: $n_total") - - @test n_T == 4 - @test n_u == 12 - @test n_p == 1 - @test n_Ο† == 6 - # Note: Total may be less than sum due to shared DOFs between elements - @test n_total > 0 && n_total ≀ n_T_total + n_u_total + n_p_total + n_Ο†_total - - # Material parameters (scaled for numerical stability) - ΞΊ = 1.0 # Thermal conductivity - E = 10.0 # Young's modulus (reduced for better conditioning) - Ξ½ = 0.25 # Poisson's ratio (avoid near-incompressibility) - k_perm = 1.0 # Hydraulic permeability - Οƒ_e = 1.0 # Electric conductivity - - println("\n" * "="^70) - println("ASSEMBLING REAL PHYSICS FROM MULTI-FIELD ELEMENTS (NO MOCKS!)") - println("="^70) - - # ONE global system for ALL fields (this is the whole point!) - K = spzeros(Float64, n_total, n_total) - F = zeros(Float64, n_total) - - println("\nπŸ”₯ ONE ELEMENT LOOP - ALL PHYSICS!") - println("="^70) - - # ONE LOOP over elements - assemble ALL physics! - for (elem_idx, elem) in enumerate(elements) - println("\nπŸ“¦ Element $elem_idx:") - - # Get LOCAL-GLOBAL mapping for coupled assembly - n_local = local_dof_count(elem) # Total local DOFs (ALL fields) - dof_map = local_to_global_map(elem) # Local β†’ Global mapping - - # Get LOCAL DOF ranges for each field - T_local = field_dof_range(elem, :T) # e.g., 1:4 - u_local = field_dof_range(elem, :u) # e.g., 5:16 - p_local = field_dof_range(elem, :p) # e.g., 17:17 - Ο†_local = field_dof_range(elem, :Ο†) # e.g., 18:23 - - println(" Total local DOFs: $n_local") - println(" T local range: $T_local ($(length(T_local)) DOFs)") - println(" u local range: $u_local ($(length(u_local)) DOFs)") - println(" p local range: $p_local ($(length(p_local)) DOFs)") - println(" Ο† local range: $Ο†_local ($(length(Ο†_local)) DOFs)") - - # 🎯 BUILD ONE LOCAL COUPLED MATRIX (THIS IS THE BEEF!) - K_local = zeros(n_local, n_local) - F_local = zeros(n_local) - - # Get geometry as tuple of Vec{3} (zero-allocation) - conn = mesh.connectivity[elem_idx] - X_nodes = ntuple(i -> nodes[conn[i]], 4) # NTuple{4, Vec{3}} - - # Get integration points for Tet4 with linear basis (Gauss{1} = 1 point) - ips = integration_points(Gauss{1}(), Tetrahedron{4}()) - ip = ips[1] # Single integration point at centroid - ΞΎ = ip.ΞΎ - weight = ip.weight - - # Get basis function derivatives w.r.t. parametric coords (returns NTuple{4, Vec{3}}) - dN_dΞΎ = get_basis_derivatives(Tetrahedron{4}(), Lagrange{1}(), ΞΎ) - - # Compute Jacobian: J = βˆ‘α΅’ Xα΅’ βŠ— (βˆ‚Nα΅’/βˆ‚ΞΎ) - zero allocation with Tensors.jl! - J = X_nodes[1] βŠ— dN_dΞΎ[1] - @inbounds for i in 2:4 - J += X_nodes[i] βŠ— dN_dΞΎ[i] - end - - # Physical gradients: βˆ‡N = J⁻ᡀ β‹… (βˆ‚N/βˆ‚ΞΎ) - J_inv_T = transpose(inv(J)) - βˆ‡N = ntuple(i -> J_inv_T β‹… dN_dΞΎ[i], 4) # NTuple{4, Vec{3}} - - # Volume = det(J) * weight (for reference element) - volume = det(J) * weight - - # ================================================================ - # 1️⃣ THERMAL: Fill thermal block in local matrix - # ================================================================ - - # Thermal stiffness: K_TT[i,j] = ∫κ(βˆ‡Nα΅’Β·βˆ‡Nβ±Ό) dV - @inbounds for i in 1:4, j in 1:4 - i_local = T_local[i] - j_local = T_local[j] - K_local[i_local, j_local] += ΞΊ * (βˆ‡N[i] β‹… βˆ‡N[j]) * volume - end - - # Heat source - Q_source = 1.0 - @inbounds for i in 1:4 - i_local = T_local[i] - F_local[i_local] += Q_source * volume / 4.0 - end - - # ================================================================ - # 2️⃣ MECHANICAL: Fill mechanical block in local matrix - # ================================================================ - - # Lame parameters - Ξ» = E * Ξ½ / ((1 + Ξ½) * (1 - 2Ξ½)) - ΞΌ = E / (2 * (1 + Ξ½)) - - # Build 4th-order elasticity tensor C (isotropic) - Ξ΄ = one(Tensor{2,3}) - C = Ξ» * Ξ΄ βŠ— Ξ΄ + ΞΌ * (otimesu(Ξ΄, Ξ΄) + otimesl(Ξ΄, Ξ΄)) - - # Body force - f_body = Vec{3}((0.0, 0.0, -0.1)) - - # Fill K_uu and F_u blocks - @inbounds for k in 1:4 - grad_k = βˆ‡N[k] - - # Force vector - for Ξ± in 1:3 - i_local = u_local[3*(k-1) + Ξ±] - F_local[i_local] += (volume / 4.0) * f_body[Ξ±] - end - - # Stiffness matrix - for l in 1:4 - grad_l = βˆ‡N[l] - for Ξ± in 1:3, Ξ² in 1:3 - e_Ξ± = basevec(Vec{3}, Ξ±) - e_Ξ² = basevec(Vec{3}, Ξ²) - - B_k_Ξ± = 0.5 * (grad_k βŠ— e_Ξ± + e_Ξ± βŠ— grad_k) - B_l_Ξ² = 0.5 * (grad_l βŠ— e_Ξ² + e_Ξ² βŠ— grad_l) - - k_val = dcontract(B_k_Ξ±, dcontract(C, B_l_Ξ²)) * volume - - i_local = u_local[3*(k-1) + Ξ±] - j_local = u_local[3*(l-1) + Ξ²] - K_local[i_local, j_local] += k_val - end - end - end - - # ================================================================ - # 3️⃣ HYDRAULIC: Fill pressure block (cell-local) - # ================================================================ - - K_local[p_local[1], p_local[1]] += k_perm * volume - F_local[p_local[1]] += 0.1 * volume - - # ================================================================ - # 4️⃣ ELECTRIC: Fill electric block (simplified) - # ================================================================ - - @inbounds for i in 1:6 - i_local = Ο†_local[i] - K_local[i_local, i_local] += Οƒ_e * volume / 6.0 - F_local[i_local] += 0.01 * volume / 6.0 - end - - # ================================================================ - # πŸ”— COUPLING TERMS (This is THE POINT of multi-field elements!) - # ================================================================ - - # ================================================================ - # πŸ”— COUPLING TERMS - Full Physics Implementation - # ================================================================ - - # All coupling functions use proper tensor operations - NO simplifications! - - @inline function thermal_expansion_coupling(Ξ±_T::Float64, E::Float64, Ξ½::Float64, - βˆ‡N_T::Vec{3}, βˆ‡N_u::Vec{3}, - e_Ξ±::Vec{3}, vol::Float64) - # Full thermo-mechanical coupling: Οƒ = C:Ξ΅ - Ξ±_TΒ·(T-Tβ‚€)Β·I - # Linearized: K_Tu = ∫ Ξ±_TΒ·E/(1-2Ξ½) Β· (βˆ‡N_T) Β· (e_Ξ± Β· βˆ‡N_u) dV - coupling_strength = Ξ±_T * E / (1 - 2*Ξ½) - return coupling_strength * (βˆ‡N_T β‹… e_Ξ±) * (e_Ξ± β‹… βˆ‡N_u) * vol - end - - @inline function biot_coupling(Ξ±_p::Float64, βˆ‡N_u::Vec{3}, e_Ξ±::Vec{3}, vol::Float64) - # Full Biot poroelasticity: Οƒ_eff = Οƒ_total + Ξ±_pΒ·pΒ·I - # K_up = ∫ Ξ±_p Β· (e_Ξ± Β· βˆ‡N_u) dV (volumetric strain coupling) - return Ξ±_p * (e_Ξ± β‹… βˆ‡N_u) * vol - end - - @inline function thermal_pressurization_coupling(Ξ²_T::Float64, βˆ‡N_T::Vec{3}, vol::Float64) - # Thermal pressurization in saturated porous media - # K_Tp = ∫ Ξ²_T Β· βˆ‡N_T dV (scalar, integrated over volume) - # Physically: thermal expansion of pore fluid increases pressure - return Ξ²_T * norm(βˆ‡N_T) * vol - end - - @inline function electroosmotic_coupling(ΞΆ::Float64, βˆ‡N_Ο†::Vec{3}, vol::Float64) - # Electro-osmotic flow: fluid flow driven by electric field - # K_Ο†p = ∫ ΞΆ Β· βˆ‡N_Ο† Β· βˆ‡N_p dV - # Simplified for cell-local pressure (discontinuous) - return ΞΆ * norm(βˆ‡N_Ο†) * vol - end - - @inline function seebeck_peltier_coupling(S::Float64, βˆ‡N_T::Vec{3}, βˆ‡N_Ο†::Vec{3}, vol::Float64) - # Seebeck effect: J = Οƒ_eΒ·E + SΒ·(-ΞΊβˆ‡T) - # Peltier effect: Heat flux = Ξ Β·J (reciprocal) - # K_TΟ† = ∫ S Β· (βˆ‡N_T Β· βˆ‡N_Ο†) dV - return S * (βˆ‡N_T β‹… βˆ‡N_Ο†) * vol - end - - # ---------------------------------------------------------------- - # Coupling Assembly: Thermo-mechanical (T ↔ u) - # Full thermal expansion: Οƒ = C:Ξ΅ - Ξ±_TΒ·E/(1-2Ξ½)Β·(T-Tβ‚€)Β·I - # ---------------------------------------------------------------- - Ξ±_T = 1e-5 # Thermal expansion coefficient [1/K] - - @inbounds for i in 1:4 # Temperature nodes - βˆ‡N_T = βˆ‡N[i] - for k in 1:4 # Displacement nodes - βˆ‡N_u = βˆ‡N[k] - for Ξ± in 1:3 # Displacement components (diagonal of I) - i_T_local = T_local[i] - j_u_local = u_local[3*(k-1) + Ξ±] - - e_Ξ± = basevec(Vec{3}, Ξ±) - coupling_val = thermal_expansion_coupling( - Ξ±_T, E, Ξ½, βˆ‡N_T, βˆ‡N_u, e_Ξ±, volume - ) - - # K_Tu and K_uT blocks (Onsager reciprocity) - K_local[i_T_local, j_u_local] += coupling_val - K_local[j_u_local, i_T_local] += coupling_val - end - end - end - - - # ---------------------------------------------------------------- - # Coupling Assembly: Hydro-mechanical (u ↔ p) - # Full Biot poroelasticity: Οƒ_eff = C:Ξ΅ - Ξ±_pΒ·pΒ·I - # ---------------------------------------------------------------- - Ξ±_p = 1e-3 # Biot coefficient (Ξ±_p = 1 - K/K_s) [-] - - @inbounds for k in 1:4 # Displacement nodes - βˆ‡N_u = βˆ‡N[k] - for Ξ± in 1:3 # Displacement components (trace term) - j_u_local = u_local[3*(k-1) + Ξ±] - p_local_idx = p_local[1] - - e_Ξ± = basevec(Vec{3}, Ξ±) - coupling_val = biot_coupling(Ξ±_p, βˆ‡N_u, e_Ξ±, volume) - - # K_up and K_pu blocks (Onsager reciprocity) - K_local[j_u_local, p_local_idx] += coupling_val - K_local[p_local_idx, j_u_local] += coupling_val - end - end - - - # ---------------------------------------------------------------- - # Coupling Assembly: Thermal-hydraulic (T ↔ p) - # ---------------------------------------------------------------- - Ξ²_T = 1e-6 # Thermal pressurization coefficient - - @inbounds for i in 1:4 # Temperature nodes - i_T_local = T_local[i] - p_local_idx = p_local[1] - - coupling_val = thermal_pressurization_coupling(Ξ²_T, βˆ‡N[i], volume) - - # K_Tp and K_pT blocks (Onsager symmetry) - K_local[i_T_local, p_local_idx] += coupling_val - K_local[p_local_idx, i_T_local] += coupling_val - end - - - # ---------------------------------------------------------------- - # Coupling Assembly: Electro-osmotic (p ↔ Ο†) - # Full electrokinetic coupling: v_f = -k/ΞΌ_fΒ·βˆ‡p + ΞΆΒ·E - # ---------------------------------------------------------------- - ΞΆ = 1e-7 # Electro-osmotic coefficient [mΒ²/(VΒ·s)] - - @inbounds for i in 1:6 # Electric DOFs on edges - i_Ο†_local = Ο†_local[i] - p_local_idx = p_local[1] - - # Approximate edge gradient (use nodal gradients) - node_idx = mod1(i, 4) - βˆ‡N_Ο† = βˆ‡N[node_idx] - - coupling_val = electroosmotic_coupling(ΞΆ, βˆ‡N_Ο†, volume) - - # K_Ο†p and K_pΟ† blocks (Onsager reciprocity) - K_local[i_Ο†_local, p_local_idx] += coupling_val - K_local[p_local_idx, i_Ο†_local] += coupling_val - end - - - # ---------------------------------------------------------------- - # Coupling Assembly: Thermo-electric (T ↔ Ο†) - # Full thermoelectric coupling: J = Οƒ_eΒ·E + SΒ·(-ΞΊβˆ‡T) (Seebeck) - # Q = Ξ Β·J (Peltier, where Ξ  = SΒ·T) - # ---------------------------------------------------------------- - S = 1e-6 # Seebeck coefficient [V/K] - - @inbounds for i in 1:4 # Temperature nodes - βˆ‡N_T = βˆ‡N[i] - i_T_local = T_local[i] - for j in 1:6 # Electric DOFs on edges - j_Ο†_local = Ο†_local[j] - - # Approximate edge gradient (use nodal gradients) - node_idx = mod1(j, 4) - βˆ‡N_Ο† = βˆ‡N[node_idx] - - coupling_val = seebeck_peltier_coupling(S, βˆ‡N_T, βˆ‡N_Ο†, volume) - - # K_TΟ† and K_Ο†T blocks (Onsager reciprocity: Peltier = SeebeckΒ·T) - K_local[i_T_local, j_Ο†_local] += coupling_val - K_local[j_Ο†_local, i_T_local] += coupling_val - end - end - - - # ---------------------------------------------------------------- - # Coupling Assembly: Piezoelectric (u ↔ Ο†) - FULL 3RD ORDER TENSOR! - # ---------------------------------------------------------------- - # Full piezoelectric constitutive laws: - # D_k = Ρ·E_k + e_kijΒ·Ξ΅_ij (direct: strain β†’ polarization) - # Οƒ_ij = C_ijklΒ·Ξ΅_kl - e_kijΒ·E_k (converse: field β†’ stress) - # - # Weak form coupling: - # K_uΟ† = ∫ e_kij Β· (βˆ‚N_u^i/βˆ‚x_j) Β· (βˆ‚N_Ο†/βˆ‚x_k) dV - # K_Ο†u = ∫ e_kij Β· (βˆ‚N_Ο†/βˆ‚x_k) Β· (βˆ‚N_u^i/βˆ‚x_j) dV (Onsager reciprocal!) - # - # For real materials (quartz, PZT, PVDF), e_kij has specific symmetries - # Here: simplified diagonal-dominant tensor for demonstration - - # Create 3rd-order piezoelectric tensor e_kij using Tensor{3,3}! - # This is THE mathematically correct way - Tensors.jl handles all contractions! - e_piezo = Tensor{3,3}((k,i,j) -> k==i==j ? 1e-8 : 0.0) - - # Helper functions for proper tensor contractions - @inline function compute_strain_gradient_product(e::Tensor{3,3}, - βˆ‡N_u::Vec{3}, - βˆ‡N_Ο†::Vec{3}, - i_comp::Int, - vol::Float64) - # Contract: e_kij Β· (βˆ‚N_u^i/βˆ‚x_j) Β· (βˆ‚N_Ο†/βˆ‚x_k) - # This is the FULL piezoelectric coupling integral! - result = 0.0 - for k in 1:3, j in 1:3 - # e[k,i_comp,j] Β· (βˆ‚N_u/βˆ‚x_j) Β· (βˆ‚N_Ο†/βˆ‚x_k) - result += e[k,i_comp,j] * βˆ‡N_u[j] * βˆ‡N_Ο†[k] - end - return result * vol - end - - # Assembly: displacement-electric coupling (FULL tensor contraction!) - @inbounds for node_k in 1:4 # Displacement nodes - βˆ‡N_u = βˆ‡N[node_k] - - for i_comp in 1:3 # Displacement components (stress Οƒ_ij row i) - j_u_local = u_local[3*(node_k-1) + i_comp] - - for edge_j in 1:6 # Electric DOFs on edges - j_Ο†_local = Ο†_local[edge_j] - - # Approximate edge gradient using nodal values - node_idx = mod1(edge_j, 4) - βˆ‡N_Ο† = βˆ‡N[node_idx] - - # Full tensor contraction: e_kij Β· (βˆ‚u^i/βˆ‚x_j) Β· E_k - coupling_val = compute_strain_gradient_product( - e_piezo, βˆ‡N_u, βˆ‡N_Ο†, i_comp, volume - ) - - # Symmetric (reciprocal) coupling - Onsager reciprocity! - # Direct piezoelectric: D = e:Ξ΅ - # Converse piezoelectric: Οƒ = e^TΒ·E (transposed!) - K_local[j_u_local, j_Ο†_local] += coupling_val - K_local[j_Ο†_local, j_u_local] += coupling_val - end - end - end - - # ================================================================ - # πŸš€ SCATTER LOCAL TO GLOBAL (ONE OPERATION!) - # ================================================================ - - println("\n πŸ“€ Scattering coupled local matrix ($n_localΓ—$n_local) to global") - @inbounds for i in 1:n_local - I = dof_map[i] - F[I] += F_local[i] - for j in 1:n_local - J = dof_map[j] - K[I, J] += K_local[i, j] - end - end - end # End of element loop - - println("\nβœ“ Assembly complete!") - println(" ONE coupled system matrix: $(size(K))") - println(" Total non-zeros: $(nnz(K))") - - println("\n" * "="^70) - println("APPLYING BOUNDARY CONDITIONS AND SOLVING") - println("="^70) - - # Apply BCs - properly constrain ALL fields to avoid singularity! - # - # Physical interpretation: - # - Node 1: Fully grounded (T=0, u=0, reference for all fields) - # - Node 2: Prevent rigid motion in x (ux=0) - # - Electric: Ground edge 1 to prevent floating potential (Ο†_edge1=0) - - bc_dofs = [ - 1, # T at node 1 (thermal ground) - n_T_total+1, n_T_total+2, n_T_total+3, # u at node 1 (mechanical ground) - n_T_total+4, # ux at node 2 (prevent x-rotation) - n_T_total+n_u_total+n_p_total+1 # Ο† at edge 1 (electric ground) - ] - - for dof in bc_dofs - K[dof, :] .= 0.0 - K[:, dof] .= 0.0 - K[dof, dof] = 1.0 - F[dof] = 0.0 - end - - println("\nBoundary conditions (FULL MULTI-PHYSICS):") - println(" Thermal: Node 1 fixed at T=0 K (thermal ground)") - println(" Mechanical: Node 1 fully fixed u=(0,0,0) (mechanical ground)") - println(" Mechanical: Node 2 ux=0 (prevent rigid rotation)") - println(" Electric: Edge 1 fixed at Ο†=0 V (electric ground)") - println(" Hydraulic: Natural BCs (traction-free, no flow prescribed)") - - # Solve - println("\n🎯 Solving coupled system...") - println(" Matrix size: $(size(K))") - println(" Non-zeros: $(nnz(K))") - println(" Condition number estimate: checking...") - - # Add small regularization to prevent singularity from weakly coupled terms - # This is physically reasonable - represents small stabilization - Ξ΅_reg = 1e-12 - for i in 1:n_total - K[i,i] += Ξ΅_reg - end - - println(" Added regularization (Ξ΅=$Ξ΅_reg) for numerical stability") - - # Solve using robust method - sol = try - result = K \ F - println(" βœ“ Solution converged!") - result - catch e - println(" ERROR: System still singular!") - println(" This indicates physical model needs more constraints") - rethrow(e) - end - - # Extract fields - T_sol = sol[1:n_T_total] - u_sol = sol[n_T_total+1:n_T_total+n_u_total] - p_sol = sol[n_T_total+n_u_total+1:n_T_total+n_u_total+n_p_total] - Ο†_sol = sol[n_T_total+n_u_total+n_p_total+1:end] - - println("\n" * "="^70) - println("✨ SOLUTION (REAL PHYSICS!)") - println("="^70) - - println("\n🌑️ Temperature field:") - for i in 1:length(T_sol) - println(" Node $i: T = $(@sprintf("%.6f", T_sol[i])) K") - end - - println("\nπŸ—οΈ Displacement field:") - n_disp_nodes = div(length(u_sol), 3) - for node_id in 1:n_disp_nodes - ux = u_sol[3*(node_id-1)+1] - uy = u_sol[3*(node_id-1)+2] - uz = u_sol[3*(node_id-1)+3] - println(" Node $node_id: u = ($(@sprintf("%.6f", ux)), $(@sprintf("%.6f", uy)), $(@sprintf("%.6f", uz))) m") - end - - println("\nπŸ’§ Pore pressure field:") - for i in 1:length(p_sol) - println(" Cell $i: p = $(@sprintf("%.6f", p_sol[i])) Pa") - end - - println("\n⚑ Electric potential (edges):") - for i in 1:length(Ο†_sol) - println(" Edge $i: Ο† = $(@sprintf("%.6f", Ο†_sol[i])) V") - end - - # Verification tests - @test all(isfinite.(T_sol)) - @test all(isfinite.(u_sol)) - @test all(isfinite.(p_sol)) - @test all(isfinite.(Ο†_sol)) - - @test T_sol[1] β‰ˆ 0.0 atol=1e-10 # BC - @test u_sol[1:3] β‰ˆ [0.0, 0.0, 0.0] atol=1e-10 # BC - - # Check non-trivial solution - @test maximum(abs.(T_sol[2:end])) > 1e-6 - @test maximum(abs.(u_sol[4:end])) > 1e-6 - @test maximum(abs.(p_sol)) > 1e-6 - - println("\n" * "="^70) - println("πŸŽ‰ ACHIEVEMENTS UNLOCKED:") - println("="^70) - println(" βœ… ONE element type with FOUR physics fields!") - println(" βœ… ONE local coupled matrix per element (23Γ—23)") - println(" βœ… ALL physics assembled together (true coupling!)") - println(" βœ… Thermo-mechanical coupling: K_Tu, K_uT (thermal expansion)") - println(" βœ… Hydro-mechanical coupling: K_up, K_pu (Biot poroelasticity)") - println(" βœ… Thermal-hydraulic coupling: K_Tp, K_pT (thermal pressurization)") - println(" βœ… Electro-osmotic coupling: K_Ο†p, K_pΟ† (electrokinetic flow)") - println(" βœ… Thermo-electric coupling: K_TΟ†, K_Ο†T (Seebeck/Peltier)") - println(" βœ… Piezoelectric coupling: K_uΟ†, K_Ο†u (Tensor{3,3} elegance!)") - println(" βœ… Total: 12 off-diagonal coupling blocks! (ALL physics coupled!)") - println(" βœ… Modular coupling functions (inlined for zero overhead)") - println(" βœ… 3rd-order tensor formulation (e_kij via Tensor{3,3})") - println(" βœ… Onsager reciprocity respected (all couplings symmetric)") - println(" βœ… Local-to-global mapping via type system") - println(" βœ… REAL thermal diffusion (βˆ«ΞΊβˆ‡TΒ·βˆ‡T' dV)") - println(" βœ… REAL 3D elasticity (∫C:Ξ΅:Ξ΅ dV)") - println(" βœ… Zero-allocation Tensors.jl operations") - println(" βœ… get_basis_derivatives API (no manual gradients)") - println(" βœ… Cell-local pressure DOFs (discontinuous)") - println(" βœ… Edge-based electric DOFs") - println(" βœ… Full $n_total Γ— $n_total coupled system solved") - println(" βœ… Type-safe field access: .T, .u, .p, .Ο†") - println(" βœ… field_dof_range() - extract field blocks (compile-time!)") - println(" βœ… local_to_global_map() - scatter operation") - println("="^70) - - println("\nπŸ’‘ THIS IS THE POWER OF MULTI-FIELD ELEMENTS!") - println(" ONE element β†’ ONE local matrix β†’ ALL physics coupled!") - println(" T ↔ u (thermal expansion), T ↔ p (thermal pressurization)") - println(" T ↔ Ο† (Seebeck/Peltier), u ↔ p (Biot poroelasticity)") - println(" u ↔ Ο† (piezoelectric via Tensor{3,3}!), p ↔ Ο† (electro-osmotic)") - println(" β†’ Complete multi-physics: 4 fields Γ— 6 couplings = 12 blocks!") - println(" β†’ Tensors.jl elegance: 3rd-order piezoelectric tensor!") - println(" β†’ Modular design: coupling functions inlined for performance!") - println(" β†’ Geothermal, nuclear waste, CO2 sequestration, smart materials!") - println(" Natural coupling, type-safe, composable, ELEGANT! πŸš€") - -end