Skip to content
Merged
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
33 changes: 33 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: tests

on:
push:
branches:
- main
pull_request:

concurrency:
group: tests-${{ github.ref }}
cancel-in-progress: true

env:
UV_PYTHON: "3.12"

jobs:
lint:
name: lint and type-check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: uv.lock
- name: Install dependencies
# The train extra is needed for ty to resolve torch & co.; the cpu selector
# keeps that to the small CPU-only wheel, which setup-uv caches.
run: uv sync --locked --extra train --extra cpu
- name: Ruff
run: uv run ruff check .
- name: Ty
run: uv run ty check
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ ORCA runs a linear pipeline. Each stage receives a context dictionary and adds i

### Stage 1 — GDS generation (`GDSGenerator`)

The geometry class's `input_parameter_iterator` samples parameter combinations (randomly or on a grid). For each combination, `create_gds_file()` is called to produce a GDS layout file. The number of samples is set by `num_samples`.
The geometry class's `input_parameter_iterator` samples parameter combinations (randomly or on a grid). For each combination, `create_gds_file()` is called to produce a GDS layout file. The number of samples is set by `num_samples`; `seed` makes the `"random"` picking strategy reproducible.

### Stage 2 — GDS conversion (`GDSConverter`)

Expand Down
2 changes: 1 addition & 1 deletion docs/pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ flowchart TB

## Stage 1 — GDS generation (`GDSGenerator`)

The geometry class's `input_parameter_iterator` samples parameter combinations (randomly or on a grid). For each combination, `create_gds_file()` is called to produce a GDS layout file. The number of samples is set by `num_samples`.
The geometry class's `input_parameter_iterator` samples parameter combinations (randomly or on a grid). For each combination, `create_gds_file()` is called to produce a GDS layout file. The number of samples is set by `num_samples`; `seed` makes the `"random"` picking strategy reproducible.

## Stage 2 — GDS conversion (`GDSConverter`)

Expand Down
15 changes: 4 additions & 11 deletions examples/main.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import orca
import numpy as np

from orca.geometry.presets.tf_octa_c_ports import TransformerOcta

PLOT = False

### Example of using a custom geometry
# geometry = MyCustomGeometry( # Python class that inherits from BaseGeometry
# name = "my_geometry",
# stackup_xml = "/path/to/stackup.xml", # XML file defining the physical layer stackup
# simconfig_filename = "/path/to/simconfig.simcfg" # Simulation configuration file generated manually or with setupEM
# )
# To run the pipeline on your own geometry, subclass BaseGeometry (name, stackup_xml,
# simconfig_filename, input_parameter_iterator, create_gds_file) and pass an instance
# to orca_instance.run() below; see docs/custom_class.md for a complete example.

# hyperparameters = {'learning_rate': 0.0008166998266605425, 'batch_size': 128, 'epochs': 10, 'num_layers': 4, 'hidden_size': 512, 'activation_function': 'GELU'}
hyperparameters = {
"learning_rate": 0.0005,
"batch_size": 256,
Expand All @@ -24,7 +18,6 @@

def main():
# Use predefined geometry from examples
np.random.seed(40)
try:
import torch # optional: only installed with ORCA's "train" extra
torch.manual_seed(40)
Expand All @@ -34,7 +27,7 @@ def main():

orca_instance = orca.ORCA(
[
orca.GDSGenerator(num_samples=6000),
orca.GDSGenerator(num_samples=6000, seed=40),
orca.GDSConverter(),
#orca.PalaceSimulator(palace_executable="apptainer exec ~/Documents/git/palace/palace.sif palace"),
#orca.ModelTrainer(model=orca.OrcaMLP, hyperparameters=hyperparameters, n_train_samples=1000),
Expand Down
6 changes: 2 additions & 4 deletions examples/slurm_runs/main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import orca
import numpy as np

from orca.geometry.presets.tf_octa_c_ports import TransformerOcta


def main():
# Use predefined geometry from examples
np.random.seed(40)
try:
import torch # optional: only installed with ORCA's "train" extra
torch.manual_seed(40)
Expand All @@ -15,7 +13,7 @@ def main():

orca_instance = orca.ORCA(
[
orca.GDSGenerator(num_samples=6000),
orca.GDSGenerator(num_samples=6000, seed=40),
orca.GDSConverter(),
# launcher="slurm" runs the simulations as srun job steps on the nodes of this allocation
# (#SBATCH --nodes in the job script). bind="numa" with num_parallel_sims=0 runs one
Expand Down
119 changes: 112 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,13 @@ authors = [
]
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Console",
"Environment :: X11 Applications :: Qt",
"Intended Audience :: Developers",
"Operating System :: Unix",
"Intended Audience :: Science/Research",
"Intended Audience :: Education",
"Natural Language :: English",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
Expand Down Expand Up @@ -83,6 +88,13 @@ cpu = ["torch"]
cu126 = ["torch"]
cu130 = ["torch"]

[dependency-groups]
# Installed by `uv sync` (not by `pip install`): the linters CI runs, pinned by uv.lock.
dev = [
"ruff",
"ty",
]

[tool.uv]
conflicts = [
[{ extra = "cpu" }, { extra = "cu126" }],
Expand Down Expand Up @@ -134,10 +146,103 @@ orca = "src/orca"
[tool.setuptools.package-data]
"orca" = ["**/*.simcfg", "**/*.xml"]

[tool.black]
line-length = 79
target-version = ['py311', 'py312', 'py313']
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra --strict-markers --strict-config"
xfail_strict = true

[tool.coverage.run]
source = ["src/orca"]
branch = true

[tool.coverage.report]
show_missing = true
exclude_also = [
"if TYPE_CHECKING:",
"raise NotImplementedError",
"@(abc\\.)?abstractmethod",
]


[tool.ruff]
line-length = 100
# Resolve first-party imports out of the src layout.
src = ["src"]
# Scratch/vendored trees that are not part of the package.
extend-exclude = ["Ignore"]

[tool.ruff.lint]
select = [
"ALL", # include all the rules, including new ones
]
ignore = [
#### modules
"ANN", # flake8-annotations: full annotation coverage is not a project goal
"COM", # flake8-commas: trailing commas are the formatter's job
"C90", # mccabe complexity: PLR0912/PLR0915 cover this better
"CPY", # flake8-copyright: no per-file copyright headers in this project
"DJ", # django
"EM", # exception messages inline, consistent with TRY003 below
"EXE", # flake8-executable
"FBT", # boolean traps: unavoidable in Qt slots and config flags
"PD", # pandas-vet: false positives on numpy/scikit-rf `.values`
"T10", # debugger
"TID", # flake8-tidy-imports

#### specific rules
"D100", # ignore missing docs
"D101",
"D102",
"D103",
"D104",
"D105",
"D106",
"D107",
"D200",
"D203", # incompatible with D211; D211 is the convention here
"D205",
"D212", # summary placement is not enforced; both styles exist in the tree
"D400",
"D401",
"D415",
"E402", # false positives for local imports
"E501", # line too long
"N802", # RF symbols keep their conventional casing (S, Z, Y, N, K, ...)
"N803",
"N806",
"PLC0415", # imports are deliberately local to keep Palace/ORCA/GUI optional
"PLR0911", # simulation and GUI setup code is legitimately branch-heavy
"PLR0912",
"PLR0913",
"PLR0915",
"PLR0917",
"PLR2004", # magic-value comparison is noise in numeric/engineering code
"PTH", # os.path is used throughout; migrating is a separate refactor
"RUF001", # ambiguous unicode: units and symbols (µ, Ω, °) are intentional
"RUF002",
"RUF003",
"S603", # the Xyce subprocess call is the point of the simulator
"TRY003", # external messages in exceptions are too verbose
"TD002",
"TD003",
"FIX002", # too verbose descriptions of todos
]

[tool.ruff.lint.per-file-ignores]
# Examples are standalone scripts, not an importable package. Optional pipeline
# stages are left in as commented-out lines to be enabled by the reader.
"examples/*" = ["INP001", "T201", "ERA001"]
# Tests assert, reach into private helpers deliberately, and use fixtures that
# look like shadowed or unused arguments to the linter.
"tests/*" = [
"S101", # assert is how pytest works
"ARG001",
"ARG002",
"PLR6301",
"SLF001", # pinning private behaviour (_tell, _parse_target) is the point
"TC003",
"T201", # printing is how a test script reports
]

[tool.isort]
profile = "black"
line_length = 79
[tool.ruff.lint.pydocstyle]
convention = "google"
27 changes: 22 additions & 5 deletions src/orca/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
# Import stuff here so that they are available at the package level (i.e. from orca import ORCA, BaseGeometry, InputParameters)

from .geometry.base_geometry import BaseGeometry
from .geometry.input_parameters import InputParameterIterator
from .orca import ORCA
from .pipeline.context import PipelineContext
from .pipeline.pipeline_stage import PipelineStage
from .pipeline.gds_gen_stage import GDSGenerator
from .pipeline.gds_conversion_stage import GDSConverter
from .pipeline.gds_gen_stage import GDSGenerator
from .pipeline.pipeline_stage import PipelineStage
from .pipeline.simulation_stage import PalaceSimulator
from .geometry.base_geometry import BaseGeometry
from .geometry.input_parameters import InputParameterIterator
from .training.codecs import FlatReImCodec, OutputCodec, UpperTriangleReImCodec
from .training.guarantees import PhysicsGuarantees
from .training.codecs import OutputCodec, FlatReImCodec, UpperTriangleReImCodec
from .training.spec import FrequencyMode, IOSpec

__all__ = [
"ORCA",
"BaseGeometry",
"FlatReImCodec",
"FrequencyMode",
"GDSConverter",
"GDSGenerator",
"IOSpec",
"InputParameterIterator",
"OutputCodec",
"PalaceSimulator",
"PhysicsGuarantees",
"PipelineContext",
"PipelineStage",
"UpperTriangleReImCodec",
]

# Everything that needs PyTorch (the "train" extra) is imported on first access
# instead of here, so that `import orca` works in a simulation-only install, e.g.
# on an HPC cluster. The names stay importable as `orca.ModelTrainer` etc.; a
Expand Down
4 changes: 3 additions & 1 deletion src/orca/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import sys

from orca.gui.app import run_gui


def main():
"""
Main entry point for ORCA.
Expand All @@ -10,7 +12,7 @@ def main():
if len(sys.argv) == 1 or (len(sys.argv) > 1 and sys.argv[1] == "--gui"):
run_gui()
else:
print("CLI usage not yet implemented. Use --gui to launch the graphical interface.")
sys.exit("CLI usage not yet implemented. Use --gui to launch the graphical interface.")

if __name__ == "__main__":
main()
Empty file added src/orca/geometry/__init__.py
Empty file.
6 changes: 3 additions & 3 deletions src/orca/geometry/base_geometry.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass
from functools import cached_property
from typing import Any, TYPE_CHECKING

from orca.geometry.input_parameters import InputParameterIterator
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
from orca.geometry.input_parameters import InputParameterIterator
from orca.training.datasets.base_dataset import BaseDataset


Expand Down
Empty file.
Loading
Loading