pub struct StaticModelBuilder { /* private fields */ }Expand description
Deterministic single-shot builder. Mutable state is the growing control set
(add_top_control) — the live-refine loop; build() converges the current
state into a fresh StaticModel.
Implementations§
Source§impl StaticModelBuilder
impl StaticModelBuilder
Sourcepub fn flat(
ni: usize,
nj: usize,
top_depth_m: f64,
contact_depth_m: f64,
opts: BuildOpts,
) -> Result<Self, StaticError>
pub fn flat( ni: usize, nj: usize, top_depth_m: f64, contact_depth_m: f64, opts: BuildOpts, ) -> Result<Self, StaticError>
A flat-box start: the four corner controls at top_depth_m, so the
initial converged model is the flat box (the model-first walking
skeleton). The framework is synthesized (flat Top horizon + one contact).
§Errors
StaticError::Grid if a dimension is zero; StaticError::InvalidInput
if area/height are non-positive or the contact is not finite.
Sourcepub fn from_wireframe(
wf: &Wireframe,
opts: BuildOpts,
) -> Result<Self, StaticError>
pub fn from_wireframe( wf: &Wireframe, opts: BuildOpts, ) -> Result<Self, StaticError>
Seed from a constraining Wireframe — the data-layer hand-off. The
wireframe drives the structure (its Top horizon becomes the control
set; the first fluid contact sets the column base); the scalars it does
not carry come from opts. Grid (ni, nj) is the top-surface lattice
(ncol-1, nrow-1).
§Errors
StaticError if the wireframe has no Top horizon, the top surface is
degenerate or fully undefined, no fluid contact is present, or opts
dimensions/scalars are degenerate.
Sourcepub fn from_horizon_stack(
stack: HorizonStack,
opts: BuildOpts,
) -> Result<Self, StaticError>
pub fn from_horizon_stack( stack: HorizonStack, opts: BuildOpts, ) -> Result<Self, StaticError>
Seed from an ordered horizon stack — the canonical regional framework:
N horizons top→down define N − 1 named zones, each with its own
Conformity, layer count, and fluid contacts. This is the multi-zone
generalization of Self::from_wireframe; the classic Top+Base wireframe
is its 2-horizon / single-zone degenerate case.
The stack may mix all three horizon source kinds (HorizonSource): mapped
surfaces, mapped-but-untied surfaces, and tops-only internal horizons draped
conformally from the mapped horizon above at pick-controlled thickness.
opts supplies the areal footprint (area_m2), the gridder settings, and
the fallback priors; its nk / conformity / gross_height_m are unused
on this path (per-zone layering + the last horizon’s surface govern instead).
The builder methods (with_logs, with_property, with_min_thickness_m,
with_georef, …) apply as usual; with_min_thickness_m becomes the
per-interface order-repair floor.
§Errors
StaticError::InvalidInput if the stack has fewer than 2 horizons, the
zone-count arrays are not N − 1, the first (top) horizon is not Mapped,
a tops-only horizon has no mapped horizon above it, or a mapped surface is
degenerate / lattice-mismatched / fully undefined.
Sourcepub fn from_scatter_stack(
stack: HorizonStack,
opts: BuildOpts,
frame: StackFrame,
) -> Result<Self, StaticError>
pub fn from_scatter_stack( stack: HorizonStack, opts: BuildOpts, frame: StackFrame, ) -> Result<Self, StaticError>
Build from a stack that may carry raw scatter horizons
(HorizonSource::Scatter) — the engine owns the gridding. Each scatter
horizon is conditioned onto the frame lattice (snap-average onto the
nearest node via the frame georef, genuine voids left NaN), then the stack
resolves through the same merge-safe path as Self::from_horizon_stack
(converged solve + ExtrapolationPolicy + isochore build-down over the
actual observations). The frame georef is registered as the model’s world
frame. Mapped / TopsOnly horizons pass through untouched.
This is the single scatter-gridding authority: no caller pre-grids scatter onto the lattice, so a data-void margin between two merged horizons collapses to zero instead of carrying independently-extrapolated fill.
Conditioning seam. Scatter is conditioned onto the frame lattice by bilinear-weighted minimum curvature (petekTools
Conditioning::Bilinear): each off-node datum maps to its fractional node position and is honoured through the bilinear interpolation of its four surrounding nodes rather than snapped to the nearest — holding on-data fidelity to the lattice representation floor (grid_scatterowns the world→lattice conversion).
§Errors
StaticError as Self::from_horizon_stack, plus
StaticError::InvalidInput if a scatter horizon has no points, or
StaticError::Grid if it lands no point on the frame (fully undefined).
Sourcepub fn condition_scatter_stack(
stack: HorizonStack,
frame: &StackFrame,
) -> Result<HorizonStack, StaticError>
pub fn condition_scatter_stack( stack: HorizonStack, frame: &StackFrame, ) -> Result<HorizonStack, StaticError>
Condition a raw-scatter stack onto frame ONCE — the expensive
per-horizon cold bilinear solve (task_suite_scatter_perf) — returning the
conditioned, all-HorizonSource::Mapped stack as a shared handle.
This is the dedup seam for callers that build both a
StaticModel and its MC
StaticModelTemplate from the same scatter:
conditioning is draw-invariant and identical across the builder and the
template, so re-running Self::from_scatter_stack /
StaticModelTemplate::from_scatter_stack
on each re-solves the whole 11-horizon cold gridding redundantly (the
canonical build’s dominant cost). Condition once, then feed the returned
stack to both paths without re-conditioning:
let g = frame.georef;
let conditioned = StaticModelBuilder::condition_scatter_stack(stack, &frame)?;
// model:
let model = StaticModelBuilder::from_horizon_stack(conditioned.clone(), opts)?
.with_georef(g.origin_x, g.origin_y, g.spacing_x, g.spacing_y)
.build()?;
// MC template (byte-for-byte the same geometry `realize` reproduces):
let template = StaticModelTemplate::from_horizon_stack(conditioned, opts)?
.with_georef(g.origin_x, g.origin_y, g.spacing_x, g.spacing_y);The result is bit-identical to conditioning inside each entry point —
this only removes the redundant re-solve. Genuine data voids stay NaN.
§Errors
As Self::from_scatter_stack’s conditioning step: StaticError::InvalidInput
if a scatter horizon has no points, StaticError::Grid if one lands no
point on the frame.
Sourcepub fn with_clamp_base_to_top(self, clamp: bool) -> Self
pub fn with_clamp_base_to_top(self, clamp: bool) -> Self
Opt into clamping a crossed base (base above top) to zero gross at the
offending columns instead of erroring. Default is to error
(StaticError::CrossedSurfaces); this zeroes only those columns and
leaves the rest untouched (R1).
Sourcepub fn with_min_thickness_m(self, min_thickness_m: f64) -> Self
pub fn with_min_thickness_m(self, min_thickness_m: f64) -> Self
Opt into post-gridding order-repair: where the gridded base sits less
than min_thickness_m below the top (a thin or crossed column), pull the
base down to exactly top + min_thickness_m, preserving the top (the
better-constrained seismic pick). Independent gridding of Top and Base can
overshoot at thin margins and undo a pointwise pre-repair; this repairs the
gridded result rather than erroring. Off by default (the crossing guard
stays the default); when enabled, Provenance::warnings records a
BuildWarning::ThinColumnsRepaired with the repaired-node count and the
worst violation. Takes precedence over Self::with_clamp_base_to_top (R-c).
Sourcepub fn with_collapse_below_m(self, collapse_below_m: f64) -> Self
pub fn with_collapse_below_m(self, collapse_below_m: f64) -> Self
Opt into the cell-collapse threshold (Petrel-style): after layering, any
cell thinner than collapse_below_m collapses to zero thickness, its sliver
merged into a thicker zone-interior neighbour so rock is conserved (never
deleted, never merged across a zone boundary). Off by default. When enabled,
Provenance::warnings records a BuildWarning::CellsCollapsed with the
count. It bites hardest on sub-dz slivers under fixed-count proportional
layering in thin columns and on Follow-style truncation partials.
Sourcepub fn with_extrapolation(self, policy: ExtrapolationPolicy) -> Self
pub fn with_extrapolation(self, policy: ExtrapolationPolicy) -> Self
Set the extrapolation policy for the horizon-stack build — how every
solved stack surface/isochore behaves beyond its data hull. Default
ExtrapolationPolicy::DecayToData (dip held for start_cells, then a
linear decay to the nearest-data value over decay_cells) — conservative,
never silent unbounded natural-dip into a data void. Pass
ExtrapolationPolicy::NaturalDip to opt back into the legacy unbounded
linear extension (appropriate only when the regional dip is KNOWN to
continue). Stack path only; the classic 2-surface path is unchanged.
Sourcepub fn with_sw_gas(self, sw_gas: f64) -> Self
pub fn with_sw_gas(self, sw_gas: f64) -> Self
Set a gas-cap connate-water override applied to gas-zone cells in a
two-contact in_place split (R3), so a single shared SW cube does not
over-state gas-cap OGIP. No effect on a single-contact (no-GOC) column.
Sourcepub fn with_logs(self, samples: Vec<PetroSample>) -> Self
pub fn with_logs(self, samples: Vec<PetroSample>) -> Self
Attach positioned petro samples (TVD, φ, Sw) so build() populates cells
from upscaled logs instead of constant priors; cells with no samples keep
the priors. Empty = priors everywhere.
Sourcepub fn with_areal_trend(self, trend: TrendSurface) -> Self
pub fn with_areal_trend(self, trend: TrendSurface) -> Self
Attach an areal trend multiplier field (external-drift-lite): a gridded
lateral shape resampled to the model lattice, mean-normalized, and applied
per-column to NTG (and φ if the trend flags it) after population. The
trend gives lateral shape; the prior/log gives the level
(decision_staticmodel_regen_seam). See TrendSurface.
Superseded (deprecation-tracked). This is the interim post-population
multiplier hook; the fuller path is PropertyPipeline with
crate::model::Gaussian::with_trend, which steers the simulation itself by collocated
(Markov-1) cokriging rather than scaling a populated cube. It is retained for
the simple “lateral NTG/φ shape on a constant-prior fill” case and is still on
the build + MC-template path; a hard #[deprecated] waits until the pipeline
subsumes this use (organize wave P10 review, task_petekstatic_organize).
Sourcepub fn with_inputs_ref(self, inputs_ref: impl Into<String>) -> Self
pub fn with_inputs_ref(self, inputs_ref: impl Into<String>) -> Self
Label the provenance record with the input-bundle identity.
Sourcepub fn with_spec(self, spec: BuildSpec) -> Self
pub fn with_spec(self, spec: BuildSpec) -> Self
Install a whole declarative BuildSpec in one call — the spec analog of
chaining the individual with_* sugar (each of which mutates the same
internal spec). Values are identical either way, so the build — and every
determinism contract over it — is unchanged.
Sourcepub fn with_memory_budget(self, budget: MemoryBudget) -> Self
pub fn with_memory_budget(self, budget: MemoryBudget) -> Self
Declare the memory budget (ruling R5). Below it build() stays on the
in-core path (byte-identical); above it the model spills its geometry +
cubes to a petekTools store and reads through mmap windows. Pass
MemoryBudget::unlimited to force in-core, or MemoryBudget::bytes to
force spill at a test scale.
Sourcepub fn with_spill_dir(self, dir: impl Into<PathBuf>) -> Self
pub fn with_spill_dir(self, dir: impl Into<PathBuf>) -> Self
Where a spilled build writes its store (ruling R5). Default: the platform
temp dir. The store is removed on model drop unless Self::with_spill_persist.
Sourcepub fn with_spill_persist(self, persist: bool) -> Self
pub fn with_spill_persist(self, persist: bool) -> Self
Keep a spilled model’s store file past drop (a caller-owned location) instead of the default temp-file cleanup.
Sourcepub fn with_georef(
self,
origin_x: f64,
origin_y: f64,
spacing_x: f64,
spacing_y: f64,
) -> Self
pub fn with_georef( self, origin_x: f64, origin_y: f64, spacing_x: f64, spacing_y: f64, ) -> Self
Register the built model’s world georeference: the world (x, y) of
column (0, 0)’s centroid and the world column spacing. The upstream seam
(which knows the source horizon lattice / CRS) supplies this so the view
bundles emit ONE consistent world frame — the map raster overlays the world
outline + wells, and a world fence / bore trace sections correctly (it maps
world traces through the same xy↔ij as well registration).
Non-finite / non-positive spacing is ignored (the model stays in the local
degenerate frame). This does not alter the grid geometry (GRV is still
the area-scaled local square); it only labels the local column lattice with
its world frame. See Georef.
Sourcepub fn with_boundary(self, ring: Vec<[f64; 2]>) -> Self
pub fn with_boundary(self, ring: Vec<[f64; 2]>) -> Self
Register the world areal boundary ring for a horizon-stack build — the
study-area closure the map bundle overlays (world [x, y], first == last point
to close). The from_horizon_stack path has no wireframe to source a boundary
from, so without this it would emit the degenerate unit square while the frame
and wells are world coordinates, collapsing the viewer’s content extent
(task_petekstatic_zoned_fixes finding 2). When omitted, the build still emits a
world-extent rectangle derived from the georef (never the unit square against
a world frame); pass the real closure ring here for the true outline shape.
Ignored on the from_wireframe / flat paths (they carry the wireframe’s own
boundary).
Sourcepub fn with_property(self, pipeline: PropertyPipeline) -> Self
pub fn with_property(self, pipeline: PropertyPipeline) -> Self
Attach a per-property geostatistical pipeline (P5): build() runs it after
the base population, upscaling its positioned logs and SGS-propagating the
cube — overwriting that property’s prior/log cube with the conditioned,
simulated field. Multiple pipelines model properties one at a time (each is
its own PropertyPipeline). The pipeline’s crate::model::PropertyReport lands
on the model’s Provenance::property_reports.
Sourcepub fn with_zone_property(
self,
zone_name: impl Into<String>,
pipeline: PropertyPipeline,
) -> Self
pub fn with_zone_property( self, zone_name: impl Into<String>, pipeline: PropertyPipeline, ) -> Self
Attach a geostatistical pipeline scoped to one zone of a horizon-stack
build (P8 per-zone population, task_petekstatic_multizone_2): after the base
population and any whole-model Self::with_property pipelines, build()
runs it restricted to zone_name’s k-range
(PropertyPipeline::apply_in_zone), so each zone gets its own
variogram/trend/log-conditioning and only that zone’s slice is overwritten.
Attach one pipeline per zone; the pipeline’s report lands on
Provenance::property_reports. A zone_name absent from the built zone
table is a StaticError::InvalidInput at build time.
Sourcepub fn with_zone_priors(
self,
zone_name: impl Into<String>,
priors: ConstantPriors,
) -> Self
pub fn with_zone_priors( self, zone_name: impl Into<String>, priors: ConstantPriors, ) -> Self
Override the constant priors inside one zone of a horizon-stack build
(P8 per-zone population): after the base population build() overwrites
PORO/NTG/SW across zone_name’s k-range with priors — the per-zone
distribution level a stack zone owns (a sand zone vs a shale zone). A
Self::with_zone_property pipeline on the same zone then simulates over this
baseline. A zone_name absent from the built zone table is a
StaticError::InvalidInput at build time.
Sourcepub fn with_tie_settings(self, ties: TieSettings) -> Self
pub fn with_tie_settings(self, ties: TieSettings) -> Self
Set how the well ties are applied (TieSettings: datum substitution —
the default — or bounded-radius locality). Thin sugar over the spec; both
the ties and their settings are read at build() time, so the call order
vs StaticModelBuilder::with_well_ties does not matter here.
Sourcepub fn with_well_ties(self, ties: Vec<WellTie>) -> Self
pub fn with_well_ties(self, ties: Vec<WellTie>) -> Self
Attach explicit well ties for a horizon-stack build (P8 per-horizon ties,
task_petekstatic_multizone_2): each WellTie’s measured tops tie their
mapped horizons to the well (the top is added as a hard control at the
well’s node and the surface re-solved), and every tie’s pre-tie residual
(measured − untied model surface) is recorded in
crate::model::Provenance::well_ties and surfaced in the map bundle’s
wells[].ties. Tops for a tops-only horizon are recorded as QC residuals
against the pick-conditioned drape. Honoured on the from_horizon_stack path.
Sourcepub fn with_sugar_cube(self, sugar_cube: bool) -> Self
pub fn with_sugar_cube(self, sugar_cube: bool) -> Self
Opt into sugar-cube section rendering (flat-box cells) — default false,
where the section bundle carries per-edge cell depths and the viewer draws
dip-following trapezoids. The engine geometry is corner-point either way; this
only sets the section view’s sugar_cube flag (and flattens its edge arrays).
Sourcepub fn add_top_control(&mut self, ip: usize, jp: usize, depth_m: f64)
pub fn add_top_control(&mut self, ip: usize, jp: usize, depth_m: f64)
Add a top-surface depth control point on the (ni+1) x (nj+1) node
lattice — the new datum the next build() re-converges to honour.
Sourcepub fn control_count(&self) -> usize
pub fn control_count(&self) -> usize
Number of control points currently honoured.
Sourcepub fn build(&self) -> Result<StaticModel, StaticError>
pub fn build(&self) -> Result<StaticModel, StaticError>
Converge the model at the current control set: cold minimum-curvature
solve → conformable layering → population → a populated StaticModel.
§Errors
StaticError if the surface solve, layering, or population fails.
Trait Implementations§
Source§impl Clone for StaticModelBuilder
impl Clone for StaticModelBuilder
Source§fn clone(&self) -> StaticModelBuilder
fn clone(&self) -> StaticModelBuilder
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl Freeze for StaticModelBuilder
impl RefUnwindSafe for StaticModelBuilder
impl Send for StaticModelBuilder
impl Sync for StaticModelBuilder
impl Unpin for StaticModelBuilder
impl UnsafeUnpin for StaticModelBuilder
impl UnwindSafe for StaticModelBuilder
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.