Metadata-Version: 2.5
Name: opensci-engine
Version: 0.1.0
Summary: Deterministic, stateless, offline 3D optical / layout / control engineering engine for OpenSci (CPython + Pyodide).
Author: OpenSci
License: Proprietary
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: Typing :: Typed
Requires-Python: >=3.14
Requires-Dist: numpy<3,>=2.3
Requires-Dist: pydantic<3,>=2.11
Requires-Dist: scipy<2,>=1.16
Requires-Dist: shapely<3,>=2.1
Provides-Extra: test
Requires-Dist: pytest-cov>=5; extra == 'test'
Requires-Dist: pytest>=8; extra == 'test'
Description-Content-Type: text/markdown

# opensci-engine

Deterministic, stateless, **offline** engineering computation core for OpenSci: 3D geometric optics, Gaussian beams
(x/y-independent q-parameter), Jones polarization with a transported 3D transverse basis, analytic two-beam interference,
2.5D layout, and control / recoverability analysis. One pure-Python package, one algorithm core for native CPython,
Pyodide/WASM (Web Worker), CI and controlled server verification.

* Python **3.14**, `numpy`, `scipy`, `pydantic>=2`, `shapely` (layout only). No compiler, CMake, CUDA or system library.
* Same input + same engine version + same numerical settings = same result within the declared tolerance.
* Everything out of scope returns `OUT_OF_MODEL_SCOPE` / `UNKNOWN` — never `PASS`.

```bash
pip install opensci-engine
opensci-engine validate request.json --pretty
```

## Quick example (405 nm / 500 nm two-beam interference)

Every example in this repository is an executable fixture (`tests/fixtures/requests/*.json`, built by
`tests/fixture_scenes.py`). The 405 nm / 500 nm case:

```python
import json
from opensci_engine import ValidationRequest, validate

request = ValidationRequest.model_validate_json(open("tests/fixtures/requests/interference_405_500.json").read())
result = validate(request)

print(result.status)                                   # PASS
pair = result.interference[0]
print(f"{pair.fringe_period_nm:.6f} nm, {pair.half_angle_deg:.4f} deg")   # 500.000000 nm, 23.8911 deg (traced 3D wave vectors)
print(f"{pair.fringe_visibility:.6f} {pair.coherence_margin:.6f}")           # 1.000000 1.000000
print(result.manifest.input_hash, result.manifest.result_hash)
```

Build a scene programmatically with the factories in `opensci_engine.builders`:

```python
import math
from opensci_engine import builders as B
from opensci_engine.contracts import ProjectSnapshot, Target
from opensci_engine import ValidationRequest, validate

project = ProjectSnapshot(
    components=(
        B.laser("L", wavelength_nm=532.0, waist_radius_mm=1.0, position=(0, 0, 20), direction=(1, 0, 0)),
        B.thin_lens("Lens", focal_length_mm=100.0, clear_radius_mm=12.7, position=(10, 0, 20), axis=(1, 0, 0)),
    ),
    observations=(B.observation_plane("focus", position=(110, 0, 20), normal=(1, 0, 0), radius_mm=5.0),),
    targets=(Target(target_id="w", metric="beam_radius_mm", observation_id="focus", maximum=0.05),),
)
result = validate(ValidationRequest(project=project))
```

## Public API

| Object | Purpose |
|---|---|
| `validate(request)` / `validate_json(text)` | pure function: request -> `ValidationResult` (never raises for physical / contract validation outcomes represented by the result model) |
| `ValidationRequest`, `ProjectSnapshot`, `EngineConfiguration`, `NumericalConfig` | inputs (Pydantic v2, JSON friendly) |
| `ValidationResult`, `ValidationIssue`, `ValidationManifest` | outputs (finite numbers only, structured issues, hashes) |
| `opensci_engine.builders` | element factories producing contract objects |
| `opensci_engine.compare.compare_results` | cross-runtime comparison within `cross_runtime_rel_tol/abs_tol` |
| `opensci-engine validate|schema|compare` | command line |

Status vocabulary: `PASS`, `WARNING`, `FAIL`, `UNKNOWN`, `NOT_APPLICABLE`, `OUT_OF_MODEL_SCOPE`.
CLI exit codes: 0 PASS/WARNING/NOT_APPLICABLE, 1 FAIL, 2 UNKNOWN, 3 OUT_OF_MODEL_SCOPE, 64 usage error.

The "never raises" guarantee above applies to the `validate()` / `validate_json()` entry points: physical and
contract-validation outcomes are always represented by the `ValidationResult` model, never raised as exceptions.
Direct construction of the frozen Pydantic contract models (e.g. `builders.laser(wavelength_nm=float("nan"))` or
`SourceSpec(...)`) can still raise a Pydantic `ValidationError` *before* those entry points are called, because the
contract models deliberately set `allow_inf_nan=False`. Pass such input through `validate_json` (the CLI / Pyodide
bridge) to receive it as a structured `FAIL` / `INVALID_INPUT` result instead.

## Conventions (short; details in `docs/NUMERICAL_CONVENTIONS.md`)

Right-handed 3D, lengths mm, wavelengths nm (vacuum), angles degrees at the API, power W, `w` = 1/e² intensity **radius**.
Element local `+z` = optical axis; surface normals are explicit; Jones vectors live in a transported transverse basis
`(e1, e2)`, `e1 × e2 = d`. `E ∝ exp[i(k·r − ωt)]`.

## Documentation

* `docs/ENGINE_ARCHITECTURE.md` — layers, contracts (frozen vs provisional), dependency decisions
* `docs/PHYSICS_SCOPE.md` — equations, assumptions, applicability, failure modes, independent references per module
* `docs/NUMERICAL_CONVENTIONS.md` — units, frames, every tolerance and its reason, determinism, hashing
* `docs/PYODIDE_COMPATIBILITY.md` — Pyodide policy and the automated tests
* `KNOWN_LIMITATIONS.md`, `GOLDEN_CASE_REPORT.md`, `NUMERICAL_ACCURACY_REPORT.md`, `PYODIDE_COMPATIBILITY_REPORT.md`,
  `DEPENDENCY_REPORT.md`, `ENGINE_IMPLEMENTATION_REPORT.md`

## Development

```bash
uv venv --python 3.14 .venv
uv pip install -e ".[test]"
pytest                                   # native tests (golden physics, robustness, layout, control, determinism, hygiene)
python tests/generate_fixtures.py        # regenerate fixtures / snapshots
python benchmarks/bench_validation.py    # standard local-validation benchmark
cd tests/pyodide && npm ci && cd ../..
pytest -m pyodide                        # real Pyodide install + native/Pyodide parity (needs node + network on a cold cache)
pytest -m install                        # build the wheel, install into a clean venv, run a Golden Case
```
