Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Documentation

on:
push:
branches:
- main
tags:
- "v*"
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: "pages"
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.x"
cache: "pip"
cache-dependency-path: "pyproject.toml"

- name: Install docs dependencies
run: pip install ".[docs]"

- name: Generate API stubs
run: python scripts/gen_api_docs.py

- name: Build docs
run: zensical build --clean

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./site

deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
2 changes: 1 addition & 1 deletion .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
python-version: ["3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
python-version: ["3.11", "3.12", "3.13"]
platform: [ubuntu-latest, macos-latest]

steps:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,10 @@ ENV/
# Rope project settings
.ropeproject

# mkdocs documentation
# mkdocs/zensical documentation
/site
/docs/generated/
/docs/api/
/docs/examples/60S_*.mrc
/docs/examples/xenon_*.mrc
/docs/examples/xenon_*.csv
Expand Down
14 changes: 13 additions & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,16 @@ generated-members=numpy.*,torch.* # See https://github.com/pytorch/pytorch/issu
# NOTE: We've adopted a more data/functional programming style, so some data related
# data is not packed/unpacked as objects and rather passed as data arrays/options.
max-args=10
max-positional-arguments=10
max-positional-arguments=10

[DESIGN]
# Result/manager Pydantic models and the batched inspection/correlation backends
# legitimately carry more fields and locals than pylint's defaults allow; raise the
# ceilings rather than sprinkling per-class/per-function disables.
max-attributes=12
max-locals=20
max-statements=60

[MESSAGES CONTROL]
# TODOs are tracked intentionally in-code; don't fail CI on them.
disable=fixme
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,18 @@ Further information on development and contributing to the repo can be found in

If you use Leopard-EM in your research, please include the following citations in your biblography:

- [Leopard-EM pre-print](https://doi.org/10.1101/2025.08.26.672452)
- [Leopard-EM Manuscript](https://doi.org/10.1107/S2059798325009982)
> ```
> @article {Giammar2025.08.26.672452,
> author = {Giammar, Matthew David and Dickerson, Joshua Luke and Hall, Laina Nicole and Lucas, Bronwyn Ayla},
> title = {Leopard-EM: An extensible 2DTM package to accelerate in situ structural biology},
> elocation-id = {2025.08.26.672452},
> year = {2025},
> doi = {10.1101/2025.08.26.672452},
> publisher = {Cold Spring Harbor Laboratory},
> URL = {https://www.biorxiv.org/content/early/2025/08/29/2025.08.26.672452},
> eprint = {https://www.biorxiv.org/content/early/2025/08/29/2025.08.26.672452.full.pdf},
> journal = {bioRxiv}
> @article{Giammar:rr5256,
> author = "Giammar, Matthew D. and Dickerson, Joshua L. and Hall, Laina N. and Lucas, Bronwyn A.",
> title = "{{\it Leopard-EM}: an extensible 2D template-matching package to accelerate {\it in situ} structural biology}",
> journal = "Acta Crystallographica Section D",
> year = "2026",
> volume = "82",
> number = "1",
> pages = "30--42",
> month = "Jan",
> doi = {10.1107/S2059798325009982},
> url = {https://doi.org/10.1107/S2059798325009982},
> }
> ```

41 changes: 34 additions & 7 deletions benchmark/benchmark_match_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,17 @@
ZENODO_URL = "https://zenodo.org/records/17069838"


def download_comparison_data() -> None:
"""Downloads the example data from Zenodo."""
def download_comparison_data(force_download: bool = False) -> None:
"""Downloads the example data from Zenodo, skipping if already present.

Parameters
----------
force_download : bool
If True, re-download the data even if it appears to already be present.
"""
if YAML_PATH.exists() and not force_download:
return

subprocess.run(
["zenodo_get", f"--output-dir={DOWNLOAD_DIR}", ZENODO_URL], check=True
)
Expand Down Expand Up @@ -76,6 +85,7 @@ def benchmark_match_template_single_run(
**core_kwargs,
orientation_batch_size=orientation_batch_size,
num_cuda_streams=mt_manager.computational_config.num_cpus,
backend=mt_manager.computational_config.backend,
)
total_projections = result["total_projections"] # number of CCGs calculated, N

Expand All @@ -102,13 +112,14 @@ def benchmark_match_template_single_run(
# --> r = (N - n) / (T_N - T_n)
# --> k = N * (T_N - T_n) / (N - n)

core_kwargs["euler_angles"] = torch.rand(size=(100, 3)) * 180
core_kwargs["euler_angles"] = torch.rand(size=(300, 3)) * orientation_batch_size
start_time = time.perf_counter()

result = core_match_template(
**core_kwargs,
orientation_batch_size=orientation_batch_size,
num_cuda_streams=mt_manager.computational_config.num_cpus,
backend=mt_manager.computational_config.backend,
)
adjustment_projections = result["total_projections"] # number of CCGs calculated, n

Expand All @@ -133,11 +144,13 @@ def benchmark_match_template_single_run(
}


def run_benchmark(orientation_batch_size: int, num_runs: int) -> dict[str, Any]:
def run_benchmark(
orientation_batch_size: int, num_runs: int, force_download: bool = False
) -> dict[str, Any]:
"""Run multiple benchmark iterations and collect statistics."""
# Download example data to use for benchmarking
print("Downloading benchmarking data...")
download_comparison_data()
download_comparison_data(force_download=force_download)
print("Done!")

# Get CUDA device properties
Expand Down Expand Up @@ -228,7 +241,19 @@ def save_benchmark_results(result: dict, output_file: str) -> None:
type=str,
help="Output file for benchmark results (default: benchmark_results.json)",
)
def main(orientation_batch_size: int, num_runs: int, output_file: str):
@click.option(
"--force-download",
is_flag=True,
default=False,
help="Re-download benchmarking data even if it already appears to be present "
"(e.g. to restore files that were manually modified for local testing).",
)
def main(
orientation_batch_size: int,
num_runs: int,
output_file: str,
force_download: bool,
):
"""Main benchmarking function with Click CLI interface."""
if not torch.cuda.is_available():
print("CUDA not available exiting...")
Expand All @@ -239,7 +264,9 @@ def main(orientation_batch_size: int, num_runs: int, output_file: str):
print(f" Number of runs: {num_runs}")
print(f" Output file: {output_file}")

result = run_benchmark(orientation_batch_size, num_runs)
result = run_benchmark(
orientation_batch_size, num_runs, force_download=force_download
)
# pprint(result)
save_benchmark_results(result, output_file)

Expand Down
Loading