Skip to content

Commit

Permalink
Merge pull request #336 from robbievanleeuwen/benchmark
Browse files Browse the repository at this point in the history
Add benchmark tests
  • Loading branch information
robbievanleeuwen committed Oct 10, 2023
2 parents a900cbc + 7968d94 commit b658537
Show file tree
Hide file tree
Showing 12 changed files with 395 additions and 2 deletions.
1 change: 1 addition & 0 deletions .flake8
Expand Up @@ -7,3 +7,4 @@ per-file-ignores = tests/*:S101,__init__.py:F401,post.py:C901
rst-roles = class,const,func,meth,mod,ref
rst-directives = deprecated
pytest-fixture-no-parentheses = True
pytest-mark-no-parentheses = True
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -41,3 +41,6 @@ __pycache__/

# Jupyter Notebook
.ipynb_checkpoints

# benchmark results
.benchmarks
16 changes: 16 additions & 0 deletions CONTRIBUTING.md
Expand Up @@ -121,6 +121,22 @@ nox --session=tests
Unit tests are located in the _tests_ directory, and are written using the [pytest]
testing framework.

### Benchmarks

If the code you are modifying may affect the performance of `sectionproperties`, it is
recommended that you run the benchmarking tests to verify the performance before and
after your changes. There are three different benchmarking suites: `geometry`, `meshing`
and `analysis`. These can be run like this:

```shell
poetry run pytest -m benchmark_geom
poetry run pytest -m benchmark_mesh
poetry run pytest -m benchmark_analysis
```

Note that a plot of the results can be generated by adding the `--benchmark-histogram`
option to the above commands.

[pytest]: https://pytest.readthedocs.io/

## How to submit changes
Expand Down
17 changes: 17 additions & 0 deletions docs/contributing.rst
Expand Up @@ -129,6 +129,23 @@ this:
Unit tests are located in the *tests* directory, and are written using
the `pytest <https://pytest.readthedocs.io/>`__ testing framework.

Benchmarks
^^^^^^^^^^

If the code you are modifying may affect the performance of ``sectionproperties``, it is
recommended that you run the benchmarking tests to verify the performance before and
after your changes. There are three different benchmarking suites: ``geometry``,
``meshing`` and ``analysis``. These can be run like this:

.. code:: shell
poetry run pytest -m benchmark_geom
poetry run pytest -m benchmark_mesh
poetry run pytest -m benchmark_analysis
Note that a plot of the results can be generated by adding the ``--benchmark-histogram``
option to the above commands.

How to submit changes
---------------------

Expand Down
11 changes: 10 additions & 1 deletion noxfile.py
Expand Up @@ -154,7 +154,16 @@ def tests(session: Session) -> None:
session.install("coverage[toml]", "pytest", "pygments", "pytest-check")

try:
session.run("coverage", "run", "--parallel", "-m", "pytest", *session.posargs)
session.run(
"coverage",
"run",
"--parallel",
"-m",
"pytest",
"-m",
"not benchmark",
*session.posargs,
)
finally:
if session.interactive:
session.notify("coverage", posargs=[])
Expand Down
63 changes: 62 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions pyproject.toml
Expand Up @@ -82,6 +82,7 @@ pre-commit = "^3.4.0"
pre-commit-hooks = "^4.5.0"
Pygments = "^2.16.1"
pytest = "^7.4.2"
pytest-benchmark = { extras = ["histogram"], version = "^4.0.0" }
pytest-check = "^2.2.2"
pyupgrade = "^3.15.0"
sphinx = "^7.2.6"
Expand Down Expand Up @@ -112,6 +113,13 @@ show_missing = true
profile = "black"
lines_after_imports = 2

[tool.pytest.ini_options]
markers = [
"benchmark_geom: geometry benchmark tests (select with '-m benchmark_geom')",
"benchmark_mesh: mesh benchmark tests (select with '-m benchmark_mesh')",
"benchmark_analysis: analysis benchmark tests (select with '-m benchmark_analysis')",
]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
1 change: 1 addition & 0 deletions tests/benchmarks/__init__.py
@@ -0,0 +1 @@
"""Benchmark tests for sectionproperties."""
127 changes: 127 additions & 0 deletions tests/benchmarks/conftest.py
@@ -0,0 +1,127 @@
"""pytest benchmark configurations."""

from __future__ import annotations

from typing import Callable

import pytest

from sectionproperties.pre import Geometry, Material
from sectionproperties.pre.library import (
circular_hollow_section,
circular_section,
concrete_column_section,
rectangular_section,
)


@pytest.fixture
def concrete() -> Material:
"""Creates a concrete material object.
Returns:
Concrete
"""
return Material(
name="Concrete",
elastic_modulus=30.1e3,
poissons_ratio=0.2,
yield_strength=32,
density=2.4e-6,
color="lightgrey",
)


@pytest.fixture
def steel() -> Material:
"""Creates a steel material object.
Returns:
Steel
"""
return Material(
name="Steel",
elastic_modulus=200e3,
poissons_ratio=0.3,
yield_strength=500,
density=7.85e-6,
color="grey",
)


@pytest.fixture
def rect_geom() -> Geometry:
"""Creates a rectangular geometry.
Returns:
Geometry
"""
return rectangular_section(d=100, b=50)


@pytest.fixture
def chs_geom() -> Geometry:
"""Creates a rectangular geometry.
Returns:
Geometry
"""
return circular_hollow_section(d=100, t=3, n=128)


@pytest.fixture
def concrete_column_with_hole(concrete, steel) -> Callable:
"""Creates a concrete column with a hole at its centre.
Args:
concrete: Concrete material
steel: Steel material
Returns:
Generator function
"""

def _generate_geom() -> Geometry:
geom = concrete_column_section(
d=600,
b=300,
dia_bar=25,
area_bar=500,
n_x=3,
n_y=6,
cover=35,
n_circle=4,
filled=False,
conc_mat=concrete,
steel_mat=steel,
)
hole = circular_section(d=100, n=32).shift_section(x_offset=150, y_offset=300)

return geom - hole

return _generate_geom


@pytest.fixture
def analysis_geometry() -> Callable:
"""Create a geometry to be used for analysis.
Returns:
Generator function
"""

def _generate_geom(num_elements: int) -> Geometry:
mat_a = Material("a", 1, 0, 1, 1, color="b")
mat_b = Material("b", 10, 0, 1, 1, color="g")
mat_c = Material("c", 5, 0, 1, 1, color="r")
mat_d = Material("d", 2, 0, 1, 1, color="y")

a = rectangular_section(20, 20, mat_a)
b = rectangular_section(20, 20, mat_b).align_to(a, "right")
c = rectangular_section(20, 20, mat_c).align_to(a, "top")
d = rectangular_section(20, 20, mat_d).align_to(a, "top").align_to(a, "right")
geom = a + b + c + d
mesh_area = geom.calculate_area() / num_elements * 1.6
return geom.create_mesh([mesh_area])

return _generate_geom

0 comments on commit b658537

Please sign in to comment.