dlynch90 commited on
Commit
650702c
·
verified ·
1 Parent(s): 8cfab19

Create examples/poisson_2d_vendor.py

Browse files

Add working 2D Poisson solver using 100% vendor libraries (skfem, scipy, matplotlib) - empirical test of FEM implementation

Files changed (1) hide show
  1. examples/poisson_2d_vendor.py +109 -0
examples/poisson_2d_vendor.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """2D Poisson Equation Solver - 100% VENDOR IMPLEMENTATION
2
+
3
+ Solves: -∆u = f in Ω, u = 0 on ∂Ω
4
+
5
+ Using ONLY vendor libraries:
6
+ - skfem: FE assembly
7
+ - scipy.sparse: Linear algebra
8
+ - matplotlib: Visualization
9
+
10
+ ZERO custom FEM code.
11
+ """
12
+ import numpy as np
13
+ from scipy.sparse.linalg import spsolve
14
+ import matplotlib.pyplot as plt
15
+
16
+ # VENDOR: scikit-fem for ALL FEM operations
17
+ import skfem
18
+ from skfem.helpers import dot, grad
19
+
20
+
21
+ def solve_poisson_vendor():
22
+ """Solve 2D Poisson using pure vendor APIs."""
23
+
24
+ print("=" * 60)
25
+ print("2D Poisson Solver - VENDOR-ONLY Implementation")
26
+ print("="*60)
27
+
28
+ # STEP 1: Create mesh (skfem vendor)
29
+ print("\n[1/5] Generating mesh with skfem.MeshTri...")
30
+ mesh = skfem.MeshTri()
31
+ mesh = mesh.refined(4) # Refine 4 times
32
+ print(f" Nodes: {mesh.p.shape[1]}")
33
+ print(f" Elements: {mesh.t.shape[1]}")
34
+
35
+ # STEP 2: Define weak form (skfem vendor decorators)
36
+ print("\n[2/5] Defining weak forms with @skfem.BilinearForm...")
37
+
38
+ @skfem.BilinearForm
39
+ def laplacian(u, v, _):
40
+ """Weak form of Laplacian using skfem.helpers."""
41
+ return dot(grad(u), grad(v))
42
+
43
+ @skfem.LinearForm
44
+ def load(v, w):
45
+ """Right-hand side f=1."""
46
+ return 1.0 * v
47
+
48
+ # STEP 3: Assemble system (skfem vendor)
49
+ print("\n[3/5] Assembling system with skfem.Basis...")
50
+ basis = skfem.Basis(mesh, skfem.ElementTriP1())
51
+
52
+ K = laplacian.assemble(basis) # Returns scipy.sparse.csr_matrix
53
+ F = load.assemble(basis) # Returns numpy array
54
+
55
+ print(f" Stiffness matrix K: {K.shape}, nnz={K.nnz}")
56
+ print(f" Load vector F: {F.shape}")
57
+
58
+ # STEP 4: Apply BCs and solve (skfem + scipy vendors)
59
+ print("\n[4/5] Applying BCs and solving with scipy.sparse.linalg...")
60
+
61
+ # Get boundary DOFs (skfem vendor)
62
+ dofs = basis.get_dofs() # All boundary nodes
63
+
64
+ # Solve with BCs (skfem.condense + scipy.spsolve vendors)
65
+ u = skfem.solve(*skfem.condense(K, F, D=dofs))
66
+
67
+ print(f" Solution u: min={u.min():.6f}, max={u.max():.6f}")
68
+
69
+ # STEP 5: Visualize (matplotlib + skfem.visuals vendors)
70
+ print("\n[5/5] Visualizing with skfem.visuals.matplotlib...")
71
+
72
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
73
+
74
+ # Plot solution using skfem vendor visualization
75
+ skfem.visuals.matplotlib.plot(
76
+ basis, u, ax=ax1, shading='gouraud', colorbar=True
77
+ )
78
+ ax1.set_title('Solution u')
79
+ ax1.set_xlabel('x')
80
+ ax1.set_ylabel('y')
81
+
82
+ # Plot mesh using skfem vendor
83
+ skfem.visuals.matplotlib.draw(mesh, ax=ax2)
84
+ ax2.set_title(f'Mesh ({mesh.t.shape[1]} elements)')
85
+ ax2.set_xlabel('x')
86
+ ax2.set_ylabel('y')
87
+
88
+ plt.tight_layout()
89
+ plt.savefig('poisson_solution_vendor.png', dpi=150)
90
+ print(" Saved: poisson_solution_vendor.png")
91
+
92
+ print("\n" + "="*60)
93
+ print("SUCCESS: Solved using 100% vendor libraries")
94
+ print(" - skfem: Mesh, basis, assembly, BCs")
95
+ print(" - scipy.sparse: Linear solver")
96
+ print(" - matplotlib: Visualization")
97
+ print("="*60)
98
+
99
+ return mesh, u, basis
100
+
101
+
102
+ if __name__ == "__main__":
103
+ mesh, u, basis = solve_poisson_vendor()
104
+
105
+ # Additional vendor-based analysis
106
+ print("\nVendor-based Analysis:")
107
+ print(f" L2 norm of solution: {np.linalg.norm(u):.6f}")
108
+ print(f" Mesh quality (min angle): {mesh.quality().min():.3f}")
109
+ print(f" Basis dimension: {basis.N}")