Contributing
Thank you for your interest in contributing to COMPASS. This guide covers how to set up a development environment, the contribution workflow, and guidelines for specific types of contributions.
Development setup
Prerequisites
- Python 3.10 or later
- Git
- (Optional) NVIDIA GPU with CUDA for GPU-accelerated solvers
Clone and install
git clone https://github.com/compass-team/compass.git
cd compass
# Create a virtual environment
python -m venv .venv
source .venv/bin/activate
# Install in development mode with all optional dependencies
pip install -e ".[all,dev]"Verify the installation
# Run the test suite
PYTHONPATH=. python -m pytest tests/ -v
# Run linting
ruff check compass/
# Run type checking
mypy compass/Code style
COMPASS follows these conventions:
- Python 3.10+ with type hints on all public functions and methods
- Pydantic for configuration validation
- Google-style docstrings for all public classes and functions
- ruff for linting (rules: E, F, W, I)
- Line length: 100 characters maximum
- Imports: sorted by ruff (isort-compatible)
Example function with proper style:
def compute_qe(
result: SimulationResult,
pixel_stack: PixelStack,
wavelength: float,
) -> dict[str, float]:
"""Compute quantum efficiency per pixel from simulation result.
Extracts the absorbed power in each photodiode region and normalizes
by the incident power to obtain QE.
Args:
result: Completed simulation result with field data.
pixel_stack: Pixel geometry for photodiode region identification.
wavelength: Wavelength in micrometers.
Returns:
Dictionary mapping pixel names (e.g., "R_0_0") to QE values.
Raises:
ValueError: If result does not contain absorption data.
"""
...Adding a new solver
COMPASS uses a plug-in architecture for solvers. To add a new solver:
1. Create the solver module
Create a new file under the appropriate directory:
- RCWA solvers:
compass/solvers/rcwa/your_solver.py - FDTD solvers:
compass/solvers/fdtd/your_solver.py
2. Implement the SolverBase interface
from compass.solvers.base import SolverBase, SolverFactory
from compass.core.types import SimulationResult
from compass.geometry.pixel_stack import PixelStack
import numpy as np
class YourSolver(SolverBase):
"""Your solver adapter for COMPASS."""
def setup_geometry(self, pixel_stack: PixelStack) -> None:
"""Convert PixelStack to your solver's geometry format."""
self._pixel_stack = pixel_stack
# Convert layers, materials, and geometry to your solver's format
...
def setup_source(self, source_config: dict) -> None:
"""Configure the excitation source."""
self._source_config = source_config
# Set wavelength, angle, polarization
...
def run(self) -> SimulationResult:
"""Execute the simulation."""
# Run your solver
# Extract R, T, A, QE per pixel, field data
# Return standardized SimulationResult
...
def get_field_distribution(
self, component: str, plane: str, position: float
) -> np.ndarray:
"""Extract a 2D field slice."""
...
# Register with the factory
SolverFactory.register("your_solver", YourSolver)3. Add to the import map
In compass/solvers/base.py, add your solver to the _try_import map:
import_map = {
...
"your_solver": "compass.solvers.rcwa.your_solver", # or fdtd
}4. Add tests
Create tests/test_your_solver.py with at minimum:
- Solver creation test
- Geometry setup test
- Single-wavelength run test
- Energy balance validation test
Adding new materials
Built-in material (CSV)
Add a CSV file under materials/ with columns: wavelength_um, n, k:
wavelength_um,n,k
0.380,1.520,0.000
0.400,1.518,0.000
0.420,1.516,0.000
...Then register it in the material database configuration.
Analytical dispersion model
For materials defined by Sellmeier or Cauchy equations, add the coefficients to the material database YAML:
your_material:
model: "sellmeier"
coefficients:
B: [1.0396, 0.2318, 1.0105]
C: [0.00600, 0.02002, 103.56]Testing
Running tests
# All tests
PYTHONPATH=. python -m pytest tests/ -v
# Specific test file
PYTHONPATH=. python -m pytest tests/test_geometry.py -v
# Skip slow tests (FDTD, large sweeps)
PYTHONPATH=. python -m pytest tests/ -v -m "not slow"
# With coverage
PYTHONPATH=. python -m pytest tests/ --cov=compass --cov-report=htmlWriting tests
- Place tests in
tests/with the naming conventiontest_<module>.py - Use pytest fixtures for shared setup (configs, material database, pixel stacks)
- Mark slow tests with
@pytest.mark.slow - Test physical constraints: QE in [0, 1], energy balance R + T + A = 1
Example test:
import numpy as np
import pytest
from compass.solvers.base import SolverFactory
def test_solver_energy_balance(pixel_stack, solver_config):
"""Verify that R + T + A = 1 within tolerance."""
solver = SolverFactory.create("torcwa", solver_config)
solver.setup_geometry(pixel_stack)
solver.setup_source({"wavelength": 0.55, "theta": 0.0,
"phi": 0.0, "polarization": "unpolarized"})
result = solver.run()
total = result.reflection + result.transmission + result.absorption
assert np.allclose(total, 1.0, atol=0.02), (
f"Energy not conserved: R+T+A = {total}"
)Submitting changes
- Fork the repository and create a feature branch from
main - Make your changes following the code style guidelines above
- Add or update tests to cover your changes
- Run the full test suite and linter to verify nothing is broken
- Write a clear commit message describing the change
- Open a pull request with a description of what the change does and why
Commit message format
Use a concise imperative-style subject line:
Add meep FDTD solver adapter
Implement SolverBase interface for MIT Meep, supporting:
- 3D simulation with periodic/PML boundaries
- Broadband source with DFT monitors
- Field extraction on arbitrary planesGetting help
- Open an issue for bug reports or feature requests
- Use discussions for questions about usage or design decisions
- Tag issues with appropriate labels:
bug,enhancement,solver,material,docs