Skip to content

Commit b658537

Browse files
Merge pull request #336 from robbievanleeuwen/benchmark
Add benchmark tests
2 parents a900cbc + 7968d94 commit b658537

12 files changed

+395
-2
lines changed

.flake8

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ per-file-ignores = tests/*:S101,__init__.py:F401,post.py:C901
77
rst-roles = class,const,func,meth,mod,ref
88
rst-directives = deprecated
99
pytest-fixture-no-parentheses = True
10+
pytest-mark-no-parentheses = True

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,6 @@ __pycache__/
4141

4242
# Jupyter Notebook
4343
.ipynb_checkpoints
44+
45+
# benchmark results
46+
.benchmarks

CONTRIBUTING.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,22 @@ nox --session=tests
121121
Unit tests are located in the _tests_ directory, and are written using the [pytest]
122122
testing framework.
123123

124+
### Benchmarks
125+
126+
If the code you are modifying may affect the performance of `sectionproperties`, it is
127+
recommended that you run the benchmarking tests to verify the performance before and
128+
after your changes. There are three different benchmarking suites: `geometry`, `meshing`
129+
and `analysis`. These can be run like this:
130+
131+
```shell
132+
poetry run pytest -m benchmark_geom
133+
poetry run pytest -m benchmark_mesh
134+
poetry run pytest -m benchmark_analysis
135+
```
136+
137+
Note that a plot of the results can be generated by adding the `--benchmark-histogram`
138+
option to the above commands.
139+
124140
[pytest]: https://pytest.readthedocs.io/
125141

126142
## How to submit changes

docs/contributing.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,23 @@ this:
129129
Unit tests are located in the *tests* directory, and are written using
130130
the `pytest <https://pytest.readthedocs.io/>`__ testing framework.
131131

132+
Benchmarks
133+
^^^^^^^^^^
134+
135+
If the code you are modifying may affect the performance of ``sectionproperties``, it is
136+
recommended that you run the benchmarking tests to verify the performance before and
137+
after your changes. There are three different benchmarking suites: ``geometry``,
138+
``meshing`` and ``analysis``. These can be run like this:
139+
140+
.. code:: shell
141+
142+
poetry run pytest -m benchmark_geom
143+
poetry run pytest -m benchmark_mesh
144+
poetry run pytest -m benchmark_analysis
145+
146+
Note that a plot of the results can be generated by adding the ``--benchmark-histogram``
147+
option to the above commands.
148+
132149
How to submit changes
133150
---------------------
134151

noxfile.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,16 @@ def tests(session: Session) -> None:
154154
session.install("coverage[toml]", "pytest", "pygments", "pytest-check")
155155

156156
try:
157-
session.run("coverage", "run", "--parallel", "-m", "pytest", *session.posargs)
157+
session.run(
158+
"coverage",
159+
"run",
160+
"--parallel",
161+
"-m",
162+
"pytest",
163+
"-m",
164+
"not benchmark",
165+
*session.posargs,
166+
)
158167
finally:
159168
if session.interactive:
160169
session.notify("coverage", posargs=[])

poetry.lock

Lines changed: 62 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ pre-commit = "^3.4.0"
8282
pre-commit-hooks = "^4.5.0"
8383
Pygments = "^2.16.1"
8484
pytest = "^7.4.2"
85+
pytest-benchmark = { extras = ["histogram"], version = "^4.0.0" }
8586
pytest-check = "^2.2.2"
8687
pyupgrade = "^3.15.0"
8788
sphinx = "^7.2.6"
@@ -112,6 +113,13 @@ show_missing = true
112113
profile = "black"
113114
lines_after_imports = 2
114115

116+
[tool.pytest.ini_options]
117+
markers = [
118+
"benchmark_geom: geometry benchmark tests (select with '-m benchmark_geom')",
119+
"benchmark_mesh: mesh benchmark tests (select with '-m benchmark_mesh')",
120+
"benchmark_analysis: analysis benchmark tests (select with '-m benchmark_analysis')",
121+
]
122+
115123
[build-system]
116124
requires = ["poetry-core>=1.0.0"]
117125
build-backend = "poetry.core.masonry.api"

tests/benchmarks/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Benchmark tests for sectionproperties."""

tests/benchmarks/conftest.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
"""pytest benchmark configurations."""
2+
3+
from __future__ import annotations
4+
5+
from typing import Callable
6+
7+
import pytest
8+
9+
from sectionproperties.pre import Geometry, Material
10+
from sectionproperties.pre.library import (
11+
circular_hollow_section,
12+
circular_section,
13+
concrete_column_section,
14+
rectangular_section,
15+
)
16+
17+
18+
@pytest.fixture
19+
def concrete() -> Material:
20+
"""Creates a concrete material object.
21+
22+
Returns:
23+
Concrete
24+
"""
25+
return Material(
26+
name="Concrete",
27+
elastic_modulus=30.1e3,
28+
poissons_ratio=0.2,
29+
yield_strength=32,
30+
density=2.4e-6,
31+
color="lightgrey",
32+
)
33+
34+
35+
@pytest.fixture
36+
def steel() -> Material:
37+
"""Creates a steel material object.
38+
39+
Returns:
40+
Steel
41+
"""
42+
return Material(
43+
name="Steel",
44+
elastic_modulus=200e3,
45+
poissons_ratio=0.3,
46+
yield_strength=500,
47+
density=7.85e-6,
48+
color="grey",
49+
)
50+
51+
52+
@pytest.fixture
53+
def rect_geom() -> Geometry:
54+
"""Creates a rectangular geometry.
55+
56+
Returns:
57+
Geometry
58+
"""
59+
return rectangular_section(d=100, b=50)
60+
61+
62+
@pytest.fixture
63+
def chs_geom() -> Geometry:
64+
"""Creates a rectangular geometry.
65+
66+
Returns:
67+
Geometry
68+
"""
69+
return circular_hollow_section(d=100, t=3, n=128)
70+
71+
72+
@pytest.fixture
73+
def concrete_column_with_hole(concrete, steel) -> Callable:
74+
"""Creates a concrete column with a hole at its centre.
75+
76+
Args:
77+
concrete: Concrete material
78+
steel: Steel material
79+
80+
Returns:
81+
Generator function
82+
"""
83+
84+
def _generate_geom() -> Geometry:
85+
geom = concrete_column_section(
86+
d=600,
87+
b=300,
88+
dia_bar=25,
89+
area_bar=500,
90+
n_x=3,
91+
n_y=6,
92+
cover=35,
93+
n_circle=4,
94+
filled=False,
95+
conc_mat=concrete,
96+
steel_mat=steel,
97+
)
98+
hole = circular_section(d=100, n=32).shift_section(x_offset=150, y_offset=300)
99+
100+
return geom - hole
101+
102+
return _generate_geom
103+
104+
105+
@pytest.fixture
106+
def analysis_geometry() -> Callable:
107+
"""Create a geometry to be used for analysis.
108+
109+
Returns:
110+
Generator function
111+
"""
112+
113+
def _generate_geom(num_elements: int) -> Geometry:
114+
mat_a = Material("a", 1, 0, 1, 1, color="b")
115+
mat_b = Material("b", 10, 0, 1, 1, color="g")
116+
mat_c = Material("c", 5, 0, 1, 1, color="r")
117+
mat_d = Material("d", 2, 0, 1, 1, color="y")
118+
119+
a = rectangular_section(20, 20, mat_a)
120+
b = rectangular_section(20, 20, mat_b).align_to(a, "right")
121+
c = rectangular_section(20, 20, mat_c).align_to(a, "top")
122+
d = rectangular_section(20, 20, mat_d).align_to(a, "top").align_to(a, "right")
123+
geom = a + b + c + d
124+
mesh_area = geom.calculate_area() / num_elements * 1.6
125+
return geom.create_mesh([mesh_area])
126+
127+
return _generate_geom

0 commit comments

Comments
 (0)