# Copilot Instructions for grid1d
## Project Overview
**grid1d** is a Rust library (Edition 2024, Nightly required) for mathematically robust 1D grids and interval partitions. It emphasizes compile-time type safety, generic scalar support via `num-valid`, and performance for numerical computing, finite element methods, and adaptive mesh refinement.
**Current version**: 0.2.0
## Build & Test Commands
```bash
cargo build # Nightly auto-configured via rust-toolchain.toml
cargo test # Run all tests
cargo test --features rug # With arbitrary precision (needs libgmp-dev, libmpfr-dev, libmpc-dev)
cargo test --features backtrace # Enable backtraces in errors
cargo bench # Criterion benchmarks → target/criterion/
cargo doc --open # Generate and view documentation
```
## Architecture
| Module | Purpose | Key Types |
|--------|---------|-----------|
| `lib.rs` | Grid types, core errors | `Grid1D`, `Grid1DUniform`, `Grid1DNonUniform`, `ErrorsGrid1D` |
| `operations.rs` | Grid operations module | Re-exports from `refinement` and `union` submodules |
| `operations/refinement.rs` | Grid refinement operations | `Grid1DRefinement`, `Grid1DUniformRefinement`, `Grid1DNonUniformRefinement` |
| `operations/union.rs` | Grid union operations | `Grid1DUnion`, `ErrorsGrid1DUnion` |
| `coords.rs` | Coordinate collections | `Coords1D`, `Coords1DInDomain`, `Coords1DInDomainOwned`, `Coords1DInDomainBorrowed`, `Coords1DInDomainArc`, `Coords1DInDomainRc`, `Coords1DInDomainBox`, `Coords1DInDomainCow` |
| `intervals.rs` | Type-safe interval system (re-exports from submodules) | `IntervalClosed`, `IntervalOpen`, 4 half-open variants, `IntervalSingleton`, `Interval` enum, `IntervalFinitePositiveLength`, `IntervalFiniteLength`, `IntervalFinitePositiveLengthTrait`, `IntervalFromBounds`, `SubIntervalInPartition`, `IntervalOperations`, `IntervalUnion`, `IntervalDifference`, `IntervalTrait`, `Contains`, `GetLowerBoundValue`, `GetUpperBoundValue`, `IntervalBoundsRuntime`, `IntervalHull`, plus all unbounded variants |
| `intervals/bounded.rs` | Bounded interval implementations and core traits | `IntervalBounded`, `IntervalClosed`, `IntervalOpen`, `IntervalLowerClosedUpperOpen`, `IntervalLowerOpenUpperClosed`, `IntervalSingleton`, `IntervalFinitePositiveLength`, `IntervalFiniteLength`, `SubIntervalInPartition`, `IntervalFinitePositiveLengthTrait`, `IntervalFromBounds` |
| `intervals/unbounded.rs` | Unbounded interval implementations | `IntervalLowerClosedUpperUnbounded`, `IntervalLowerOpenUpperUnbounded`, `IntervalLowerUnboundedUpperClosed`, `IntervalLowerUnboundedUpperOpen`, `IntervalLowerUnboundedUpperUnbounded`, `IntervalInfiniteLength`, `IntervalLowerBoundedUpperUnboundedTrait`, `IntervalLowerUnboundedUpperBoundedTrait` |
| `intervals/traits.rs` | Core interval trait definitions | `IntervalTrait`, `IntervalBoundsRuntime`, `IntervalHull`, `Contains`, `GetLowerBoundValue`, `GetUpperBoundValue` |
| `intervals/operations.rs` | Interval set operations | `IntervalOperations` (intersection, union, difference, hull), `IntervalUnion` (result enum), `IntervalDifference` (result enum) |
| `intervals/conversions.rs` | Interval type conversions | Conversion traits between interval types |
| `bounds.rs` | Boundary marker types | `LowerBoundClosed`, `LowerBoundOpen`, `UpperBoundClosed`, `UpperBoundOpen`, `IntervalBound<RealType, Side, Type>`, `BoundSide`, `BoundType`, `Lower`, `Upper`, `Open`, `Closed` |
| `scalars.rs` | Domain-validated wrappers | `NumIntervals`, `IntervalId`, `PositiveNumPoints1D`, `PositiveSize`, `Size` |
| `traits.rs` | Core trait definitions | `HasDomain1D`, `HasCoords1D`, `Grid1DTrait`, `Grid1DIntervalBuilder`, `TransformedPoints1D` |
**Note**: Grid operation types (`Grid1DRefinement`, `Grid1DUniformRefinement`, `Grid1DNonUniformRefinement`, `Grid1DUnion`, `ErrorsGrid1DUnion`) are re-exported at the crate root for ergonomic access. Interval types and traits are accessible via `use grid1d::intervals::*`; the most common ones (`IntervalClosed`, `IntervalOpen`, etc., `IntervalTrait`, `IntervalFromBounds`, `IntervalFinitePositiveLengthTrait`) are also re-exported at the crate root.
### Core Traits Hierarchy
```
Grid1DTrait ─┬─ HasCoords1D (coordinate access: coords(), num_points())
└─ HasDomain1D (domain access: domain())
└─ Domain1D: Grid1DIntervalBuilder
Grid1DIntervalBuilder
└─ IntervalFinitePositiveLengthTrait
Methods: build_single_interval(self), build_first_interval(&self, coords),
build_middle_interval(&self, coords, interval_id), build_last_interval(&self, coords)
IntervalTrait ─── Base trait for all interval types (into_interval(), intersection())
IntervalFinitePositiveLengthTrait ─── For bounded intervals with positive measure (length(), midpoint(), bounds access)
IntervalFromBounds ─── Construction from raw bounds: new(), try_new(), new_unit_interval(), new_symmetric_interval()
Contains ─── Point and interval containment: contains_point(), contains_interval()
GetLowerBoundValue ─── Access to finite lower bound value: lower_bound_value()
GetUpperBoundValue ─── Access to finite upper bound value: upper_bound_value()
IntervalBoundsRuntime ─── Runtime bound access: lower_bound_runtime(), upper_bound_runtime(), max_lower_bound(), min_upper_bound()
IntervalHull ─── Smallest enclosing interval: hull(&other)
IntervalOperations ─── Set operations supertrait: intersection(), union(), difference()
Returns IntervalUnion or Option<IntervalDifference>
```
## Critical Design Patterns
### 1. Type-Safe Intervals (Never boolean flags)
Boundary semantics are encoded in the type, not runtime values:
```rust
// ✅ Correct: type encodes boundary behavior
let closed = IntervalClosed::new(0.0, 1.0); // [0, 1]
let half_open = IntervalLowerClosedUpperOpen::new(0.0, 1.0); // [0, 1)
let unbounded = IntervalLowerClosedUpperUnbounded::new(0.0); // [0, +∞)
// ❌ Wrong: this API does not exist
// Interval::new(a, b, is_left_closed, is_right_closed)
```
**All 9 interval types:**
| Type | Notation | Use Case |
|------|----------|----------|
| `IntervalClosed` | [a, b] | Standard bounded domains |
| `IntervalOpen` | (a, b) | Open sets, strict inequalities |
| `IntervalLowerClosedUpperOpen` | [a, b) | Periodic domains, partitions |
| `IntervalLowerOpenUpperClosed` | (a, b] | Asymmetric boundaries |
| `IntervalLowerClosedUpperUnbounded` | [a, +∞) | Semi-infinite domains |
| `IntervalLowerOpenUpperUnbounded` | (a, +∞) | Strict lower, unbounded above |
| `IntervalLowerUnboundedUpperClosed` | (-∞, b] | Unbounded below |
| `IntervalLowerUnboundedUpperOpen` | (-∞, b) | Strictly bounded above |
| `IntervalSingleton` | {a} | Single point (degenerate) |
### 2. Fallible (`try_new`) vs Infallible (`new`) Construction
```rust
use grid1d::intervals::*; // Required for new()/try_new() trait methods
// try_new(): validated constraints, can fail
let n = NumIntervals::try_new(0); // Err: must be ≥1
let interval = IntervalClosed::try_new(5.0, 3.0); // Err: lower > upper
// new(): always valid, no constraints or pre-validated
let id = IntervalId::new(5); // Always succeeds
let closed = IntervalClosed::new(0.0, 1.0); // Panics if invalid (use in trusted contexts)
```
**Note**: `new()` and `try_new()` for concrete interval types are defined on `IntervalFromBounds` (a supertrait of `IntervalFinitePositiveLengthTrait`).
Use `use grid1d::intervals::*` to bring these methods into scope.
Generic code that only reads or analyzes intervals should be bounded by `IntervalFinitePositiveLengthTrait`.
Generic code that materializes a concrete interval from raw `(lower, upper)` bounds should additionally require `IntervalFromBounds`.
`IntervalFinitePositiveLength` is **not** constructible from naked bounds — use `From<IntervalClosed<_>>` / `From<IntervalOpen<_>>` / etc. for public construction, or the `pub(crate)` `IntervalFinitePositiveLength::try_from_runtime_bounds(LowerBoundRuntime, UpperBoundRuntime)` when the Open/Closed semantics are already known at runtime (internal use only).
### 3. Grid Construction & Selection
```rust
use grid1d::{Grid1D, Grid1DTrait, intervals::*, scalars::NumIntervals};
use sorted_vec::partial::SortedSet;
use try_create::TryNew;
// Uniform grid: O(1) point location, best for regular discretizations
let grid = Grid1D::uniform(IntervalClosed::new(0.0, 1.0), NumIntervals::try_new(10).unwrap());
// Non-uniform grid: O(log n) point location, for adaptive/clustered meshes
// Domain is inferred from the first and last coordinates
let coords = SortedSet::from_unsorted(vec![0.0, 0.1, 0.5, 1.0]);
let grid = Grid1D::<IntervalClosed<f64>>::try_from_sorted(coords)?;
```
**Grid type selection:**
| Scenario | Grid Type | Why |
|----------|-----------|-----|
| Regular FDM mesh | `Grid1DUniform` | O(1) lookups, predictable timing |
| Boundary layers, adaptive FEM | `Grid1DNonUniform` | Custom point clustering |
| Multi-physics coupling | `Grid1DUnion` | Combines grids with bidirectional mapping |
| Mesh refinement | `Grid1DRefinement` | Parent-child interval tracking |
### 4. Scalar Types (import from `num-valid`, not `grid1d`)
```rust
use num_valid::{RealNative64StrictFiniteInDebug, RealScalar};
type Real = RealNative64StrictFiniteInDebug; // Recommended default
```
| Use Case | Type | Performance | Validation |
|----------|------|-------------|------------|
| **Development (recommended)** | `RealNative64StrictFiniteInDebug` | ⚡⚡⚡ Same as f64 | Debug only |
| Max performance | `f64` | ⚡⚡⚡ Maximum | None |
| Safety-critical | `RealNative64StrictFinite` | ⚡⚡ ~5-10% overhead | Always |
| Arbitrary precision | `RealRugStrictFinite<N>` | ⚡ Variable | Always |
**Also import from `num-valid`:** `AbsoluteTolerance`, `RelativeTolerance`, `PositiveRealScalar`, `NonNegativeRealScalar`
### 5. Error Handling with `thiserror`
All error types are generic and use `thiserror`:
```rust
// Error types include backtrace for debugging
pub enum ErrorsGrid1D<Domain1D: IntervalFinitePositiveLengthTrait> {
RequiredAtLeastTwoDistinctPoints { backtrace: Backtrace },
...
}
pub enum ErrorsIntervalConstruction<RealType: RealScalar> {
LowerBoundGreaterOrEqualThanUpperBound { lower_bound: RealType, upper_bound: RealType, backtrace: Backtrace },
}
pub enum ErrorsGrid1DUnion<Domain1D: IntervalFinitePositiveLengthTrait> {
DomainsMismatch { domain_grid_a: Domain1D, domain_grid_b: Domain1D, backtrace: Backtrace },
...
}
```
### 6. Code Generation with `duplicate`
The crate uses `duplicate::duplicate_item` macro extensively to reduce boilerplate for similar implementations across interval types:
```rust
#[duplicate_item(
interval_type lower_bound_type upper_bound_type;
[IntervalClosed] [Closed] [Closed];
[IntervalOpen] [Open] [Open];
[IntervalLowerClosedUpperOpen] [Closed] [Open];
...
)]
impl<RealType: RealScalar> IntervalTrait for interval_type<RealType> { ... }
```
## Common Operations
### Point Location & Interval Access
```rust
// Single point location
let id: IntervalId = grid.find_interval_id_of_point(&0.3);
let interval: SubIntervalInPartition<_> = grid.interval(&id);
// Iterate over all intervals
for (id, interval) in grid.iter_intervals() {
println!("Interval {}: {:?}", id.as_ref(), interval);
}
// Interval length (type-safe wrapper)
let length: PositiveRealScalar<_> = grid.interval_length(&id);
```
### Grid Union (Multi-Physics Coupling)
```rust
let union = Grid1DUnion::try_new(&flow_grid, &chemistry_grid)?;
// Bidirectional mapping
for (unified_id, flow_id, chem_id) in union.iter_interval_mappings() {
// Transfer data between physics
}
// Direct mapping access
let (original_a, original_b) = union.find_original_intervals(&refined_id);
```
### Grid Refinement (AMR)
```rust
use grid1d::{Grid1D, Grid1DTrait, intervals::*, scalars::{NumIntervals, PositiveNumPoints1D, IntervalId}};
use try_create::TryNew;
use std::collections::BTreeMap;
let grid = Grid1D::uniform(IntervalClosed::new(0.0, 1.0), NumIntervals::try_new(4).unwrap());
// Uniform refinement: each interval gets 1 extra point (2 sub-intervals)
let refinement = grid.refine_uniform(&PositiveNumPoints1D::try_new(1).unwrap());
// Track parent-child relationships
for (refined_id, original_id) in refinement.iter_refined_with_mapping() {
println!("Refined {} came from original {}", refined_id.as_ref(), original_id.as_ref());
}
// Transfer data from coarse to fine
let coarse_data = vec![1.0, 2.0, 3.0, 4.0];
let fine_data = refinement.transfer_interval_data(&coarse_data);
// Selective refinement with BTreeMap
let selective_plan = BTreeMap::from([
(IntervalId::new(1), PositiveNumPoints1D::try_new(2).unwrap()), // 3 sub-intervals
(IntervalId::new(3), PositiveNumPoints1D::try_new(1).unwrap()), // 2 sub-intervals
]);
let selective_refinement = grid.refine(&selective_plan);
```
### Coordinates with Ownership Flexibility
```rust
use grid1d::coords::{Coords1DInDomain, Coords1DInDomainOwned, Coords1DInDomainBorrowed, Coords1DInDomainArc};
// Owned, borrowed, Arc, or Rc storage
let owned: Coords1DInDomainOwned<IntervalClosed<f64>> = Coords1DInDomain::try_new(coords, domain)?;
let borrowed: Coords1DInDomainBorrowed<_> = Coords1DInDomain::try_new(&coords, domain)?;
let arc_shared: Coords1DInDomainArc<_> = Coords1DInDomain::try_new(Arc::new(coords), domain)?;
```
## Key Pitfalls to Avoid
1. **Heterogeneous intervals in collections**: Use `Interval<T>` enum wrapper, not mixed concrete types
2. **Raw `usize` for grid quantities**: Always use `NumIntervals`, `IntervalId`, `PositiveNumPoints1D`
3. **Assuming uniform spacing**: Don't assume `interval_length()` is constant for generic `Grid1DTrait`
4. **Importing from wrong crate**: Scalar types (`RealScalar`, tolerances) come from `num-valid`
5. **Ignoring boundary semantics**: Open bounds exclude endpoints—critical for partitions and containment checks
6. **Using `new()` with untrusted input**: Use `try_new()` for user input; `new()` panics on invalid data
7. **Coords types are in `coords` module**: Import `Coords1D`, `Coords1DInDomain*` from `grid1d::coords`
8. **Missing trait import for interval construction**: Use `use grid1d::intervals::*` to access `new()`/`try_new()` on interval types (they're trait methods on `IntervalFinitePositiveLengthTrait`)
## Codebase Conventions
### Derive Macros (standard pattern)
```rust
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] // Basic types
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Getters)] // With getters
#[derive(Debug, Clone, Into, Getters, Serialize, Deserialize)] // With Into conversion
```
### Serde Bounds for Generics
```rust
#[derive(Serialize, Deserialize)]
#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
pub struct IntervalBounded<RealType: RealScalar, ...> { ... }
```
### Accessor Methods
- Use `getset` crate: `#[derive(Getters)]` with `#[getset(get = "pub")]`
- Return references for complex types, copies for primitives
- Use `.as_ref()` to access inner value of newtype wrappers
### Test Organization
```rust
#[cfg(test)]
mod tests {
use super::*;
mod native64_strict_finite { // Group by scalar type
type Real = RealNative64StrictFinite;
mod coords1d { // Group by functionality
#[test]
fn test_coords1d_creation() { ... }
}
}
#[cfg(feature = "rug")]
mod rug_tests { ... } // Feature-gated tests
}
```
### Documentation
- Every public item has doc comments with examples
- Examples are runnable (`cargo test` runs doc tests)
- Mathematical properties documented in `/// ## Mathematical Properties` sections
- Performance characteristics in tables
## Project Resources
- `README.md`: Quick start, feature overview, badges
- `TUTORIAL.md`: Step-by-step examples from basic to advanced
- `DECISION_GUIDE.md`: Decision trees for grid/scalar/interval selection
- `examples/`: Standalone programs (`coords1d_serialization.rs`, `generic_coords_in_domain.rs`, `interval_hull_generalized.rs`)
- `benches/`: Criterion benchmarks (`grid1d_benchmarks.rs`, `interval_benchmarks.rs`)