Skip to content

Commit 328b642

Browse files
committedSep 23, 2024
add performance test with scikitfem
1 parent c608a0a commit 328b642

File tree

6 files changed

+139
-466
lines changed

6 files changed

+139
-466
lines changed
 

‎performance/demo.py

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
2+
"""A simple performance test adopted from sciket-fem.
3+
"""
4+
from timeit import timeit
5+
import numpy as np
6+
import skfem as skf
7+
from skfem.models.poisson import laplace, unit_load
8+
import pyamg
9+
10+
def pre(N=3):
11+
m = skf.MeshTet.init_tensor(*(3 * (np.linspace(0., 1., N),)))
12+
return m
13+
14+
def assembler(m):
15+
basis = skf.Basis(m, skf.ElementTetP1())
16+
return (
17+
laplace.assemble(basis),
18+
unit_load.assemble(basis),
19+
)
20+
21+
times = []
22+
fw = 10
23+
kmin = 6
24+
kmax = 20
25+
Nlist = [int(2 ** (k / 3)) for k in range(kmin, kmax)]
26+
# print(Nlist)
27+
28+
print('| N | DoFs | Assembly | Solve prep | Solve setup | Solve |')
29+
print('| ---- | ---- | ---- | ---- | ---- | ---- |')
30+
for N in Nlist:
31+
m = pre(N)
32+
33+
assemble_time = timeit(lambda: assembler(m), number=1)
34+
A, b = assembler(m)
35+
D = m.boundary_nodes()
36+
37+
condense_time = timeit(lambda: skf.condense(A, b, D=D), number=1)
38+
A, b, _, _ = skf.condense(A, b, D=D)
39+
40+
setup_time = timeit(lambda: pyamg.smoothed_aggregation_solver(A).aspreconditioner(), number=1)
41+
ml = pyamg.smoothed_aggregation_solver(A,
42+
max_coarse=10).aspreconditioner()
43+
44+
mlsolver = skf.solver_iter_pcg(verbose=False, M=ml, rtol=1e-8)
45+
solve_time = timeit(lambda: skf.solve(A, b, solver=mlsolver), number=1)
46+
47+
times.append([N, len(b), assemble_time, condense_time, setup_time, solve_time])
48+
print(f'| {N:10d} | {len(b):10d} | {assemble_time:>{fw}.5f} | {condense_time:{fw}.5f} | {setup_time:{fw}.5f} | {solve_time:{fw}.5f} |')
49+
50+
import matplotlib.pyplot as plt
51+
fig, ax = plt.subplots()
52+
n = [t[1] for t in times]
53+
ax.loglog(n, [t[2] for t in times], label='Assembly')
54+
ax.loglog(n, [t[3] for t in times], label='Solve prep')
55+
ax.loglog(n, [t[4] for t in times], label='Solve setup')
56+
ax.loglog(n, [t[5] for t in times], label='Solve')
57+
ax.set_xlabel('# DoFs')
58+
ax.set_ylabel('time (s)')
59+
ax.grid(True)
60+
plt.legend()
61+
62+
figname = f'./output/performance.png'
63+
import sys
64+
if '--savefig' in sys.argv:
65+
plt.savefig(figname, bbox_inches='tight', dpi=150)
66+
else:
67+
plt.show()

‎performance/output/performance.png

65 KB
Loading

‎performance/readme.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
This demo shows the performance of finite element assembly (by way of scikit-fem)
2+
and multigrid setup/solve (within PCG). The solver is run to a tolerance of `1e-8`.
3+
The figure shows:
4+
5+
- `DoFs`: the total number of degrees of freedom in the system
6+
- `Assembly`: the total time to assemble the FE matrix (scikit-fem)
7+
- `Solve prep`: the total time to condense the system to non-Dirichlet nodes (scikit-fem)
8+
- `Solve setup`: the total time for the AMG setup phase (pyamg)
9+
- `Solve`: the total time for the AMG solve phase (pyamg) withing PCG (scikit-fem)

‎readme.md

+32-461
Large diffs are not rendered by default.

‎requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ matplotlib
33
vedo
44
pyinstrument
55
pyyaml
6+
scikit-fem

‎runner.py

+30-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import subprocess
22
import yaml
33
import os
4+
import sys
45
from glob import glob
56

6-
def exectute_demo(exampledir, name='demo.py'):
7+
def execute_demo(exampledir, name='demo.py'):
78
"""Exectue a demo in a particular directory."""
89
nameall = name.split()
910
output = subprocess.run(['python3'] + nameall + ['--savefig'],
@@ -75,6 +76,8 @@ def exectute_demo(exampledir, name='demo.py'):
7576
Other:
7677
- dir: profile_pyamg
7778
title: Profiling Performance
79+
- dir: performance
80+
title: Scaling performance of AMG and FE assembly
7881
""")
7982

8083
# generate table and header
@@ -89,6 +92,20 @@ def exectute_demo(exampledir, name='demo.py'):
8992

9093
main = '\n'
9194

95+
dirs = None
96+
if len(sys.argv) > 1:
97+
dirs = sys.argv[1:]
98+
for d in dirs:
99+
found = False
100+
for section in toc:
101+
for demo in toc[section]:
102+
if d == demo['dir']:
103+
found = True
104+
if not found or not os.path.isdir(d):
105+
print('usage: runner.py')
106+
print('usage: runner.py dir1 [dir2] ...')
107+
exit()
108+
92109
for section in toc:
93110

94111
# add to the TOC
@@ -100,7 +117,7 @@ def exectute_demo(exampledir, name='demo.py'):
100117

101118
if toc[section] is not None:
102119
for demo in toc[section]:
103-
print(f'Processing {demo["dir"]}')
120+
print(f'Processing {demo["dir"]}.', end=' ')
104121
title = demo.get('title', None)
105122
if title:
106123
hrefid = title.replace(' ', '').lower()
@@ -117,10 +134,18 @@ def exectute_demo(exampledir, name='demo.py'):
117134
main += readmeoutput
118135

119136
# get the demo output
137+
runit = True
138+
if dirs is not None:
139+
if demo['dir'] not in dirs:
140+
runit = False
141+
print('')
142+
if runit:
143+
print('[--->rerunning]')
120144
for demoname in demonames:
121-
output = exectute_demo(demo['dir'], name=demoname)
122-
if len(output) > 0:
123-
main += '\n```\n' + output + '```\n'
145+
if runit:
146+
output = execute_demo(demo['dir'], name=demoname)
147+
if len(output) > 0:
148+
main += '\n```\n' + output + '```\n'
124149

125150
# get the output figs
126151
figs = glob(os.path.join(f'{demo["dir"]}', 'output') +'/*.png')

0 commit comments

Comments
 (0)
Please sign in to comment.