Conner Majic 92a417c102 ci: improve reproducibility and add dependency audit gate
Use lockfile-driven installs in Docker paths and add high-severity npm audit checks in CI while documenting local CORS policy for compose-based development.

Made-with: Cursor
2026-04-17 08:23:34 -06:00

Majic Rod Solver — Rod-string solver stack (math-first)

Deterministic C numerical core (FDM + FEA), Node API for XML and orchestration, TypeScript GUI for workflow. See AGENTS.md for agent rules and Agents/MATH_SPEC.md for equations and paper citations.

Contributor workflow: CONTRIBUTING.md · Security: SECURITY.md · History: 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:

  • 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:
git tag -a v0.1.0 -m "v0.1.0 — initial published snapshot"
git push origin v0.1.0
  1. 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.

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

cd solver-api && npm install && npm run dev
cd gui-ts && npm install && npm run dev
  • API: http://localhost:4400/health
  • GUI: http://localhost:5173

Run with Docker

make run          # or: docker compose up --build
make smoke        # requires API on 4400
make down

Validation

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)

curl -sS "http://localhost:4400/solve/default?solverModel=both" | jq '.schemaVersion, .runMetadata, .comparison | keys'

Predictive (POST XML)

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)

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:

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

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)

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

docker build -t rods-ci .
docker run --rm rods-ci
  1. AGENTS.md
  2. Agents/MATH_SPEC.md
  3. Agents/COMPUTE_PLAN.md
  4. docs/engineering/units.md
  5. docs/engineering/validation.md
Description
No description provided
Readme 172 KiB