Files
rods/README.md
2026-04-17 08:27:15 -06:00

273 lines
11 KiB
Markdown

# Majic Rod Solver — Rod-string solver stack
Deterministic **C** numerical core (FDM + FEA), **Node** API for XML and orchestration, **TypeScript** GUI for workflow. See **[AGENTS.md](AGENTS.md)** for agent rules and **[Agents/MATH_SPEC.md](Agents/MATH_SPEC.md)** for equations and paper citations.
Contributor workflow: **[CONTRIBUTING.md](CONTRIBUTING.md)** · Security: **[SECURITY.md](SECURITY.md)** · History: **[CHANGELOG.md](CHANGELOG.md)**
## Repository layout
| Directory | Purpose |
|-----------|---------|
| `solver-c/` | C: damped-wave FDM, dynamic bar FEM, diagnostic transfer, trajectory preprocess, **JSON stdin** drivers |
| `solver-api/` | Express: `POST /solve`, `GET /solve/default`, `POST /case/parse`, `POST /solve/validate-card`, XML → SI |
| `gui-ts/` | Vite + React tabbed case editor + uPlot dynacards |
| `data/cases/` | `base-case.xml` and regression inputs |
| `data/golden/` | Golden SHA-256 fingerprint for default solve regression |
| `Agents/` | `MATH_SPEC.md`, `COMPUTE_PLAN.md` (handoff) |
| `docs/engineering/` | Architecture, schema, units, validation |
| `references/papers/` | Literature citations and access notes for the solver math backbone |
## Vision
Build a transparent, deterministic rod-string and wellbore mechanics platform that is field-usable and research-grade.
- Physically explainable pumping-system simulations, not black-box outputs.
- Dynamometer cards as the primary interpretation surface.
- Inspectability of imported case data and solver assumptions.
- Multi-model validation (FDM vs FEA) with clear comparison metadata.
## Forge metadata (Gitea)
Repository **title**, **description**, **topics**, default branch protections, and **Releases** are configured in the Gitea UI (they are not stored as files in git).
Suggested values you can paste into the repo settings page for [connermajic/rods](https://gitea.majicmedia.ca/connermajic/rods.git):
- **Name / title:** `Rods` (or `Rods — rod-string solver stack`)
- **Description:** `Deterministic C core (FDM+FEA) + Node XML/SI API + TypeScript GUI for sucker-rod dynamometer workflows.`
- **Topics / keywords:** `sucker-rod`, `dynamometer`, `rod-pumping`, `finite-difference`, `finite-element`, `wellbore-trajectory`, `oil-and-gas`, `typescript`, `nodejs`, `c`
- **Website:** leave blank until you publish a demo URL
### Releases
Releases are optional until you want a downloadable snapshot. When you are ready:
1. Pick a version (for example `v0.1.0`).
2. Tag the commit on `main`:
```bash
git tag -a v0.1.0 -m "v0.1.0 — initial published snapshot"
git push origin v0.1.0
```
3. In Gitea: **Releases → New Release**, select the tag, paste notes from `CHANGELOG.md`, and attach artifacts if you build any.
## Prerequisites
- **Local:** `gcc`, `make`, Node 20+, `npm`
- **Docker:** Docker Engine + Compose plugin
## Run locally
### Build and run C solver (stdin JSON)
The API spawns `solver-c/solver_main` and pipes **one JSON object** on stdin (`schemaVersion: 2`). Legacy 9-argument CLI is **removed**.
```bash
gcc -std=c99 -I./solver-c/include \
./solver-c/src/solver_common.c \
./solver-c/src/json_stdin.c \
./solver-c/src/trajectory.c \
./solver-c/src/solver_diagnostic.c \
./solver-c/src/solver.c \
./solver-c/src/solver_fea.c \
./solver-c/src/solver_fourier.c \
./solver-c/src/main.c -lm -o ./solver-c/solver_main
gcc -std=c99 -I./solver-c/include \
./solver-c/src/solver_common.c \
./solver-c/src/json_stdin.c \
./solver-c/src/trajectory.c \
./solver-c/src/solver_diagnostic.c \
./solver-c/src/solver.c \
./solver-c/src/solver_fea.c \
./solver-c/src/solver_fourier.c \
./solver-c/src/main_fea.c -lm -o ./solver-c/solver_fea_main
./solver-c/test_solver
```
### API + GUI
```bash
cd solver-api && npm install && npm run dev
cd gui-ts && npm install && npm run dev
```
Optional API hardening for browser clients:
```bash
cd solver-api && CORS_ORIGINS="http://localhost:5173" npm run dev
```
- API: `http://localhost:4400/health`
- GUI: `http://localhost:5173`
## Run with Docker
```bash
make run # or: docker compose up --build
make smoke # requires API on 4400
make down
```
## Validation
```bash
make test # solver-api vitest + gui-ts tests + solver-c test_solver
make test-solver-sanitize # optional: ASan/UBSan build of solver-c test harness
./solver-c/test_solver
```
Golden hash: `solver-api` tests assert `/solve/default` body matches `data/golden/default.solve.sha256` (after normalizing `generatedAt`).
## API examples
### Predictive (default base case)
```bash
curl -sS "http://localhost:4400/solve/default?solverModel=both" | jq '.schemaVersion, .runMetadata, .comparison | keys'
```
### Predictive (POST XML)
```bash
curl -sS -X POST http://localhost:4400/solve \
-H "Content-Type: application/json" \
-d "{\"xml\": $(jq -Rs . < data/cases/base-case.xml), \"solverModel\": \"fdm\"}" | jq '.solver.pointCount, .schemaVersion'
```
### Extended physics outputs (profiles/diagnostics/fourier)
```bash
curl -sS -X POST http://localhost:4400/solve \
-H "Content-Type: application/json" \
-d "{\"xml\": $(jq -Rs . < data/cases/base-case.xml), \"solverModel\": \"both\", \"options\": {\"enableProfiles\": true, \"enableDiagnosticsDetail\": true, \"enableFourierBaseline\": true, \"fourierHarmonics\": 10}}" \
| jq '.solver.profiles.nodeCount, .solver.diagnostics.valveStates[0], .comparison.fourier.harmonics'
```
### Diagnostic (measured surface card)
Build `surfaceCard` from a predictive run or field data:
```bash
CARD=$(curl -sS "http://localhost:4400/solve/default?solverModel=fdm")
# Then POST xml + workflow + surfaceCard (see solver-api/tests/api.test.js)
```
### Surface card QA only
```bash
curl -sS -X POST http://localhost:4400/solve/validate-card \
-H "Content-Type: application/json" \
-d '{"surfaceCard":{"position":[0,1,2],"load":[10,11,12]}}' | jq .
```
### Parse XML only (no solve)
```bash
curl -sS -X POST http://localhost:4400/case/parse \
-H "Content-Type: application/json" \
-d "{\"xml\": $(jq -Rs . < data/cases/base-case.xml)}" | jq '.schemaVersion, .model.wellName, (.unsupportedFields | length)'
```
Returns `{ model, rawFields, unsupportedFields, warnings, schemaVersion: 2 }` — same shape as `GET /case/default`, but for an uploaded XML string. Used by the GUI to hydrate its case editor from an import.
## GUI tabbed case editor
`gui-ts` renders a tabbed UI backed by a single `CaseState` that round-trips to/from the XML document. On first load it pulls `GET /case/default` and populates every tab; editing any field mutates `CaseState`, which is serialized back into `<INPRoot><Case>…</Case></INPRoot>` on solve or export. Untouched XML fields (fatigue, IPR blocks, pumping-unit catalog keys, etc.) are preserved verbatim in the output.
| Tab | Contents |
|-----|----------|
| Well | Well name, company, units selection, pump depth, tubing anchor / size |
| Trajectory | Editable MD / Inc / Az survey table with vertical + deviated presets |
| Kinematics | Pumping speed (SPM), speed option, unit ID, upstroke/downstroke percentages |
| Rod String | Taper table (diameter, length, modulus, rod type) with base-case preset |
| Pump | Plunger diameter, friction, intake pressure, fillage option + percent |
| Fluid | Water cut, water SG, oil API, tubing gradient |
| Solver | `solverModel` (fdm/fea/both), workflow selector, damping + friction knobs, engineering checks gate, **Run Solver** |
| Results | KPI banner, uPlot dynacard (polished + downhole, with FEA overlay when applicable), FDM↔FEA comparison, warnings, unsupported-field list, 3D wellbore/rod/pump view with DLS or side-load contour, trajectory analytics table, pump diagnostics, export tools |
| Advanced / XML | File upload + paste box (POST `/case/parse`), export current state as XML, raw-field inspector |
### Built-in engineering checks
- **Pump depth vs total rod length:** solver run is blocked if the absolute mismatch exceeds **15 m**.
- **Trajectory integrity:** requires at least 2 stations and strictly increasing measured depth.
- **DLS warning threshold:** if max dogleg severity exceeds **15 deg/100**, the UI surfaces a warning.
These are fixed guardrails (not user configurable) to keep behavior deterministic and consistent across sessions.
### 3D wellbore visualization
The Results tab includes a 3D projected wellbore panel:
- Tubing trajectory polyline colored by DLS contour (green/yellow/red).
- **Bad sections** highlighted in red for DLS >= **15 deg/100**.
- Rod string overlay drawn from surface to rod total length with depth color gradient.
- Pump marker placed along trajectory at `PumpDepth`.
- Interactive controls: drag to rotate, `Shift+drag` to pan, mouse wheel / buttons to zoom, projection toggle (perspective/orthographic), and reset view.
- Overlay modes:
- `DLS`: uses fixed bad-section threshold `15 deg/100`
- `Side-load risk`: colors by normalized side-load profile returned from solver outputs (`options.enableProfiles=true`)
### Trajectory analytics + cross-highlight
- Results includes a per-segment trajectory table (`MD start/end`, `ΔMD`, `DLS`, severity).
- Clicking a segment row highlights the corresponding 3D trajectory segment.
- Clicking a segment in 3D highlights the corresponding table row.
- Filter toggle supports "bad sections only".
- Keyboard accessibility: segment rows are focusable and selectable with `Enter` / `Space`.
### Pump placement diagnostics
Results tab now reports:
- nearest survey station to pump depth,
- pump-to-station `ΔMD`,
- survey-end to pump `ΔMD`,
- rod-total to pump `Δ`,
- tubing-anchor to pump `Δ`,
with quick navigation buttons back to Well / Trajectory / Rod tabs for correction.
### Visualization artifact export
Results tab export buttons:
- 3D wellbore **SVG**
- 3D wellbore **PNG**
- run/check summary **JSON**
These are generated client-side from the rendered SVG and current run/check state.
### Diagnostic workflow (GUI wired)
- Kinematics tab accepts measured surface card points as `position,load` rows.
- `Validate Surface Card` calls `POST /solve/validate-card`.
- Solver tab `workflow=diagnostic` now sends `surfaceCard` to `POST /solve`.
- Solve calls include `options.enableProfiles=true` so side-load overlays can be rendered.
## Solver modes
| `solverModel` | Behavior |
|----------------|----------|
| `fdm` | Finite-difference damped wave + variable rod + trajectory friction |
| `fea` | 1D bar FEM + Newmark + Rayleigh damping |
| `both` | Runs FDM + FEA; returns `solvers` and extended `comparison` |
| `workflow` | Behavior |
|-------------|----------|
| `predictive` | Harmonic surface motion (unless overridden later) |
| `diagnostic` | Surface card BC; FDM in C; FEA uses bisection on pump load to match measured top load |
## Optional CI image
```bash
docker build -t rods-ci .
docker run --rm rods-ci
```
## Where to read next
1. [AGENTS.md](AGENTS.md)
2. [Agents/MATH_SPEC.md](Agents/MATH_SPEC.md)
3. [Agents/COMPUTE_PLAN.md](Agents/COMPUTE_PLAN.md)
4. [docs/engineering/units.md](docs/engineering/units.md)
5. [docs/engineering/validation.md](docs/engineering/validation.md)