A complete description of the agent classes, scheduling, decision rules and calibrated constants behind each of the six pre-salt engines, together with the shared simulation harness and the regulatory framework the Phase IV engines represent.
This document describes what the code does, not what the code ought to do. Several mechanisms are deliberately stylised: they reproduce the shape of a real process at the resolution a decision needs, and they are labelled as such in the limitations block of each engine. Nothing here has been calibrated against Petrobras operational data; see the Data tab for what a calibrated deployment would require.
One config in, one wide result table out
All six engines are driven by a single flat SimulationConfig object declared in src/lib/simulation.ts. The object is deliberately flat rather than nested per engine: the control panel binds sliders directly to keys, scenarios are expressed as Partial<SimulationConfig> patches that compose with whatever the user has already tuned, and the whole config serialises to a single JSON blob for reproducibility. Fields that do not apply to the selected engine are simply ignored by that engine.
Every engine returns an array of SimulationResult rows, one row per emitted time step. That interface is wide and almost entirely optional: each engine writes time plus its own named subset of roughly a dozen fields, and leaves the rest undefined. Charts and KPI tiles read by key, so the presentation layer never needs to know which engine produced a row. The engine metadata table ENGINES declares, per engine, which keys become charts and which become KPI tiles, with their formatting and their good direction.
Horizons are engine-specific and carry engine-specific units, given by ENGINE_HORIZONS: logistics 90 days, aviation 60 days, swarm 400 mission steps, spill 40 days, auction 24 blocks, supplier 60 months. Switching engine in the UI resets numSteps to the natural horizon and re-ranges the slider, because a 400-step swarm mission and a 400-month supplier plan are not the same request.
All stochastic draws route through a single shared PRNG in src/lib/rng.ts. The generator is Mulberry32, chosen because it is a dozen lines of integer arithmetic and therefore produces bit-identical streams in TypeScript and in Python. A non-zero seed gives a fully reproducible run: the same config always yields the same result array. A seed of 0 is the agreed sentinel for non-deterministic mode — the RNG falls through to Math.random, which is what the Monte Carlo sweep uses to generate independent replications without needing a separate code path.
The RNG exposes uniforms, a Box–Muller normal, Bernoulli draws, exponentials, uniform integers and a weighted index sampler. Because every engine consumes the same stream in a fixed order, changing the number of draws anywhere in a step loop changes every subsequent draw — which means engine edits are not backward-compatible with previously recorded seeded runs. Recorded baselines should always carry the commit hash alongside the seed.
public/simulation.py is the Python side of the same contract. It carries a bit-identical Mulberry32 and exposes run_simulation(config) and run_simulation_json(config_json), so the browser can execute the model under Pyodide and the offline validation pipeline can execute the same code path outside the browser. In the current build the Python file implements the harness, the PRNG and a reference step loop; the six engine bodies are maintained in TypeScript and are the authoritative implementation. Any change to an engine, a constant or the result schema must be mirrored on the Python side before the Monte Carlo pipeline can be treated as validating the same model rather than a sibling of it. This is a known and deliberate gap, not an oversight.
Two config fields, shockEnabled and shockMagnitude, are honoured by every engine but mean something different in each, and each applies the shock over a different fraction of its horizon: logistics multiplies the wave climate between 35% and 50% of the run; aviation multiplies the aircraft disruption rate between 30% and 45%; supplier multiplies demand arrivals between 35% and 55%; auction divides the oil price between 40% and 60% of the block sequence; spill multiplies the cross-shelf drift anomaly for the whole run. The shock is a stress handle for scenario work, not an estimated event distribution.
The rules the Phase II and Phase IV engines are representations of
The two Phase IV engines are only meaningful against the Brazilian upstream regime, and the spill engine only against the licensing posture for the Equatorial Margin. What follows is context for reading the model outputs. It is a working summary, not legal advice, and the model implements simplified versions of each rule as described in the engine sections above.
Pre-salt and strategic areas are licensed under a production-sharing regime rather than the concession regime that governs the rest of the Brazilian upstream. The contractor recovers costs through cost oil, subject to a ceiling, and the remainder — profit oil — is split with the Union. Under the permanent offer of production sharing, blocks sit in a standing inventory with pre-published data packages and terms, and cycles are called periodically rather than as discrete numbered rounds. The CNPE fixes the signature bonus and the minimum profit-oil share in advance, so bidding collapses onto a single competitive dimension: the profit-oil percentage offered to the Union. The auction engine implements exactly that single-dimension first-price mechanism, with profitOilFloor standing in for the CNPE minimum.
The original production-sharing law made Petrobras the mandatory operator of every pre-salt block with a minimum 30% participating interest. Law 13,365/2016 removed that obligation and replaced it with a right: before each round, Petrobras may exercise a preference to operate blocks it selects, taking at least a 30% participating interest, with the CNPE confirming the outcome. The effect is that Petrobras chooses where to be operator rather than being compelled everywhere, and the choice is informed by its subsurface position on adjacent acreage. The auction engine represents this through preemptionEnabled, a tighter private signal for Petrobras, and an exercise test that only triggers when the block clears Petrobras's hurdle at the winning terms and at a 30% stake. The simplification is that the model resolves preemption after the award rather than before the round; the economic content — a better-informed party taking a fixed minimum interest at someone else's price — is preserved.
Contractual local-content commitments oblige the operator to source a certified share of goods and services domestically, measured per contract against item-level rules, with certification by accredited bodies and penalties assessed on the shortfall at the end of each phase. Resolution 19 consolidated the measurement and penalty framework, including the treatment of exploration and development phases separately and the possibility of transferring or adjusting commitments. The supplier engine collapses all of this into a single contractual percentage (localContentPct), a flat penalty rate on the shortfall value (penaltyRatePct), and a voluntary overlay (voluntaryLcWeight) representing expectations applied to assets outside the formal mandate. Certification, per-contract assessment and phase boundaries are not modelled.
The programme for stimulating the competitiveness of the Brazilian supplier chain lets operators earn Local Content Units — UCLs — through qualifying activity: direct investment in domestic supplier technology and yard capability, and the purchase of Brazilian equipment for operations abroad. UCLs can then be applied against a local-content shortfall elsewhere in the portfolio, which converts a binary compliance failure into a tradeable position and gives the operator a reason to invest in capability rather than simply pay the penalty. The supplier engine models this as a single fungible balance accruing at UCL_PER_USD_M = 1.35 per USD million of PEDEFOR spend, applied against the monthly shortfall before the penalty is struck. The scenario pair lc_squeeze and pedefor_offset exists to make the difference visible: the same 75% target with and without the offset mechanism.
Exploratory drilling on the Equatorial Margin, including the Foz do Amazonas basin that the spill engine is configured for, is licensed by IBAMA and has been conditioned on a pre-operational assessment: a demonstration, before spudding, that the declared emergency response capability actually exists and works. In practice that has meant a full-scale simulated exercise covering the oiled-wildlife response chain — capture, stabilisation and rehabilitation of fauna, with staffed centres at credible distances — alongside the vessel, boom and dispersant resources in the individual emergency plan. The binding constraints are response time from staging bases in Belém and Macapá to a well roughly 175 km offshore Amapá, and the seasonal reorientation of the winds and currents that determines whether a release drifts offshore or into the Amazon plume and the mangrove coast. Those two constraints are precisely what the spill engine parameterises through responseDelayH, vessel staging, and the JFMA/JASO seasonal forcing sets. The engine does not represent the licensing process itself, the wildlife response chain, or any specific plan.
Read these before quoting any number from this platform
Source files: src/lib/simulation.ts, src/lib/rng.ts, src/lib/engines/logistics.ts, src/lib/engines/aviation.ts, src/lib/engines/swarm.ts, src/lib/engines/spill.ts, src/lib/engines/auction.ts, src/lib/engines/supplier.ts, public/simulation.py.