Metadata-Version: 2.4
Name: optixde
Version: 0.3.1
Summary: OptiXDE: optical-inspired PDE solver
Author-email: Yang Yang <yangyhhu@foxmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/yangyLab/OptiXDE
Project-URL: Repository, https://github.com/yangyLab/OptiXDE
Project-URL: Issues, https://github.com/yangyLab/OptiXDE/issues
Keywords: PDE,spectral methods,FFT,scientific computing,GPU
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.20
Provides-Extra: plot
Requires-Dist: matplotlib>=3.6; extra == "plot"
Provides-Extra: sparse
Requires-Dist: scipy>=1.9; extra == "sparse"
Provides-Extra: torch
Requires-Dist: torch; extra == "torch"
Provides-Extra: gpu
Requires-Dist: torch; extra == "gpu"
Provides-Extra: cupy
Requires-Dist: cupy; extra == "cupy"
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: coverage>=7.5; extra == "dev"
Requires-Dist: matplotlib>=3.6; extra == "dev"
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: ruff>=0.5; extra == "dev"
Requires-Dist: scipy>=1.9; extra == "dev"
Requires-Dist: twine>=5; extra == "dev"
Dynamic: license-file

<p align="center">
  <img src="https://yangylab.github.io/optixde-site/assets/optixde_logo_horizontal.png" alt="OptiXDE logo" width="620">
</p>

# OptiXDE

[![Python](https://img.shields.io/badge/python-3.9%2B-blue.svg)](pyproject.toml)
[![Release](https://img.shields.io/badge/release-v0.3.1-blueviolet.svg)](https://github.com/yangyLab/OptiXDE/releases/tag/v0.3.1)
[![Tests](https://img.shields.io/badge/tests-unittest-green.svg)](#development)
[![GPU](https://img.shields.io/badge/GPU-PyTorch%20periodic%20FFT-purple.svg)](docs/colab_torch_gpu.md)

OptiXDE is a lightweight Python package for optical-inspired PDE solvers on
uniform grids. It focuses on fast, matrix-free spectral methods for rectangular
domains, with experimental embedded-domain and solid-mechanics modules growing
alongside the core solver stack.

The core convention is consistent across the package:

- Poisson: `-Δu = f`
- Helmholtz: `(-Δ + k0^2)u = f`
- Diffusion: `u_t = D Δu`
- Wave: `u_tt = c^2 Δu`

## Highlights

- **Matrix-free solvers** for Poisson, Helmholtz, diffusion, and wave equations.
- **Operator splitting** utilities for nonlinear PDEs such as Allen--Cahn.
- **Custom linear PDE compilation** from symbolic equations to periodic
  spectral operators, without changing the existing specialized solvers.
- **Nonlinear examples** for Allen--Cahn and 1D periodic viscous Burgers.
- **Periodic incompressible flow** via 2D vorticity-streamfunction Navier--Stokes.
- **Periodic immersed-cylinder flow** with a Brinkman mask and inflow-restoring fringe.
- **Schrödinger/paraxial propagation** for optical-style complex wave fields.
- **FFT/DCT/DST transforms** for periodic, Dirichlet, and Neumann boundary
  conditions.
- **Robin boundary support** through specialized fallbacks and finite-difference
  projection helpers.
- **PyTorch backend** for periodic FFT solvers on CPU or CUDA GPUs.
- **Backend diagnostics** with `return_info=True` and capability flags.
- **Geometry helpers** for signed-distance primitives and Boolean operations.
- **Segmented rectangular solvers** for Poisson, transient diffusion, paired
  periodic subsets, and component-wise Navier--Stokes velocity boundaries.
- **Experimental modules** for embedded segmented domains and periodic solid mechanics.

## Install

Install the current release from PyPI:

```bash
pip install optixde
```

Install optional features from PyPI as needed:

```bash
pip install "optixde[plot]"    # Matplotlib plotting helpers
pip install "optixde[sparse]"  # SciPy-based polygonal embedded solvers
pip install "optixde[gpu]"     # PyTorch backend for periodic GPU FFT solvers
```

For local development:

```bash
git clone https://github.com/yangyLab/OptiXDE.git
cd OptiXDE
pip install -e .
```

For development, optional feature groups can be installed from the checkout:

```bash
pip install -e ".[plot]"    # Matplotlib plotting helpers and examples
pip install -e ".[sparse]"  # SciPy-based polygonal embedded solvers
pip install -e ".[torch]"   # PyTorch backend alias
pip install -e ".[gpu]"     # PyTorch backend for periodic GPU FFT solvers
pip install -e ".[cupy]"    # CuPy backend, if your CUDA/CuPy stack is ready
pip install -e ".[dev]"     # tests, lint, plotting, sparse extras
```

## Quick Start

```python
import numpy as np
from optixde.solvers import (
    diffusion2d_solve,
    helmholtz2d_solve,
    poisson2d_solve,
    wave2d_solve,
)

Lx = Ly = 2.0 * np.pi
N = 64
x = np.linspace(0.0, Lx, N, endpoint=False)
y = np.linspace(0.0, Ly, N, endpoint=False)
X, Y = np.meshgrid(x, y, indexing="xy")

u_exact = np.sin(2 * X) * np.cos(3 * Y)
f = 13.0 * u_exact

u = poisson2d_solve(f, Lx, Ly, bc="periodic")
w = helmholtz2d_solve(f, Lx, Ly, k0=1.0, bc="periodic")
next_u = diffusion2d_solve(
    u_exact,
    D=0.1,
    Lx=Lx,
    Ly=Ly,
    dt=0.01,
    bc="periodic",
)
next_wave_u, next_wave_v = wave2d_solve(
    u_exact,
    np.zeros_like(u_exact),
    c=1.0,
    Lx=Lx,
    Ly=Ly,
    dt=0.01,
    bc="periodic",
)
```

## Solver Map

### Poisson

```python
u = poisson2d_solve(f, Lx, Ly, bc="periodic")
u = poisson2d_solve(f, Lx, Ly, bc="dirichlet")
u = poisson2d_solve(f, Lx, Ly, bc="neumann")
u = poisson2d_solve(f, Lx, Ly, bc="robin", robin=(alpha, beta, g))
```

Periodic and Neumann Poisson problems require `mean(f) = 0`; OptiXDE enforces
this by default and fixes the additive constant with a zero-mean gauge.

### Helmholtz

```python
u = helmholtz2d_solve(f, Lx, Ly, k0=1.0, bc="periodic")
u = helmholtz2d_solve(f, Lx, Ly, k0=1.0, bc="dirichlet")
u = helmholtz2d_solve(f, Lx, Ly, k0=1.0, bc="neumann")
```

`k0 > 0` makes the operator strictly elliptic, so no zero-mean constraint is
needed.

### Diffusion

```python
u = diffusion2d_solve(u0, D, Lx, Ly, dt, bc="periodic")
u = diffusion2d_solve(u0, D, Lx, Ly, dt, bc="dirichlet")
u = diffusion2d_solve(u0, D, Lx, Ly, dt, bc="neumann")
u = diffusion2d_solve(u0, D, Lx, Ly, dt, bc="robin", robin=(alpha, beta, g))
```

Periodic diffusion also supports an ETD1 source term through `source=`.

### Wave

```python
u, v = wave2d_solve(u0, v0, c, Lx, Ly, dt, bc="periodic")
u, v = wave2d_solve(u0, v0, c, Lx, Ly, dt, bc="dirichlet")
u, v = wave2d_solve(u0, v0, c, Lx, Ly, dt, bc="neumann")
```

The wave solver advances the first-order state `(u, v)` for `u_tt = c^2 Δu`
with an exact spectral update for each mode. With `return_info=True`, it returns
`u_next, v_next, info`.

## Boundary Conditions

| Boundary condition | Poisson | Helmholtz | Diffusion | Wave | Notes |
| --- | --- | --- | --- | --- | --- |
| `periodic` | Yes | Yes | Yes | Yes | FFT-based; supports NumPy, Torch, and CuPy backends. |
| `dirichlet` | Yes | Yes | Yes | Yes | DST-based rectangular-domain solver. |
| `neumann` | Yes | Yes | Yes | Yes | DCT-based rectangular-domain solver; Poisson uses a zero-mean gauge. |
| `robin` | Yes | No | Yes | No | Uses exact Dirichlet/Neumann fallbacks where possible and penalty/projection helpers otherwise. |

The older `mode=` keyword remains accepted as a compatibility alias for `bc=`.

## Backends And GPU

The default backend is NumPy:

```python
u = poisson2d_solve(f, Lx, Ly, bc="periodic", backend_name="numpy")
```

For GPU work, PyTorch is the recommended path:

```python
u = poisson2d_solve(
    f,
    Lx,
    Ly,
    bc="periodic",
    backend_name="torch",
    device="cuda",
)
```

Current PyTorch scope:

- Supported: periodic Poisson, Helmholtz, diffusion, wave, Burgers,
  vorticity-streamfunction Navier--Stokes, and Schrödinger FFT paths.
- Supported: CPU tensors and CUDA tensors, depending on your PyTorch install.
- Not yet supported: Torch DCT/DST paths for Dirichlet and Neumann solvers.
- Periodic Brinkman/fringe Navier--Stokes supports NumPy and CuPy arrays; the
  compatibility `cylinder_re200_*` names refer to the same general solver.
- The final Re=200 paper driver defaults to CuPy/CUDA and provides
  `--backend numpy` for CPU smoke and regression runs.

For Colab GPU validation, see `docs/colab_torch_gpu.md`.

## Diagnostics

Backends expose lightweight capability flags:

```python
from optixde.fft_backend import get_backend

backend = get_backend("torch", device="cuda")
print(backend.capabilities)
```

Solver entry points can return metadata with `return_info=True`:

```python
u, info = poisson2d_solve(
    f,
    Lx,
    Ly,
    bc="periodic",
    backend_name="torch",
    device="cuda",
    return_info=True,
)

print(info["backend"], info["device"], info["bc"], info["transform"])
```

Common `info` fields are stable across solvers:

- `solver`, `equation`, `bc`, `transform`: which equation and numerical path ran.
- `backend`, `device`, `capabilities`: NumPy/Torch/CuPy backend and feature flags.
- `input_shape`, `output_shape`, `input_dtype`, `output_dtype`: array metadata.
- PDE extras such as `dt`, `viscosity`, `epsilon`, `wave_speed`, or `coefficient`.

This is useful for tests, benchmarks, Colab runs, and checking whether a solve
used FFT, DCT, DST, operator splitting, or a fallback path.

## Examples

Example scripts live under `examples/`:

- `examples/base/`: core PDE demos and rectangular-domain experiments.
- `examples/benchmarks/`: CPU/GPU backend timing scripts and benchmark cases.
- `examples/solid/`: periodic solid-mechanics demos.
- `examples/demo_post.py`: plotting helper demo.

See `examples/README.md` for a command index.

Run a small backend benchmark:

```bash
python examples/benchmarks/torch_fft_backend_benchmark.py --device cpu --sizes 64 128
```

On a CUDA machine or Colab runtime:

```bash
python examples/benchmarks/colab_torch_gpu_smoke.py --device cuda --n 256
python examples/benchmarks/torch_fft_backend_benchmark.py --device cuda --sizes 512 1024
```

Run the paper-style wave examples:

```bash
python examples/base/wave_single_mode_phase.py --sizes 128 256 512
python examples/base/wave_gaussian_packet.py --n 256
python examples/base/wave_gaussian_packet.py --n 192 --sigma 0.28 --center-x 3.141592653589793 --center-y 3.141592653589793 --T 6 --animation-output output/animations/wave/gaussian_packet.gif
python examples/base/wave_section_6_3_reproduction.py --output-dir examples/artifacts/section_6_3_wave
```

Run the nonlinear Allen--Cahn splitting example:

```bash
python examples/base/allen_cahn_splitting.py --n 128 --T 0.1
python examples/base/burgers_1d_periodic.py --n 256 --T 1.0
python examples/base/burgers_1d_periodic.py --backend torch --device cuda --n 1024 --T 1.0
python examples/base/burgers_section_reproduction.py --n 256 --ref-n 2048 --output-dir examples/artifacts/burgers_section --scan
python examples/base/navier_stokes_taylor_green.py --n 128 --T 1.0
python examples/base/navier_stokes_cylinder_brinkman.py --nx 128 --ny 64 --steps 1000 --plot --output examples/artifacts/navier_stokes_cylinder_brinkman.png --history-output examples/artifacts/navier_stokes_cylinder_brinkman_history.csv --history-figure examples/artifacts/navier_stokes_cylinder_brinkman_history.png --summary-output examples/artifacts/navier_stokes_cylinder_summary.csv --wake-figure examples/artifacts/navier_stokes_cylinder_wake.png --spectrum-figure examples/artifacts/navier_stokes_cylinder_spectrum.png
python examples/base/navier_stokes_cylinder_brinkman.py --nx 160 --ny 80 --steps 16000 --dt 0.001 --viscosity 0.005 --forcing 0.05 --perturbation 0.08 --penalty-eta 0.002 --animation-output output/animations/navier_stokes/cylinder_wake_vorticity.gif --animation-stride 200 --animation-vmax 8 --quiet
python examples/base/navier_stokes_cylinder_brinkman.py --nx 128 --ny 64 --steps 5000 --report-every 10 --quiet
python examples/base/navier_stokes_cylinder_brinkman.py --nx 96 --ny 48 --steps 400 --scan --scan-viscosity 0.002 0.0036 --scan-forcing 0.1 0.15 --scan-output examples/artifacts/navier_stokes_cylinder_scan.csv --quiet
python examples/base/schrodinger_gaussian_packet.py --n 128 --T 1.0
```

Compile a scalar constant-coefficient periodic PDE:

```python
from optixde.custom_pde import Equation, Field, compile_pde, dt, laplacian, solve_pde

u = Field("u")
problem = compile_pde(
    Equation(dt(u), 0.05 * laplacian(u)),
    shape=u0.shape,
    domain=(Lx, Ly),
)
times, states = solve_pde(problem, u0, dt=0.01, t_end=1.0)
```

Use a named compile-time parameter when the physical coefficient belongs to
the model specification:

```python
from optixde.custom_pde import Parameter

diffusivity = Parameter("diffusivity")
problem = compile_pde(
    Equation(dt(u), diffusivity * laplacian(u)),
    shape=u0.shape,
    domain=(Lx, Ly),
    parameters={"diffusivity": 0.05},
)
```

The binding is validated, compiled into the spectral symbol and propagator,
and recorded in `problem.report["parameters"]`. See the
[CustomPDE physics compiler](docs/custom_pde_physics_compiler.md) for the
supported scope and the distinction between compile-time `Parameter` objects
and solve-time `Source` inputs.

For parameter sweeps or future physics rollouts, keep the equation and backend
configuration in a reusable template:

```python
from optixde.custom_pde import PDETemplate

physics = PDETemplate(
    Equation(dt(u), diffusivity * laplacian(u)),
    shape=u0.shape,
    domain=(Lx, Ly),
)
plan = physics.compile(parameters={"diffusivity": 0.05})
```

First-order scalar and two-field transient equations can also propagate
gradients through Torch spectral steps. Trainable parameters may appear in
spatial, reaction, nonlinear, or additive source terms:

```python
theta = torch.tensor(0.05, device="cuda", requires_grad=True)
u_next = physics.step(
    u0,
    dt=0.01,
    parameters={"diffusivity": theta},
    differentiable=True,
)
loss_fn(u_next).backward()
```

The same template accepts batched states and differentiable multi-step
rollouts:

```python
times, states = physics.rollout(
    z0,  # shape: (batch, ny, nx)
    dt=0.01,
    steps=20,
    parameters={"diffusivity": theta},
    differentiable=True,
)
```

Declare controlled inputs separately from prescribed physical sources:

```python
from optixde.custom_pde import Action, Source

equation = Equation(
    dt(u),
    diffusivity * laplacian(u) + Source("wind") + Action("heater"),
)
z_next = physics.step(
    z_t,
    dt=0.01,
    parameters={"diffusivity": theta},
    sources={"wind": wind_t},
    actions={"heater": action_t},
    differentiable=True,
)
```

Use `inputs={"wind": wind_t, "heater": action_t}` when a unified mapping is
more convenient for a world-model interface.

For world-model code, `PDETemplate` exposes one automatically dispatched
State/Action interface across scalar and two-field, linear and semilinear
dynamics:

```python
plan = physics.compile_dynamics(parameters=theta)
z_next = step_dynamics(plan, z_t, dt=dt_value, actions=action_t)
times, trajectory, executed_actions = rollout_dynamics(
    plan,
    z_t,
    dt=dt_value,
    steps=horizon,
    policy=lambda state, time, index: {
        "heater": controller(state)
    },
    return_actions=True,
)
```

The shorter `physics.step(...)` and `physics.rollout(...)` methods compile and
dispatch through the same boundary. Scalar state is an array; coupled state is
a mapping from field names to arrays. Pass `mode="linear"` or
`mode="semilinear"` only when strict manual selection is needed—the default
`mode="auto"` inspects the symbolic specification.

Rollouts accept one fixed `actions` mapping, an `action_sequence` containing
one mapping per step, or a closed-loop `policy(state, time, index)`. These
providers are mutually exclusive. Policy outputs remain on the selected
backend, so Torch gradients propagate through the complete state/action
feedback loop. With `return_actions=True`, the third return value maps each
Action name to its step-first executed trajectory.

Compile polynomial semilinear equations into spectral `L` and pseudospectral
`N` operators explicitly:

```python
burgers = PDETemplate(
    Equation(dt(u), viscosity * laplacian(u) - 0.5 * dx(u**2)),
    shape=u0.shape,
    domain=(Lx, Ly),
)
compiled = burgers.compile_semilinear(
    parameters={"viscosity": 0.05},
    dealias="three_halves",
    nonlinear_order=4,
)
times, states = rollout_semilinear(compiled, u0, dt=0.002, steps=100)
```

The multidimensional three-halves path is exact for quadratic polynomial
nonlinearities on even periodic grids and remains differentiable with the Torch
backend. Use `two_thirds` for cubic and higher-degree terms.
Pointwise `sin`, `cos`, `exp`, and `tanh` nodes are also available for
non-polynomial physics and retain backend portability and Torch gradients; use
`two_thirds` or `none` for these functions.

Two-field systems use the same explicit semilinear boundary. Cross-field
products remain in physical space while the existing exact 2×2 modal
propagator advances the coupled linear operator:

```python
reaction_diffusion = EquationSystem(
    Equation(dt(u), du * laplacian(u) - u * v**2 + feed * (1 - u)),
    Equation(dt(v), dv * laplacian(v) + u * v**2 - (feed + kill) * v),
    fields=(u, v),
)
compiled = compile_semilinear_system(
    reaction_diffusion,
    shape=u0.shape,
    domain=(Lx, Ly),
    parameters={"feed": 0.04, "kill": 0.06},
)
times, states = rollout_semilinear_system(
    compiled,
    {"u": u0, "v": v0},
    dt=0.01,
    steps=100,
)
```

Coupled semilinear execution supports leading batch dimensions, typed
Source/Action inputs, RK1/RK2/RK4 nonlinear substeps, and `none` or
`two_thirds` dealiasing. Trainable coupled parameters and exact three-halves
padding remain outside this first stage.

Compile the wave equation as a coupled first-order system:

```python
from optixde.custom_pde import (
    Equation,
    EquationSystem,
    Field,
    compile_system,
    dt,
    laplacian,
    solve_system,
)

u = Field("u")
v = Field("v")
wave = compile_system(
    EquationSystem(
        Equation(dt(u), v),
        Equation(dt(v), c**2 * laplacian(u)),
        fields=(u, v),
    ),
    shape=u0.shape,
    domain=(Lx, Ly),
)
times, fields = solve_system(
    wave,
    {"u": u0, "v": v0},
    dt=0.01,
    t_end=1.0,
)
```

The same wave equation can be written directly with a second time derivative;
the compiler lowers it to the two-field system automatically:

```python
from optixde.custom_pde import dtt

wave = compile_pde(
    Equation(dtt(u), c**2 * laplacian(u)),
    shape=u0.shape,
    domain=(Lx, Ly),
)
times, fields = solve_pde(
    wave,
    {"u": u0, "u_t": v0},
    dt=0.01,
    t_end=1.0,
)
```

The lowered wave state is the explicit mapping
`{"u": displacement, "u_t": velocity}` and can use the unified dynamics API:

```python
next_state = step_dynamics(wave, state, dt=0.01, actions=actions)
times, trajectory = rollout_dynamics(wave, state, dt=0.01, steps=100)
```

Compiled reports expose `state_names` and `state_representation`, allowing a
world-model wrapper to validate scalar-array and field-mapping states without
depending on the underlying solver class.

The public `state_schema(plan)` and `validate_state(plan, state)` helpers turn
that metadata into one runtime contract. `validate_state` preserves leading
batch dimensions, moves values through the selected backend, and reports all
malformed runtime states with `PDESolveError`.

The compiler supports first- and second-order scalar equations, named scalar
physics parameters, and two-field constant-coefficient systems on
two-dimensional periodic domains, steady equations, and first-order transient
equations with derivatives up to fourth order. Existing PDE-specific solver
APIs remain unchanged.

The paper Burgers driver uses the public solver with fourth-order nonlinear
stages and exact three-halves padding:

```python
u_next = burgers1d_step(
    u,
    nu,
    L,
    dt,
    nonlinear_order=4,
    dealias="three_halves",
)
```

Run the unified reference-validation table:

```bash
python examples/benchmarks/solver_reference_validation.py --sizes 64 128 --output examples/artifacts/solver_reference_validation.csv
```

## Package Layout

```text
optixde/
  bc/             Robin, rasterization, and segmented-boundary helpers
  fft_backend/    NumPy, PyTorch, CuPy, and propagator-cache utilities
  geometry/       Signed-distance primitives and Boolean geometry
  operators/      Spectral operators and transform helpers
  post/           Optional Matplotlib plotting utilities
  solvers/        Core, segmented-domain, and solid-mechanics solvers
```

Primary public imports:

- `from optixde.bc import BoundarySet, BoundaryConditionSet, DirichletBC, NeumannBC, RobinBC`
- `from optixde.solvers import poisson2d_segmented, transient_diffusion2d_segmented`
- `from optixde.solvers import make_segmented_navier_stokes_grid, navier_stokes2d_segmented_solve`
- `from optixde.solvers import poisson2d_solve, diffusion2d_solve, wave2d_solve`
- `from optixde.solvers import burgers1d_solve, navier_stokes2d_vorticity_solve`
- `from optixde.solvers import navier_stokes2d_brinkman_fringe_step`
- `from optixde.solvers import cylinder_re200_step` (compatibility alias)
- `from optixde.solvers import schrodinger2d_solve, allen_cahn2d_solve`

PDE-specific modules are also kept as stable compatibility namespaces:

- `optixde.solvers.poisson`
- `optixde.solvers.helmholtz`
- `optixde.solvers.diffusion`
- `optixde.solvers.wave`
- `optixde.solvers.splitting`
- `optixde.solvers.allen_cahn`
- `optixde.solvers.burgers`
- `optixde.solvers.navier_stokes`
- `optixde.solvers.cylinder_re200`
- `optixde.solvers.schrodinger`
- `optixde.fft_backend`
- `optixde.geometry`
- `optixde.bc`

See [Segmented and mixed boundary conditions](docs/segmented_boundary_conditions.md)
for piecewise selectors, validation rules, time-dependent values, corner ownership,
and current solver capabilities.

## Development

Install development extras:

```bash
pip install -e ".[dev]"
```

Run tests:

```bash
python -m unittest discover -s tests -p "test*.py"
```

Optional checks:

```bash
python -m compileall -q optixde tests examples/benchmarks
python examples/benchmarks/solver_reference_validation.py --sizes 16 32 --burgers-ref-n 64 --burgers-dt 0.005 --burgers-ref-dt 0.0025 --burgers-T 0.02 --ns-dt 0.01 --ns-T 0.02 --wave-T 0.02
ruff check optixde tests
```

The test suite includes public import checks, transform checks, solver
diagnostics, Robin smoke tests, analytic convergence tests for the core
solvers, and a lightweight reference-validation benchmark used by CI. Torch GPU
tests are skipped automatically when PyTorch or CUDA is not available.

## Citation

If you use OptiXDE in academic work, please cite the software release and the
related paper or preprint when available. GitHub can read the citation metadata
from `CITATION.cff`.

```bibtex
@misc{yang2026optixdefastopticalinspiredsolver,
      title={OptiXDE: A fast optical-inspired solver for differential equations}, 
      author={Yang Yang and Mingjiao Yan and Zongliang Zhang},
      year={2026},
      eprint={2609.01009},
      archivePrefix={arXiv},
      primaryClass={math.NA},
      url={https://arxiv.org/abs/2609.01009}, 
}
```

Release notes are tracked in `CHANGELOG.md`.

## Copyright, License, and Disclaimer

Copyright (c) 2025-2026 Yang Yang
<yangyhhu@foxmail.com>.

OptiXDE is open-source software distributed under the
[MIT License](LICENSE). You may use, copy, modify, and redistribute the
software subject to the terms of that license. Third-party libraries,
datasets, papers, and other referenced materials remain subject to their
respective licenses and copyrights.

OptiXDE is research software provided "as is", without warranty of any kind.
Numerical results should be independently verified before the software is used
for engineering, safety-critical, clinical, financial, or other consequential
decisions. The authors and contributors are not liable for losses or damages
arising from use of the software, to the extent permitted by applicable law.

Project information, source code, issue reporting, and release history are
available at [github.com/yangyLab/OptiXDE](https://github.com/yangyLab/OptiXDE).

## Project Status

OptiXDE is currently an early-stage research/development package. The stable
center is the rectangular-domain spectral solver stack; segmented-domain,
Robin, CuPy, and solid-mechanics pieces are still evolving.

When adding new solvers, keep the `-Δ` operator convention consistent across
the package and prefer backend-aware, matrix-free implementations where
possible.
