WellSim Manual · user guide & equations reference ← Open the app

WellSim Manual

Nodal analysis, minimum connected reserves, and production forecasting for oil & gas wells — a web port of the field-proven M. El-Ashry Excel toolset, with the macros replaced by deterministic solvers and every formula pinned against the original workbook cells.

356 regression and security tests passing 43/43 sheet-parity sensitivities gas march bit-exact vs workbook port 3355 · no runtime dependencies

What’s built

ModuleOilGas
1 — Well model (nodal analysis) Natural ✓ · Gas lift ✓ · ESP full stack ✓ (123 pumps in 4 catalogues, matching, design proof) · Water Well tab ✓ (producer + injector) Natural ✓
2 — Reserve estimate ✓ Havlena–Odeh MB · static-gauge MB · reservoir limit ✓ p/Z prod data · p/Z from SITHP · reservoir limit · p/Z from memory gauges
3 — Forecast ✓ Tarner · Walsh generalized-MB (Rv) — saturation-tracking MB + nodal ✓ p/Z tank + nodal, history overlay
4 — Lift selection (artificial lift) ✓ ESP / Gas lift / Sucker rod / Jet / PCP — life-of-well envelope screen + UDC economics —

Project-wide principles: Darcy IPR is the default and dominant J for both fluids (the test J calibrates a matched permeability at your judged skin); two pressure records (Pri immutable anchor, Pr working); explicit physics only — Brill & Beggs Z everywhere, no goal-seek; well-head temperature always calculated (Ramey); gas impurities are inputs wherever gas properties appear.

Run & test

node src/server/server.js               # serves http://localhost:3355
npm test                                # 356 tests
node scripts/validation-sweep.mjs       # 43 module-vs-workbook sensitivity cases

The server uses only Node built-ins; the UI is plain HTML/JS (Plotly for charts). Documentation lives in the repo: README.md, docs/user-guide.md, docs/equations.md, docs/deploy.md. A standalone portable build (WellSim.exe, no install, cases saved beside the exe) is produced from this same source tree by build.ps1.

UI conventions

1625 Input — you type it
2059.9 Input-or-calculated — blank = program fills it (grey); typing reclaims it
3511.3 Output — read-only (the workbook’s pink cells)

Oil — Well model

Lift types: Natural flow · Gas lift · ESP (one active).

Water wells have their own tab — and their own section: Water — Well model.

Defaults

GroupFields (default)
Well & flowFTHP 700 psi · WC 50% · GOR 5000 scf/stb · ID 2.992 in · roughness 0.00006
Trajectorytop perf 2810 mAH · kick-off 1910 m · deviation 7°
Fluids & PVT46 API · γg 0.842 · Rsi 700 scf/stb · Tres 201°F · μ tubing 6 cp · water SG 1.05 · Pb blank = calc
Heat transfersoil 90°F · U 3 BTU/hr·ft²·°F · OD 3.5 in · Cp 0.51
IPR (Darcy)Pri 3550 · K 50 mD · H 42.653 ft · Re 1640.5 · Rw 0.5104 · skin 0
Test2100 stb/d @ 700 psi · Pwf blank = get Pwf
Gas liftinjection 2490.92 mTVD · sweep to 2 MMscf/d in 10 steps
ESPpump 2993.08 mAH (= 2985 mTVD on the demo trajectory) · ΔP 1325.16 psi (manual) · tubing gas blank = formation · the ESP demo case sets the pump at 2985 mAH (back on natural/gas lift)
Per-lift demo cases — the table above is the NATURAL-flow demo, and each lift type is a different source workbook: selecting Gas lift or ESP on a pristine form loads that workbook’s live case (ESP: FTHP 160, WC 5%, GOR 384, User PI 2.7 @ 2650, test 2565 @ 160, 145 stages @ 50 Hz, actual Pint/Pdis 1392/2720; Gas lift: FTHP 300, WC 25%, GOR 412, User PI 1.07897 @ 5000 — the workbook’s solved Jones J, whose matched K lands on its hand-tuned 16.37 mD). Typed values are never overwritten, and Natural swaps the table above back. The IPR basis selector (Darcy | User PI) drives it: in PI mode the J is your input and the matched K shows grey (derived); Darcy geometry becomes optional.

Workflow

  1. Solve well — IPR + VLP, operating point (rate, Pwf, calculated WHT), AOF, well-head PQ curve with WHT on the second axis.
  2. Calibrate from test — get Pwf marches the test Pwf from FTHP if blank; the Jones/test J is computed Pri-anchored; you judge skin (guidance table by drilling/completion method) and the program back-solves the matched K so J(Darcy) = J(test).
  3. Match head factor from test (under Match factors, all three tabs) — the Calibrate button matches the IPR; this one matches the march. It solves the Matching head so the marched pressure meets a typed measurement, with Matching friction held where you set it (1 = untouched); both are bounded to 0.8–1.2. Natural, gas-lift and water wells match the marched Pwf to the typed Test Pwf (a grey get-Pwf value is not a measurement); ESP wells match the marched discharge pressure to the measured Pdis and report Pint as a consistency check (Pint is Pdis minus the pump dP, so a large Δ points at the pump or its wear); gas wells use the first test row. A target the bounds cannot reach is pinned at the bound and flagged — “check friction, PVT or the test rate” — never silently accepted. Missing inputs are named. The matched factor is written into the input; re-solve to use it.
  4. Sensitivities — VLP parameter sets (blank = base) and future pressures (0.75/0.5/0.25 × the current Pr, refilled as you edit Pr or Pri unless you have typed your own) with the future-J chain. The VLP columns follow the fluid and the ACTIVE LIFT TYPE: oil natural FTHP · GOR · W.C · tubing ID; oil gas lift + injection gas; oil ESP + frequency (Hz); water natural FTHP · tubing ID; water gas lift + injection gas; water ESP + frequency; water injector injection THP · injected-water temperature · tubing ID. Typed values survive a lift switch. The chart carries the current-Pr IPR (heavy teal) — the curve every solution is solved against — then the future-pressure family. Pri is not drawn: on a depleted well it is history, and a second high curve only competes with the one the answers belong to (the pressure AXIS still tops out at Pri, so runs stay comparable). Each set’s solved NODE is marked with a diamond, and a set that changes the water cut also gets its own thin IPR, because oil rate = gross × (1−W.C) gives it a different curve in rate terms. Every node is therefore a visible intersection of two drawn curves, and the numbers match the nodal-solution table beneath the chart, which every module carries.
  5. Sensitivity pressure axis — the IPR/VLP sensitivity chart tops its pressure axis at Pri on oil, water and gas wells: the inflow side can never exceed the initial reservoir pressure, and the VLP tail that does (high rates the well could never reach) would otherwise stretch the scale and squash the families. The injector keeps an auto axis — injection deliberately pushes ABOVE Pri, so capping it would clip the very curves it exists to show.
  6. Pump @ solution point — wherever an ESP node is solved (the model match AND every sensitivity case) the pump’s full state is reported in one identical parameter set: pump · stages · frequency (with the curve’s reference frequency) · wear · head total and per stage · ΔP · rate at the pump before and after separation · composite gradient · free gas at intake · separator efficiency · thrust with its down/BEP/up window · hydraulic power · ΔP convergence. In the sensitivity view the sets sit side by side, one column each, so the trade-off reads straight across.
  7. Every sensitivity set is solved — a table under the chart gives each VLP set its own NODAL SOLUTION against the current IPR (rate, Pwf, WHP, calculated WHT). On ESP wells it also carries the frequency, solved ΔP, head, intake/discharge and thrust per set, and two more charts appear: the pump curve per sensitivity (each set at its own frequency, node starred, over the shared thrust envelope) and the traverse per sensitivity (each set’s top-down march and IPR back-calc with its ΔP step at pump depth). Oil and water alike.
  8. ESP frequency sensitivity — with a pump selected (oil or water — both tabs read the same four catalogues) the coupled ΔP is re-solved per rate at each frequency (affinity-scaled curve, intake state and all); with Manual ΔP the quoted ΔP is scaled by the affinity law (f/f₀)². Beyond the pump’s flow range the head is zero, so the frequency curves legitimately merge.
  9. Injector sensitivities — the injector has its own family: available BHIP vs rate per set against the injectivity line at future pressures. Injection water is incompressible with a fixed viscosity, so its temperature moves the BOTTOMHOLE TEMPERATURE, not the pressure — the BHT families are drawn on the second axis.
  10. Gas-lift performance — injection sweep, optimum, incremental dQ/dInj.
  11. ESP — the full pump stack: four pump catalogues in the background, 123 pumps, picked in two steps — catalogue, then pump with add-new-pump (custom per-stage curve — its thrust-marker rows are coloured: row 4 blue = down-thrust limit, row 6 gold = BEP, row 8 red = up-thrust limit, the fixed rows the physics reads) and Manual ΔP fallback; affinity scaling 30–60 Hz with the down-thrust/BEP/up-thrust envelope; intake free-gas block with the default-ON gas separator (95%, “Separator required” above 10% free gas, tubing GLR cut by the BE74 fraction); the coupled solve closes ΔP→march→intake→head→ΔP and matches the bottom-up traverse (Pwf from the IPR at CONSTANT Pres) to the top-down one at the intake — PI is the IPR match factor. Match stages (first run, new pump, wear 0) solves the installed stage count with a DESIGN PROOF: all stage-dependent march pressures must stay above the design floor (input, default 300 psi) — the minimum binds at the pump intake, and a match that would starve it is capped at the stage count where intake = floor, and the anchor Pwf is written grey into the test-Pwf cell (the workbook macro’s C22 — overwrite it to anchor the match on a measured value); Match wear (actual Pint/Pdis) applies the wear factor only — the implied PI and matched K are reported as QC, never written to your inputs (on the demo’s actual gauges the QC recovers the workbook’s PI 2.7 as 2.698); Match separator efficiency (actual Pint/Pdis) is the wear match’s sibling — the same measured ΔP solving the OTHER unknown: wear is held at its input (0 = new pump) and the gas-separator efficiency is solved by Brent on 0–100% so the theoretical ΔP at the measured intake state reproduces the measured one (one measured ΔP fixes one unknown — match η at start-up on a new pump, then hold it and match wear over the run life). It draws the η-sweep (pump ΔP vs separation, the measured ΔP as a dashed line, the gas-locked band shaded — the separation below which intake gas runs Qgross@pump off the curve and the pump delivers no head), reports free gas before/after separation, and on success writes the matched efficiency into the separator input so every downstream run uses it. An optional test Pwf measured at the perfs anchors PI and turns the back-march from Pint into a consistency check. Out-of-range cases are named: a measured ΔP above the 100% curve cannot be gas (check stages, frequency or PI); below the 0% curve it is wear, or worse. On the demo gauges at wear 0 it recovers the workbook’s 95% as 95.16%. Two ESP views: Model match with the FINAL charts (IPR vs the coupled ESP-VLP nodal plot, wellhead PQ & WHT, PumpCurve with the results block beside, and the Traverse on its own row with measured markers — also shown in Manual-ΔP mode, where the input ΔP appears as the highlighted step at pump depth); and Sensitivity — future-Pres cases at 0.9 / 0.8 / 0.7 × Pr (shallower than the 0.75/0.5/0.25 used elsewhere, because a pump still has to lift at modest depletion), each FULLY solved: the Darcy future J, then a complete coupled ESP solve. The chart draws the current-Pr IPR (heavy teal), each case’s IPR and the coupled ESP VLP — one curve, since pump, tubing and THP do not change with Pr — so every solved node is a visible crossing marked by a diamond, with the pressure axis topped at Pri as elsewhere. The table lists the ESP state AT that node — rate, Pwf, Pint, Pdis, ΔP, head, Qg@pump, gradient, free gas %, WHT and thrust — which is how you see the pump working harder and the intake falling as the reservoir depletes. ESP cases also appear in the main VLP/IPR sensitivity block (frequency column, coupled solve per set), but that block solves at the CURRENT Pr only.
Multi-layer IPR (optional, oil & gas) — a Layers selector (Single default | Multi-layer): each row has an Active box (unticked, the zone is left out of the composite entirely; at least 2 must be ticked) and a Zone name (defaults Layer1…Layer4, editable; it labels the layers table and the IPR/VLP legend on every lift; cleared to blank it shows as Layer<row>, so names stay put when a zone between them is switched off). By default only the first two zones are active; then per-layer K/H/skin/Pr (Re/Rw & Pb shared) + per-layer fluids (blank = base), each layer’s Darcy J with its own μ·B (oil) / μ·z (gas) at the layer’s Pr. Layers collapse to one final J (training deck 4): oil via the theoretical average pressure + solution-point match; gas exactly (J_t = ΣJᵢ, PrAvg = √(ΣJᵢPrᵢ²/ΣJᵢ)). Blended ratios drive the marches; a Layers-@-operating-Pwf table shows each layer’s contribution with crossflow flagged. Every well-model route solves on the composite — Solve well on any lift, Calibrate, the gas-lift curve, the sensitivities and the head / ESP matches (until 30 Sep 2026 only Solve well and Calibrate did); the reserve and forecast modules stay single-layer by design. Darcy basis only (C&n has no exact collapse). Fit to test: with Multi-layer active, Calibrate also fits the commingled system — every layer K is scaled by one closed-form factor λ = J(test)/J(total) so the total J lands exactly on the Jones/test J (layer K ratios preserved; solved K’s written back into the table). Sensitivities stay single-layer.
Pump catalogues — pump selection is two steps, catalogue then pump, so a long list never hides the pump you want. Four catalogues, 123 pumps: Original catalogue (oil + water ESP), the 69 WD/WG/WE/FLEX/ESP-B pumps from ESP_DataBase; Borets 2015 (13); SLB REDA 2020 (24); Novomet (17). The three vendor catalogues were recovered from the vector geometry of the vendor curves, so each pump was cross-checked against evidence the tracer never read — Borets against the workbook pumps it shares model names with (~3% on head), SLB and Novomet against each page’s own printed Recommended Operating Range, whose rate axis is calibrated without reading that range so the check stays independent. Pumps that failed the check were left out rather than shipped unproven (Novomet ships 17 of 42 model+design pairs; 11 of the rest are unrecoverable because their axis digits are vector outlines with no numbers left to calibrate against). Manual ΔP and Custom pump are not catalogue entries and stay in the list whichever catalogue is selected. A saved case stores the pump, never the catalogue — the catalogue is derived from the pump name on load, so cases saved before catalogues existed still restore.

Water — Well model

Well type: Producer | Injector · producer lift types: Natural flow · Gas lift · ESP.

The oil-tab structure without the Reserve/Forecast modules (lift types, get-Pwf calibration and sensitivities included), running the same modified-Griffith march at its limiting case: API fixed at 10° (SG = 1.000 exactly), w.c. 100 %, GOR = Rsi = 0, Pb = 0. Rates are gross water (bbl/d); the IPR is built in as the pure linear Darcy form (no Vogel curvature) with the Darcy J on water properties (μ = 0.5 cp, Bw = 1); future-pressure IPR sensitivities keep J constant. Defaults: FTHP 200 psi (producer) · 2000 bbl/d · Pri 4800 psi. Head-dominated: it flows naturally only when Pr beats THP + the static water column (≈ 0.446 psi/ft at SG 1.05) — otherwise “no-intersection” is the physics, not an error.

Defaults

GroupFields (default)
Well & flowFTHP 200 psi (producer) · injection THP 2000 psi (injector) · ID 2.992 in · roughness 0.00006
Trajectorytop perf 2810 mAH · kick-off 1910 m · deviation 7°
Fluidswater SG 1.05 · lift-gas SG 0.842 · Tres 201°F · injection water temp 90°F
Heat transfersoil 90°F · U 3 BTU/hr·ft²·°F · OD 3.5 in · Cp 0.51
IPRbasis Darcy | User PI · Pri 4800 · K 50 mD · H 42.653 ft · Re 1640.5 · Rw 0.5104 · skin 0 · fracture gradient 0.7 psi/ft (injector parting check; blank = off)
Test2000 bbl/d @ 200 psi · Pwf blank = get Pwf
ESPpump 2993.08 mAH · 145 stages @ 50 Hz · wear 0 · no separator (water is gas-free) · Manual ΔP 1325.16 psi
Water ESP — the water tab drives the same four catalogues as the oil tab: select a pump and the ΔP is SOLVED from its curve (stages × affinity × wear) rather than typed, with the head and the down-thrust/BEP/up-thrust window reported at the operating point, and Match stages solving the stage count against the same design-floor proof. Water is gas-free, so there is no free-gas intake block and no separator — the pump curve works on the water volume at the intake. Manual ΔP stays in the dropdown for a pump known only by its ΔP (a frequency sensitivity then scales it by (f/f₀)²), and Custom pump (add new)… takes a per-stage curve exactly as on the oil tab — same 11-row table, same coloured thrust-marker rows. The water tab also carries the IPR basis selector: User PI types J directly on the linear water IPR (Pb = 0), matched K grey, Darcy geometry optional. Solving draws two extra charts: the PumpCurve (30–60 Hz family, the down-thrust/BEP/up-thrust envelope and the operating point starred, with the ESP results block beside it) and the Traverse (top-down vs the IPR back-calc, with the pump ΔP step at pump depth).
Injector — the Water Well tab has a Producer | Injector selector. An injector has no FLOWING tubing head pressure — every surface pressure is the injection THP, through the marches, the injectivity IPR and the sensitivity columns (inputs read Injection THP / Test injection THP, the sensitivity column Inj THP psi). A fracture gradient input (psi/ft, default 0.7 — blank disables the check) gives the formation parting pressure at the perforations. When the solved BHIP exceeds it the summary says so and reports the injection THP that would land exactly on parting, solved by re-running the nodal solve at trial THPs; if no THP both injects and stays below parting, it says that instead. The control is the surface pressure, not the rate — available BHIP = THP + head − friction is HIGHEST at low rate, so injecting less raises the bottomhole pressure rather than lowering it. Sensitivity defaults: injection THP 100 / 1000 / 2000 psi and future pressures 0.75 / 0.5 / 0.25 × the current Pr. Running them adds the Injectivity sensitivities chart — each THP set solved against Pri AND each future Pres (a full nodal grid), plotted as injection THP (y) against the solved injection rate (x), one line per reservoir pressure, with the grid tabulated beneath. The injector reverses the march (water flows DOWN: BHIP = injection THP + head − friction) — selecting it swaps the THP default from 200 to 2000 psi (and back on Producer) — and relaxes temperatures top-down from the injection-water temperature (input, default 90°F) toward geothermal — the bottomhole injection temperature (BHT) is the calculated output. Injectivity IPR: q = J·(Pwf − Pr) with the same water Darcy J; operating point = available BHIP (falls with rate) crossing Pr + q/J (rises). Charts: injectivity nodal + injection-THP-required vs rate with BHT on the second axis. Calibration from an injection test (BHIP input-or-marched → J injectivity → matched K). Lift hides for the injector; the sensitivities switch to the injector’s own set (injection THP · injected-water temperature · tubing ID). Its results carry only three result rows — injectivity node, available-BHIP sensitivities and injection-THP-required — the gas-lift, pump-curve and pump-traverse rows belong to producers and are cleared; switching well type wipes the previous type’s results so nothing stale is read as the injector’s. When THP + head cannot reach Pr the THP deficit is reported. Laminar Fanning 16/Re below the transition, Chen above.
IPR basis — the same Darcy | User PI selector as the oil tab: in PI mode you type J directly on the linear water IPR (Pb = 0, no Vogel curvature), the matched K shows grey (derived from your J at the water μ = 0.5 cp, Bw = 1), and the Darcy geometry becomes optional. There is no water source workbook — this tab is WellSim’s own extension of the oil march at its limiting case, pinned by its own tests.

Oil — Reserve estimate

1
Prod data & macro

Per-row Pwf (input-or-march) → Pr by composite-Vogel inversion iterated with J_2 → Havlena–Odeh MB. Headline N = AVERAGE(F/Eo); slope of F vs Eo as cross-check; F–Eo crossplot + N vs Np charts.

Model column (per row): which well model solves that date’s Pwf and which J backs out its Pr. Natural — plain march, Darcy future J. Gas lift — lifted march at the row’s own GL inj (blank = panel), Pr with the typed PI. ESP — coupled pump solve at the row’s own Hz (blank = panel), Pr with the typed PI. User — type both Pwf and Pr. New rows take the active lift type; a change fills down. A row naming a model the panel cannot supply stops with that row named. The Forecast continues the well as the last modelled row.

2
Static Pres history

Measured memory-gauge pressures typed directly (Date | Pres) — no IPR/VLP anywhere. Np/Gp from the prod-data cumulative. The strongest data when surveys exist.

3
Reservoir limit

m = −slope(Pwf, t), Ct = Cg·Sg+Co·So+Cw·Sw+Cf, STOIP = q̄/(Ct·m). Defaults Sg 0.1 / So 0.8 / Sw 0.15. Early lower bound.

Keep the window early and short. This is an early-production method — on an undersaturated pressure path Eo is compressibility-only, and years-apart rows legitimately blow N up. Demo data (days apart) gives 14.7 / 91.9 / 1.5 MMstb across the three selections — the spread is the tool exposing input uncertainty; judge each method by its own inputs.

Gas — Well model

Defaults

GroupFields (default)
Well & flowFTHP 1625 psi · CGR 57.436 · WGR 3.846 stb/MMscf · ID 2.992 in · base roughness 0.0021 in
Trajectorytop perf 3013 mAH · kick-off 690 m · deviation 23.65°
Fluids & PVTcond. 48.7 API · γg 0.763 · N₂ 1.2% · CO₂ 3% · H₂S 2 ppm · Tres 232°F · μcond 2 cp · σ 30 dyn/cm
ReservoirPri 3800 psi · Pr blank = Pri
IPR (Darcy)K 5 mD · H 80 ft · Re 1640.5 · Rw 0.5104 · skin 0
Multi-rate test2440/5.192 · 2000/10.002 · 1625/14.137 (psi / MMscf/d), Pwf blank = get Pwf

IPR basis (one active): Darcy Pr² (default, dominant) or C & n (grey = fitted from the test). Calibration fits C&n, computes the test J, and matches Darcy K at your judged skin. Impurities feed the sour pseudo-critical route everywhere.

The reserve survey tables — oil memory-gauge, gas SITHP and gas memory-gauge — take Paste from clipboard, Import CSV, +10 rows and Clear, and grow to the data just like prod_data. Clipboard cells are read in the table’s own input-column order; a first line starting with a letter is treated as a header and skipped (a 17-Nov-14 date is not).

Gas — Reserve estimate

1
Prod data & macro

Per-row Pwf (input-or-march) → Pr = √(1000q/J + Pwf²) → explicit Z → Gp trapezoid → p/Z vs Gp line → minimum connected GIIP.

Model column (per row): Model — the workbook’s row: Pwf typed or marched from FTHP, Pr backed out of the IPR (a Pr left in the cell is replaced). User — type both Pwf and Pr, as from a build-up gauge on that date; no march, no IPR. Rows are independent — nothing fills down. A User row missing either value stops with that row named. The same rows feed the reservoir limit and the Forecast’s start state.

2
Pres from SITHP

Static shut-in surveys — Date | STHP | SITHT | CGR | WGR; no rate column, a shut-in row is q = 0 by definition. Pres by the static gas march: gas-head-only stations on the geothermal profile from SITHT at the head to Tres at the perfs, per-station Z on that line — so the shut-in head temperature shapes the whole column, not an average (1.2% vs the workbook case). SITHT blank = the well-model soil temp, shown grey. Gp from prod cumulative; no IPR/VLP.

3
Reservoir limit

GIIP = q̄/(Ct·m)/1000. Defaults Sg 0.85 / So 0 / Sw 0.15; Cg grey = from two Bg points; slim table + Pwf-decline chart.

4
Pres from memory gauges

Measured static Pr — no march, no IPR: the gauge reading IS the datum. Inputs Date | Pr; calculated z | Gp | p/Z. Each reading must be a stabilized build-up corrected to datum. Defaults are the pressures selection 2 computes, so the two routes cross-check (both 123.80 Bscf).

Oil — Forecast (Tarner | Walsh)

Saturation-tracking material-balance forecast with a Method selector (one active). N and the start state (date, Np, Pres) are grey input-or-calculated, chained off the Reserve module — the date defaults to the last prod-data date, and both methods book an anchor row at that date so the curve begins there and joins the history. The forecast stream continues the well as last measured: Forecast THP, Forecast W.C and Forecast GOR are grey input-or-calculated, defaulting to the last prod row and overridable. All three are wellbore conditions held for the whole run — the GOR feeds the lift march every step, so a well producing free gas keeps that gas helping it flow, while the charted F GOR stays the MB reservoir GOR; both methods share the same inputs and the same Pwf source selection: nodal at forecast THP (VLP-coupled, default) or fixed minimum Pwf. Per step, both solve the same two residuals deterministically: pressure closes the gas balance (Gp by GOR trapezoid = Gp from MB), saturation closes the MB saturation; rel-perm from the sheet’s Kro(So)/Krg(Sg) polynomials; Brent/alternation to convergence instead of GoalSeek loops, the rate coupled to the trial pressure. PVT (Rs/Bo/Bg/μ) is evaluated at the trial pressure inside the solve — the training-material policy (“all PVT data evaluated at the assumed reservoir pressure”).

1
Tarner (default)

The classic method (workbook Tarner sheet): standard MB, So = (1−Swi)(N−Np)Bo/(N·Boi(1−ctΔp)), GOR = Rs + (Krg/Kro)(μoBo/μgBg), rate from the mobility PI J1 = 0.00708Kh/(ln(Re/Rw)−0.75+S).

2
Walsh (generalized MB, Rv)

Walsh’s generalized material balance (PETSOC 95-01-07) with the volatilized oil-gas ratio Rv(P): (Bg−Rv·Bo) terms in both the Gp and So equations, Foo-corrected GOR, and the CONSTANT calibrated PI on total mobility (qt = J·λt·Δp, qo = qt·λo/(λt·Bo)). Rv from the workbook’s tuned polynomial. Quirks preserved: μg = μo (the sheet’s Gas-Visc column reads the oil-viscosity cell); Bg in cf/scf.

Defaults: step 30 d · THP, W.C and GOR from the last prod row (300 psi / 60% / 5000 scf/stb on the demo well) · min Pwf 500 · Swi 0.15 · Cw 2.63e-6 · Cf 3.25e-6 · abandonment 50 stb/d · max 60 steps. Validation: 7 cell-pinned tests reproduce the Walsh sheet’s first solved row to 9+ digits; replaying the sheet’s Pwf column tracks its 64-step trajectory within ≈1% on pressure (EUR −2.2%, attributable to the sheet reading PVT from its parallel Tarner column). Chart: history + forecast (rate, GOR, Pres) on a calendar axis, table below.

Gas — Forecast

p/Z tank coupled to the nodal model, chained off the end of history: start date / start Gp / start Pres are grey input-or-calculated (last prod date, cumulative, minimum solved Pres). Defaults: step 30 d · FTHP 300 psi · plateau 12 MMscf/d · abandonment 1 MMscf/d · max 60 steps · GIIP & pᵢ/Zᵢ grey = from the reserve fit.

Where GIIP comes from — blank condensate SG = 141.5/(131.5+API) and MW = 42.43·SG/(1.008−SG) are reported on the reserve and forecast summaries and feed the gas-equivalent balance: GE = 133.01·SG/MW Mscf/STB, condensate in Bscf = MMstb × GE, Gp total = Gp + condensate equivalent, and the p/Z line is fitted on Gp total on every route (the forecast depletes on the same basis, and its chart, start line and EUR are reported on Gp total); every reserve route (prod data, SITHP, reservoir limit, gauges) reports a condensate cumulative beside Gp — and a condensate rate where a row has one — without touching the p/Z fit; a grey Forecast CGR (blank = latest CGR in the prod table, override by typing) gives a condensate rate = q × CGR and a condensate cumulative that continues from the history, both as forecast-table columns; GIIP and pi/Zi chain off Reserve selection 1 (prod data), the workbook’s own chain. It does NOT follow the reserve selection currently shown: on the demo that is 182.26 Bscf from prod data vs ~120 Bscf from SITHP or the gauges. The field label and the result line both say so; type a value to override.

Each step: Pr from the p/Z line (first step anchors on start Pres) → Darcy IPR at Pr → nodal operating point at forecast FTHP → q = min(op, plateau) → Gp advances. Terminates on abandonment / depletion / dead well / max steps, reporting EUR and recovery %. The chart overlays history + forecast on a calendar-date axis; the results table sits below it.

Oil — Lift selection (artificial lift)

A first-pass screening advisory that ranks five artificial-lift methods — ESP · Gas Lift · Sucker Rod · Jet Pump · PCP — for a well described at three life snapshots — Initial, half the horizon, and the horizon, which the table header lets you set at 1, 2, 3 or 4 years (the workbook’s case is Initial / +6 months / +1 year); the middle column’s label follows at half, and changing the horizon re-screens at once. It ports the “Artificial Lift Method Selection” workbook: a technical envelope screen, then a UDC economic screen, then the analyst’s call. Reached from the Oil tab’s module selector (Well model / Reserve / Forecast / Lift selection); it computes on open and re-runs on any edit.

Technical screen (life-of-well). Each snapshot’s eight parameters — gross rate, to-perf. depth (m), GLR, WHP, water cut, GOR, max well deviation, max dog-leg — are tested against each method’s global Min–Max envelope bands (the workbook’s limits sheet, version-stamped on every run). A method is technically applicable only when it passes every parameter at every snapshot; the matrix marks each cell pass / partial (passes at some snapshots) / fail, so a method that is fine now but marginal later stays visible. The four envelope charts plot the well’s design line (the three snapshots) against every method’s box, grouped as the workbook’s four levels — L1 Depth (m)+Rate, L2 WHP+GLR, L3 W.C+GOR, L4 Max deviation+Max dog-leg. Four inputs are life-constant and are typed once, under Initial: the bubble point, the to-perf. depth, the max well deviation and the max dog-leg belong to the fluid and the wellbore rather than to time, so the two later columns carry the Initial value (shown greyed) and the handler screens the same constant at every snapshot. Two of the eight are calculated, not typed, and are shown as live grey rows above the inputs that follow as you type: the gross rate is the composite-Vogel Q from Pwf/Pr/Pb/J, and the GLR follows from the GOR and the water cut — GLR = GOR × (1 − W.C/100), since the GOR is gas per barrel of OIL and the GLR gas per barrel of TOTAL liquid, so a typed GLR could only contradict its own inputs. The workbook’s own figures are exactly this: 400 × 0.98 / 0.80 / 0.50 = 392 / 320 / 200 across the three snapshots, so deriving it reproduces the sheet rather than departing from it.

Economic screen (UDC). The cumulative oil over the chosen horizon is the trapezoid of the oil rate (Q·(1−W.C)) at the three snapshots — the same shape at every horizon, so a 2-year horizon on the same three rates integrates to exactly twice the 1-year cum — the well’s production, so the same cumulative applies to every method. Technical acceptance comes first: only the methods that survive the envelope screen and this well’s conditions are costed at all — a method that cannot be run on the well has no economics worth reading, and pricing it would invite a cheap-but-undeployable option to sit beside the real candidates (the sucker rod is the cheapest of the five on the demo, and rightly never appears). The dropped ones are listed under the table as not costed — technically rejected first, naming which of the two reasons applied, so nothing is silently absent. Each survivor’s UDC = capex / horizon cum OIL + opex, in $ per barrel of oil; the economical method is the cheapest of them at or under the UDC limit. The denominator is oil, never gross liquid — on the demo well that is 369,581 stb against a gross 427,963 stb, and costing on gross would quietly understate the ESP’s UDC by 0.18 $/bbl, by more as the well waters out. Capex is a per-method input; ESP and Gas Lift are engine-backed elsewhere in WellSim, the other three are marked “clipboard” (analyst figures). A per-method cumulative may be pasted to override the common one.

Gates. Four tick-boxes rule a method out on top of the envelope screen. Three of them stop a method for a facility or a metallurgy reason rather than an operating envelope; the fourth is the analyst’s own judgement. In every case the reason travels with the exclusion: no gas compression nearby rules out Gas Lift (it has no source of injection gas — removable by tying in compression); the well flowing naturally rules out Sucker Rod (a rod pump belongs on a well that can no longer flow unaided); and high H2S/CO2 rules out both Sucker Rod and PCP — sour service attacks the rod string by sulphide stress cracking and the stuffing box, while H₂S and CO₂ swell and harden the PCP’s stator elastomer until it chunks (removable by an elastomer qualified for that gas composition). The jet pump is deliberately NOT excluded by sour gas: the published screening tables rate it well on corrosion — no moving parts downhole, a carbide nozzle and throat, and inhibitor can ride down continuously in the power fluid — and its real weakness, free gas at the throat, is already screened by the GLR band. What sour gas does raise for a jet pump is a surface-facilities question, sour returns through an open power-fluid loop, so it travels as a named warning beside the result rather than as an exclusion. The fourth box, Exclude jet pump, is not a well condition at all but the analyst’s own call, and it says so: a jet pump lifts by momentum exchange between a high-velocity power-fluid jet and the produced fluid, and that mixing is irreversibly lossy — overall efficiency runs about 20–30 %, against 50–60 % for an ESP or a rod pump — so the power fluid must be pumped at high rate and pressure, buying a surface plant (HP triplex pumps, power-fluid tankage and treating, an extra string or flowline) and the energy bill with it, while the throat needs enough intake submergence to stay off its cavitation limit. Ticking it excludes the jet pump with that reason attached; unticking screens it again. The two kinds of exclusion stay distinguishable, and nothing is removed silently: the matrix verdict reads PASS, out (outside its envelope) or excluded (clears its envelope, ruled out by this well’s conditions or by the analyst), the reasons are listed under the matrix, and a method that clears its bands only to be gated is named in the warnings — the actionable case, since compression can be installed where sour service cannot be typed away. The economics may only choose from what survives both: on the demo well, clearing the gas-compression box rules Gas Lift out and the pick falls to the Jet pump at 3.79 $/bbl.

Advisory, not a design. Engineering-class screening: the bands are heuristic, not physical limits, and passing the screen does not guarantee a feasible detailed design — the final method is the analyst’s (as in the workbook). Qgross reuses the same composite-Vogel IPR as the well model, so no physics is duplicated. Demo well at the default 1-year horizon: applicable ESP + Gas Lift + Jet, cum oil 369,581 stb, economical method Gas Lift at 3.41 $/bbl oil; at 2 years the cum doubles and every UDC’s capex term halves. The horizon stretches the three rates you typed; it does not forecast them. Moving it to 4 years spreads the same Initial / middle / final rates over four years rather than projecting a decline — so type the rates you expect AT those dates, or the longer horizon will simply flatter every method equally.

Constants & conversions

ConstantValue
scf → m³0.02831684639
bbl → m³0.158987304
Water density998.9926968 kg/m³ ≡ 62.36509524 lb/ft³
psi → Pa1 / 0.000145037738
π (trajectory cosine)22/7  (Excel legacy, preserved)
π (areas)3.14
Pb calibration constant2.1045604254721

Oil PVT (Standing-metric family)

With TC = (T−32)·5/9, ρₒ = 141.5/(131.5+API)·998.9926968 kg/m³, rs in m³/m³.

Pb [psi]  = 125·[ (582·rsi/γg)^0.83 · 10^(0.00164·Tc − 1768/ρo) − 1.4 ] / 14.5 · 2.1045604254721
Rs(P<Pb)  : rs = (γg/582) · [ (8e-6·P_Pa + 1.4) · 10^(1768/ρo − 0.00164·Tc) ]^(1/0.83);  Rs = Rsi above Pb
Bo_sat    = 0.9759 + 12e-5 · (177·rs·√(γg/ρo) + 2.25·Tc + 40)^1.2
Bo(P>Pb)  = e^(3e-6·(Pb−P)) · Bo_sat(Rsi)
μ_od (Glaso)      = 3.141e10 · T_F^(−3.444) · (log10 API)^a ,  a = 10.313·log10(T_F) − 36.447
Beggs–Robinson    : A = 10.715·(Rs+100)^−0.515 ,  B = 5.44·(Rs+150)^−0.338 ,  μo = A·μod^B
Vasquez–Beggs P>Pb: μ = μob·(P/Pb)^m ,  m = 2.6·P^1.187·10^(−3.9e-5·P − 5)

Gas PVT

Sweet pcrits (oil family): Tpc = 169 + 314·γg     Ppc = 708.75 − 57.7·γg
Sour route (gas wells)   : γhc = (γg − 0.9672·yN2 − 1.5195·yCO2 − 1.1762·yH2S)/(1 − Σy)
  Tc_hc = 187 + 330·γhc − 71.5·γhc²      Pc_hc = 706 + 51.7·γhc − 11.1·γhc²
  Kay mixing (+227.3/547.6/672.4 T; +493/1071/1306 P) + Wichert–Aziz ε correction
Brill & Beggs Z (explicit — the ONLY Z model):
A = 1.39·(Tpr−0.92)^0.5 − 0.36·Tpr − 0.101
B = (0.62−0.23·Tpr)·Ppr + (0.066/(Tpr−0.86) − 0.037)·Ppr² + 0.32/10^(9(Tpr−1))·Ppr⁶
C = 0.132 − 0.32·log10(Tpr)         D = 10^(0.3106 − 0.49·Tpr + 0.1824·Tpr²)
Z = A + (1−A)/e^B + C·Ppr^D
μg = μ_base(γg,T)/Tpr · exp( Dempsey poly16(Ppr,Tpr) )   (a0 = −2.462 … a11 = 0.00441, sheet column V)
ρg = 28.97·γg·P / (Z·10.73·(T+460))  [lb/ft³]
b (march)       = 0.0283·Z·(T+460)/(P+14.5)
Bg (res. limit) = 0.00504·5.61·(T+460)·Z/P          [cf/scf]
Bg (oil MB)     = 0.0283·Z·(T+460)/(P+14.5)/5.615    [bbl/scf]

Quirk preserved: the oil family anchors Tpr at station 1; the gas march uses local Tpr.

Trajectory, temperatures & marches

TVD = kickoff + (AH − kickoff)·cos(θ·(22/7)/180)
  — also places the ESP pump: its setting depth is entered as MEASURED
    depth (mAH) and converted to TVD on this trajectory
Ramey WHT (always calculated):
  K10 = (OD_ft·3.14·U)/((w/24)·Cp)      K11 = e^(−K10·L₁)   (L₁ = FIRST station spacing — sheet quirk)
  T_shelf(x) = T_soil + (T_res−T_soil)·TVD(x)/TVD_tot
  bottom-up: T(i) = T_shelf(i) + (T(i+1) − T_shelf(i))·K11
Chen friction (Fanning):
  1/√f = −4·log10[ ε/3.7065 − (5.0452/Re)·log10( ε^1.1098/2.8257 + (7.149/Re)^0.8981 ) ]
dP_fric/dx = (1/144)·f·w²/(7.413e10·d_ft⁵·ρ_mix)·matchFriction
Head acts on ΔTVD, friction on ΔAH — explicit Euler, 25–30 stations, no iteration.

Oil march (modified Griffith) — one march for natural / gas lift / ESP:

E_L = 1 − 0.38·(1 + v_m/0.8 − √disc)                      (Griffith, author's 0.38)
Ashry head factor (applied to the hydrostatic term):
  f_WC  = 7.27117418e-6·WC² − 1.19359442e-3·WC + 1.00062546
  GOR≤400   : f_GOR = −6.59987105e-7·GOR² + 3.56896045e-4·GOR + 1.02495053
  400–5000  : f_GOR =  1.97914840e-8·GOR² − 1.61983769e-4·GOR + 1.09560265   (capped at 5000)
  F_head = (f_GOR·f_WC)^0.82

Segments: natural 29 stations; gas lift 15+14 around the injection point; ESP 26 + pump node + 2 (ΔP at the pump, separated tubing gas above, back-march for intake with properties at max(P, 100)).

Gas march (modified Gray) — Gray holdup with condensate + water loading, effective-roughness friction (base 0.0021 in), local Tpr. Bit-parity pins: BHP!D51 = 3598.66511095252 @ 1e-8; three real get Pwf points (3414.02 / 2913.97 / 2647.82 psi).

IPR

Oil — composite Vogel (q_gross; q_oil = q_gross·(1−WC/100)):
  Pr>Pb, Pwf≥Pb : q = J·(Pr − Pwf)
  Pr>Pb, Pwf<Pb : q = J·(Pr − Pb) + (J·Pb/1.8)·(1 − 0.2·Pwf/Pb − 0.8·(Pwf/Pb)²)
  Pr≤Pb         : q = (J·Pr/1.8)·(1 − 0.2·Pwf/Pr − 0.8·(Pwf/Pr)²)
Darcy J (dominant): J = 0.00708·K·h / [ μo·Bo·(ln(Re/Rw) − 0.75 + S) ]
Calibrate: user judges skin → closed-form matched K so J_Darcy = J_test (Pri-anchored)
User PI basis: type J + Pres directly (ESP workbook route, "Iput PI"); K becomes the
  DERIVED cell — same closed-form match against the typed J, grey = program-filled.
  Geometry optional. Lift-type switch loads that workbook’s demo case on a pristine form.
Future J (J_2x): μo·Bo re-evaluated at the future Pr, with Beggs–Robinson A/B at the
  CURRENT-Pr Rs (sheet quirk). Pins: J_21–23 = 5.27146844059 / 5.8313895807703 / 6.78094220552862
Gas:
  Darcy Pr² : q [MMscf/d] = J·(Pr² − Pwf²)/1000        J_test = 1000·q/(Pri² − Pwf²)
  From K    : J = 703e-6·K·h / [ μg·Z·(T+460)·(ln(0.472·Re/Rw) + S) ]
  C & n     : q = C·(Pr² − Pwf²)^n                     (log-log least squares)
  Pr from test: Pr = √(1000·q/J + Pwf²)   |   Pr = √((q/C)^(1/n) + Pwf²)

Multi-layer (optional): per-layer rates at common Pwf; equivalent record gas-exact (J_t = ΣJᵢ, Pr₀ = √(ΣJᵢPrᵢ²/ΣJᵢ)); oil Pr-average solves ΣQᵢ = 0. Every layer is drawn on the IPR/VLP chart beside the total (thin dashed, labelled with its own Pr), together with IPR (layers summed) — the true commingled sum, which the solid collapsed IPR matches only at the solution point on oil but at every pressure on gas, where the collapse is exact. Layer curves and their sum come from ONE rate evaluation per grid point, so they add up by construction. Crossflow is shown, not hidden: a layer whose Pr sits below the flowing pressure takes fluid in, so its rate goes NEGATIVE — drawn red and dotted, labelled CROSSFLOW, its table row tinted, and its share of gross signed so the column still sums to 100%. The rate axis is floored at zero so a crossflow excursion cannot squeeze the operating range into half the plot.

Nodal analysis

Operating point: Brent root of R(q) = VLP(q) − IPR(q), highest-rate crossing (stable branch)
Well-head curve: WHP(q) = Pwf_IPR(q) − Pwf_VLP(q) + FTHP, with calculated WHT per rate
get_Pwf: any blank test/production Pwf is marched from its FTHP at its own rate and ratios

Reserves

GAS — condensate & the gas-equivalent balance (ALL four selections):
  SG_c = 141.5/(131.5 + API_c)          MW_c = 42.43·SG_c/(1.008 − SG_c)
  GE   = 133.01·SG_c/MW_c  [Mscf/STB]   (API 49.3 → SG 0.78263274336, MW 147.346636758)
  q_c = q_gas·CGR [STB/d]   N_p,c = trapezoid(q_c·dt)/1e6 [MMstb]   G_p,c = N_p,c·GE [Bscf]
  G_p,tot = G_p + G_p,c     ← the balance is written on TOTAL gas; no condensate API → GE = 0 (dry)
GAS — p/Z (selections 1 & 2):
  Pr per row (closed form) → Z → Gp trapezoid → least-squares p/Z vs G_p,tot
  GIIP [Bscf] = −(p/Z)ᵢ / slope                      (minimum connected, total-gas basis)
  SITHP statics: gas-head-only station march, geothermal T from SITHT at the head to Tres, per-station Z
  survey routes read N_p,c off the prod-table timeline, same clock as Gp
GAS & OIL — reservoir limit (selection 3):
  m = −slope(Pwf vs t) over ALL rows
  Cg = (Bg₁ − Bg₂)/Bg₁/(P₂ − P₁)   (first & last rows; input-or-calculated)
  Ct = Cg·Sg + Co·So + Cw·Sw + Cf
  GIIP [Bscf] = q̄/(Ct·m)/1000      STOIP [MMstb] = q̄/(Ct·m)/1e6
OIL — Havlena–Odeh (selections 1 & 2, solution-gas drive → minimum STOIIP):
  Pr per row: composite-Vogel inversion fixed-point iterated with J_2 (macro loops 5×)
  Np, Gp trapezoids (produced gas: Rsi above Pb, row GOR below);  Rp = Gp/Np
  F  = Np·( Bo + (Rp − Rs)·Bg )        Eo = Bo + (Rsi − Rs)·Bg − Boi
  Headline  N = AVERAGE(F/Eo)  over rows with Np > 0        (sheet AD1)
  Cross-check N = SLOPE(F vs Eo) incl. the exact (0,0) anchor (sheet V1)
  Selection 2 evaluates the same MB at MEASURED gauge pressures — no IPR/VLP.

Forecast (gas)

start: date = last prod date, Gp = cumulative, Pres = MIN solved Pres, cond cum = history
       CGR = the LATEST prod row’s CGR                          (all overridable)
per Δt: G_p,tot = G_p + N_p,c·GE
        (p/Z)_target = (p/Z)ᵢ·(1 − G_p,tot/GIIP)      ← driven by TOTAL gas
        Pr = start Pres (first step) | Brent-invert P/Z(P) = target
        Darcy IPR at Pr → nodal op at forecast FTHP → q = min(q_op, plateau)
        q_c = q·CGR [STB/d]
        Gp += q·Δt/1000            N_p,c += q_c·Δt/1e6
stops: abandonment | depleted | died | max-steps
  →  EUR (dry Gp), EUR cond (MMstb and Bscf), EUR TOTAL, recovery % on the total basis
     (depleting on dry Gp alone drains the tank too slowly and overstates the EUR)

Forecast (oil — Tarner & Walsh)

Both march the macro's GoalSeek PAIR per step, alternated to convergence:
  AE: Gp by the producing-GOR trapezoid over Np = Gp from the MB  → solve P  (Brent)
  AF: the assumed So                           = So from the MB  → solve So (substitution)
  Pwf frozen for the step; PVT at the TRIAL pressure; 25 passes, |ΔP|<1e-6, |ΔSo|<1e-10

shared:  Kro(So), Krg(Sg) = the sheet 6th-order polynomials (Tarner!F16/G16), verbatim
         Sg = max(0, 1 − Swi − So);   So clamped to [0.05, 1 − Swi]
         ct = (Cw·Swi + Cf)/(1 − Swi)·(Pri − P)    Swi .15, Cw 2.63e-6, Cf 3.25e-6 /psi
         Np += qo·Δt/1e6;   Gp += (GOR_prev + GOR_new)/2 · ΔNp
         Pwf 'vlp'   = nodal vs the REAL oil march at forecast FTHP, floored at minPwf
             'fixed' = constant minPwf (the sheet's active behaviour)
         an anchor row at the start date (Δt = 0) joins the forecast to its history

Tarner — solution-gas MB, MOBILITY Darcy PI (no μ·B; mobility enters through λ):
  J1  = 0.00708·K·h/(ln(Re/Rw) − 0.75 + S)     λt = Kro/μo + Krg/μg    λo = Kro/μo
  qo  = J1·(λo/Bo)·(P − Pwf)
  So  = (1 − Swi)·(N − Np)·Bo / (N·Boi·(1 − ct))                      (Tarner!Y)
  Gp  = N·(Rsi − Rs) − (N·Boi − (N − Np)·Bo)/Bg + N·Boi·ct/Bg + Np·Rs  (Tarner!O)
  GOR = Rs + (Krg/Kro)·(μo·Bo)/(μg·Bg)                                (Tarner!P)
  Bg in bbl/scf;  μg at reservoir T, sweet pseudo-criticals, LOCAL Tpr

Walsh — generalized (volatile-oil) MB with Rv, on the CONSTANT calibrated PI J:
  Rv(P) = the sheet 6th-order polynomial (Walsh!AH) / 1000                 [bbl/scf]
  qt  = J·λt·(P − Pwf);   qo = qt·λo/(λt·Bo)              J = Walsh!$I$13 = J_2
  Gp  = (N·(Boi·(1 − Rv·Rs) − (Bg − Rv·Bo)·Rsi − (Bo − Rs·Bg)) + Np·Bo − Np·Rs·Bg)
        / (Bo·Rv − Bg)  +  (N·Boi/Bg)·ct/(1 − Swi)                    (Walsh!O)
  So  = (1 − Swi)·((1 − Np/N)·Bo·Bg − Boi·Rv·Bo)
        / (Boi·(Bg − Rv·Bo)·(1 − ct))                              (Walsh!Y)
  Foo = 1/(1 + (Bo·Krg·μo·Rv)/(Bg·Kro·μg))                            (Walsh!AI)
  GOR = Foo·(Rs + (Krg/Kro)·(μo·Bo)/(μg·Bg))                          (Walsh!P)
  QUIRKS KEPT: μg = μo (Walsh!W reads the OIL-visc cell BHP!AQ)
               Bg in cf/scf, NOT bbl/scf — the Walsh MB is written in those units
  start Gp = Np₀·Rsi   (Tarner starts at 0)

stops: qo < abandon (50 stb/d) | Np ≥ .999·N | P ≤ max(1.02·minPwf, 120) | dead | max-steps
       → EUR, recovery %
LIMIT: neither MB carries a water term — the Forecast W.C affects LIFT only, never
       the balance. On a high-water-cut well that is a real limit of the method.

Documented deviations from the workbooks

  1. Z factor: the sheets GoalSeek Hall–Yarborough (left unconverged in places); WellSim is explicit Brill & Beggs. Station parity pinned at 1e-9 with sheet Z injected; march drift bands: natural <2%, gas-lift <5.5%, ESP <3–5%; gas march bit-exact.
  2. Solvers: Brent replaces GoalSeek and the forecast’s quadratic-LSQ intersection — same intent, exact crossing.
  3. Forecast plateau Pwf: reported at the produced (constrained) rate; the sheet’s unconstrained-intersection quirk is not replicated.
  4. Pressure update: exact p/Z inversion instead of the one-step-lag Z estimate.
  5. Oil reservoir-limit Cg anchors: first & last rows (sheet pinned rows 5 & 14) — near-immaterial for oil Ct.
  6. WHT: always calculated (Ramey); the input-THT path was removed by design decision.
  7. Rounding artifacts preserved where parity needs them: π = 22/7 trajectory cosine; the rouhgsc artifact available as a test override.
  8. Oil-forecast PVT evaluation point: Rs/Bo/Bg/μ are evaluated at the TRIAL pressure inside each step’s solve, for both methods. The saved Tarner sheet froze PVT at initial values; the training material directs otherwise, and the Walsh Gp residual moves with pressure almost entirely through the PVT (Bg, Rs, Rv) — freezing it leaves no root.
  9. Walsh PVT table pressure: the sheet reads its PVT table at the parallel TARNER sheet’s pressures (a shared-table convenience of the workbook); WellSim reads it at Walsh’s own solved pressure.
  10. Per-row Model / User in the gas prod_data (WellSim extension, 25 Sep 2026): the gas reserve workbook backs every row’s Pr out of the IPR and has no typed-Pr convention. The Model column lets a date that carries a measured build-up pressure enter it directly (User: Pwf and Pr typed) while every other date solves exactly as the sheet does (Model). No fill-down, because the usual case is one gauge on one date. Rows without a model are Model rows.
  11. Per-row well model in prod_data (WellSim extension, 6 Sep 2026): no workbook switches the well model within one production history — each sheet solves every row with its own lift and its own J. The Model column lets a history that crossed a lift change (natural → gas lift → ESP) be solved row by row with the model and the J that applied on that date, and the Forecast starts from the last modelled row. Rows without a model solve exactly as the sheets do.
  12. Lift-selection depth is METRES: the workbook’s limits sheet gives depth as 1000–3700 for an ESP and so on, and it means metres — its Level-1 chart axis reads “Depth, m”, and its demo well is 3200, the same ~3200 m well the oil model carries at 2810 mAH. The port had labelled those numbers ft, so the row now reads To perf. Depth, m (screening bands v1.3, 8 Sep 2026). No number moved: a run that typed 3200 meaning depth scores exactly as before, but a depth typed in feet for that well — 10,500 — now correctly screens out instead of being read as a 10,500 m well would be. A case saved before the rename keeps its typed depth.
  13. Lift-selection geometry floors: the workbook’s limits sheet starts every method’s deviation AND dog-leg bands at 0.1, which puts a perfectly vertical, perfectly straight well OUTSIDE all five envelopes at once — the opposite of the truth, since a vertical hole with no dog-leg is the easiest case any of them will ever see. WellSim uses 0 for both (screening bands v1.2, 8 Sep 2026): a lift method is limited by how MUCH a hole bends, never by how little. The bands version is stamped on every run, so an older screen still reproduces.
  14. Lift-selection UDC term: the workbook’s Cost×10⁶/CUM + Opex×CUM/CUM carries a ×CUM/CUM that is identically 1; WellSim drops it — UDC = capex/cum + opex.

Troubleshooting

SymptomCause & fix
“unparseable date row N”Fix that row; nothing was computed. Use d-MMM-yy, dd/mm/yyyy hh:mm:ss, ISO, or a day number.
“no depletion signal”The fitted p/Z (or MB Eo) is not declining — more data spread needed, or the volume genuinely is not closing.
Huge oil MB NUndersaturated path + window too long. Use the early-production window (days–weeks) or trust the static-gauge selection.
Gas march error: CGR + WGR > 0Gray’s liquid terms divide by liquid rate — use a small CGR (0.1 stb/MMscf) for essentially dry gas.
Reserve/forecast uses the wrong wellBoth always read the current Well-model inputs — calibrate the well model first.

Contact

Questions, bug reports, or a workbook you would like ported — write to muhamad.elashry@gmail.com.