Module curlew.synthetic
Generate synthetic datasets and models for testing purposes.
Functions
def anderson(shape=None, offset1=225, offset2=250, **kwargs)-
Expand source code
def anderson( shape=None, offset1=225, offset2=250, **kwargs ): """ Return a synthetic model with a slightly curved layer-cake stratigraphy cut by two intersecting normal faults. Parameters ---------- shape : tuple The width and height of the generated data. offset1 : tuple The offset of the first (older) normal fault. offet2 : tuple The offset of the second (younger) normal fault. Keywords --------- All keywords are passed to `curlew.synthetic.sample(...)` Returns -------- M : curlew.geology.geomodel.GeoModel Geomodel of the synthetic model with sampled constraints bound to events. """ G = _make_grid(shape) extent = _physical_extent(shape) ndim = _model_ndim(shape) vert = 1 if ndim == 2 else 2 s0 = strati('s0', C=QuadraticField( 'f0', input_dim=ndim, curve=_g2((-0.00005, 0), ndim), origin=_p2((1000, 500), ndim) ) ) s1 = fault( 's1', C=LinearField( 'f1', input_dim=ndim, origin=_p2((950, 550), ndim), gradient=_g2((np.cos( np.deg2rad(35) ), np.sin( np.deg2rad(35) )), ndim) ), offset=offset1, shortening=_g2((0, -1), ndim) ) s2 = fault( 's2', C=LinearField( 'f2', input_dim=ndim, origin=_p2((1050, 500), ndim), gradient=_g2((-np.cos( np.deg2rad(35) ), np.sin( np.deg2rad(35) )), ndim) ), offset=offset2, shortening=_g2((0, -1), ndim) ) for seed, name in _isosurface_seeds(extent, ndim, 5): s0.addIsosurface(name, seed=seed) M = GeoModel( [s0, s1, s2], grid=G, name='anderson' ) s = M.predict(G) Cs = sample(s, M, pv='rgb', bind=False, **kwargs) kw_fault = dict(kwargs) kw_fault.pop('breaks', None) kw_fault.pop('pv', None) kw_fault['pval'] = kw_fault.get('pval', 1.0) Cf1 = sample(s1.predict(G), M, pv='rgb', breaks=[0.5], xstep=600, bind=False, **kw_fault) Cf2 = sample(s2.predict(G), M, pv='rgb', breaks=[0.5], bind=False, **kw_fault) ordered = _csets_in_order(Cs) strat = ordered[0] prop = ordered[-1] if 'property' in Cs else None f1_c = _csets_in_order(Cf1)[0] f2_c = _csets_in_order(Cf2)[0] gv = strat.gv if gv is not None: mask = (gv[:, vert] > 0.9) & (gv[:, vert] < 1.1) strat.gp = strat.gp[mask] strat.gv = strat.gv[mask] strat.gop = strat.gop[mask] strat.gov = strat.gov[mask] for fc in (f1_c, f2_c): if fc.vv is not None: fc.vv = fc.vv * 0 M['s0'].field.bind(strat) M['s1'].field.bind(f1_c) M['s2'].field.bind(f2_c) if prop is not None: M.bind(prop) return MReturn a synthetic model with a slightly curved layer-cake stratigraphy cut by two intersecting normal faults.
Parameters
shape:tuple- The width and height of the generated data.
offset1:tuple- The offset of the first (older) normal fault.
offet2:tuple- The offset of the second (younger) normal fault.
Keywords
All keywords are passed to <code><a title="curlew.synthetic.sample" href="#curlew.synthetic.sample">sample()</a>(...)</code>Returns
M:GeoModel- Geomodel of the synthetic model with sampled constraints bound to events.
def extract_constraints(M, events=None, include_property=False)-
Expand source code
def extract_constraints(M, events=None, include_property=False): """ Return bound constraint sets from a synthetic ``curlew.geology.geomodel.GeoModel`` for use when fitting neural fields. Parameters ---------- M : curlew.geology.geomodel.GeoModel A synthetic model returned by one of the builders in this module (after ``sample`` has run). events : list of str, optional Event names to include. Defaults to all events in ``M.events``. include_property : bool If True, include global property constraints under the key ``'property'``. Returns ------- dict Constraint sets keyed by event name (and ``'property'`` when requested). """ if events is None: events = [e.name for e in M.events] out = {} for name in events: c = M[name].field.C if c is not None: out[name] = c.numpy() if include_property and M.C is not None: out["property"] = M.C.numpy() return outReturn bound constraint sets from a synthetic
GeoModelfor use when fitting neural fields.Parameters
M:GeoModel- A synthetic model returned by one of the builders in this module (after
sample()has run). events:listofstr, optional- Event names to include. Defaults to all events in
M.events. include_property:bool- If True, include global property constraints under the key
'property'.
Returns
dict- Constraint sets keyed by event name (and
'property'when requested).
def goguel(shape=None, zstep=300, dip=60.0, offset=0.2, n_faults=3, **kwargs)-
Expand source code
def goguel(shape=None, zstep=300, dip=60.0, offset=0.2, n_faults=3, **kwargs): """ Return a synthetic model with horizontal stratigraphy and several finite faults. Each fault has planar geometry (shared implicit field), constant slip scaled by a cosine-tapered ellipsoidal patch (finite fault extent). Parameters ---------- shape : tuple, optional The width and height of the generated data. zstep : float Vertical spacing between fault seed positions. dip : float Fault dip in degrees (from horizontal). offset : float Slip magnitude. Values in ``(0, 1)`` are interpreted as a fraction of the ellipsoid major-axis radius; values ``>= 1`` are absolute slip (same units as extent). n_faults : int Number of finite faults (default 3). Keywords -------- Passed to :func:`curlew.synthetic.sample`. Returns ------- M : curlew.geology.geomodel.GeoModel. """ # Grid, extent, and model centre (fault/strat seeds are placed relative to these). G = _make_grid(shape) extent = _physical_extent(shape) ndim = _model_ndim(shape) vert = 1 if ndim == 2 else 2 origin = np.asarray(G.center, dtype=float) # Ellipsoid major axis and fault spacing scale with model height; slip from offset arg. patch_radius = 2.5 * extent[vert] / (n_faults + 1) xstep = 1.5 * extent[0] / (n_faults + 1) if isinstance(offset, (int, np.integer)) or (isinstance(offset, float) and offset >= 1): slip_mag = float(offset) else: slip_mag = offset * patch_radius slip = [slip_mag] * n_faults # Stagger fault centres in x and z, then re-centre the cluster on the grid midpoint. cx = np.array([xstep * (i + 1) for i in range(n_faults)]) cz = np.array([extent[vert] - zstep * (i + 1) for i in range(n_faults)]) cx = cx - np.mean(cx) + origin[0] cz = cz - np.mean(cz) + origin[vert] # Layer-cake basement: horizontal scalar field and regularly spaced isosurface seeds. s0 = strati( "s0", type=LinearField, origin=_g2(origin, ndim), gradient=_g2(np.array([0.0, 1.0]), ndim), ) for seed, name in _isosurface_seeds(extent, ndim, 8): s0.addIsosurface(name, seed=seed) # One fault GeoEvent: shared plane dipping toward +x; vertical shortening gives normal (top-down) slip. fault_grad = np.array([np.sin(np.deg2rad(dip)), np.cos(np.deg2rad(dip))]) contacts = [f"ff{i}" for i in range(n_faults)] modifiers = [f"fe{i}" for i in range(n_faults)] s1 = fault( "s1", type=LinearField, origin=_g2(origin, ndim), gradient=_g2(fault_grad, ndim), shortening=_g2((0, -1), ndim), offset=slip, contact=contacts, width=0, modifier=modifiers, n_steps=1, ) # Ellipse frame: tangent along strike, normal across the fault plane; axes set patch size. fn = _g2(s1["s1"].grad.detach().cpu().numpy(), ndim) ft = _g2(np.array([-fn[1], fn[0]]), ndim) axes = _g2(np.array([patch_radius, 0.75 * patch_radius]), ndim) # Per fault: isosurface seed on the shared plane + cosine ellipsoid that tapers slip to zero. for i, (_x, _z) in enumerate(zip(cx, cz)): seed = _g2(np.array([_x, _z]), ndim) s1.addIsosurface( f"ff{i}", seed=seed.copy(), field="s1", ) if ndim == 2: directions = np.array([ft, fn]) else: directions = np.stack([ft, np.cross(ft, fn), fn]) s1.addField( f"fe{i}", field=EllipsoidalField( f"fe{i}", input_dim=ndim, origin=seed.copy(), axes=axes, directions=directions, decay="cosine", ), ) # Assemble model and sample constraints (bedding on s0, fault plane on s1, global property). M = GeoModel([s0, s1], grid=G, name="goguel") s = M.predict(G) Cs = sample(s, M, pv="rgb", bind=False, **kwargs) kw_fault = dict(kwargs) kw_fault.pop("breaks", None) kw_fault["pval"] = kw_fault.get("pval", 1.0) fault_breaks = [s1.getIsovalue(f"ff{i}") for i in range(n_faults)] Cf = sample( s1.predict(G), M, pv="rgb", breaks=fault_breaks, bind=False, **kw_fault, ) ordered = _csets_in_order(Cs) strat = ordered[0] prop = ordered[-1] if "property" in Cs else None fault_c = _csets_in_order(Cf)[0] # Keep near-horizontal bedding orientations only (s0 gradient is vertical). gv = strat.gv if gv is not None: mask = (gv[:, vert] > 0.9) & (gv[:, vert] < 1.1) strat.gp = strat.gp[mask] strat.gv = strat.gv[mask] strat.gop = strat.gop[mask] strat.gov = strat.gov[mask] if fault_c.vv is not None: fault_c.vv = fault_c.vv * 0 M["s0"].field.bind(strat) M["s1"]["s1"].bind(fault_c) if prop is not None: M.bind(prop) return MReturn a synthetic model with horizontal stratigraphy and several finite faults.
Each fault has planar geometry (shared implicit field), constant slip scaled by a cosine-tapered ellipsoidal patch (finite fault extent).
Parameters
shape:tuple, optional- The width and height of the generated data.
zstep:float- Vertical spacing between fault seed positions.
dip:float- Fault dip in degrees (from horizontal).
offset:float- Slip magnitude. Values in
(0, 1)are interpreted as a fraction of the ellipsoid major-axis radius; values>= 1are absolute slip (same units as extent). n_faults:int- Number of finite faults (default 3).
Keywords
Passed to :func:<code><a title="curlew.synthetic.sample" href="#curlew.synthetic.sample">sample()</a></code>.Returns
M : curlew.geology.geomodel.GeoModel.
def hutton(shape=None, **kwargs)-
Expand source code
def hutton( shape=None, **kwargs ): """ Return a synthetic model with a folded basement cut by an unconformity. Parameters ---------- shape : tuple The width and height of the generated data. Keywords --------- All keywords are passed to `curlew.synthetic.sample(...)` Returns -------- M : curlew.geology.geomodel.GeoModel Geomodel of the synthetic model with sampled constraints bound to events. """ G = _make_grid(shape) extent = _physical_extent(shape) ndim = _model_ndim(shape) s0 = strati('s0', C=QuadraticField( 'f1', input_dim=ndim, gradient=_g2((0, 1), ndim), origin=_p2((0, 0), ndim), curve=_g2((-0.00002, 0), ndim) )) d1 = fold('d1', origin=_p2((0, 0), ndim), extension=_g2((0, 1), ndim), compression=_g2((1, 0.3), ndim), wavelength=2000, amplitude=250, sharpness=0.7) s1 = strati('s1', C=QuadraticField( 'f1', input_dim=ndim, gradient=_g2((0.1, 0.9), ndim), origin=_p2((1000, 500), ndim), curve=_g2((-0.00002, 0), ndim) ), base=0) for seed, name in _isosurface_seeds(extent, ndim, 5): s0.addIsosurface(name, seed=seed) for seed, name in _isosurface_seeds(extent, ndim, 10): s1.addIsosurface(name, seed=seed) M = GeoModel( [s0,d1,s1], grid=G, name='hutton' ) s = M.predict(G) Cs = sample(s, M, pv='rgb', bind=False, **kwargs) ordered = _csets_in_order(Cs) M['s0'].field.bind(ordered[1]) M['s1'].field.bind(ordered[0]) if len(ordered) > 2: M.bind(ordered[2]) return MReturn a synthetic model with a folded basement cut by an unconformity.
Parameters
shape:tuple- The width and height of the generated data.
Keywords
All keywords are passed to <code><a title="curlew.synthetic.sample" href="#curlew.synthetic.sample">sample()</a>(...)</code>Returns
M:GeoModel- Geomodel of the synthetic model with sampled constraints bound to events.
def lehmann(shape=None, **kwargs)-
Expand source code
def lehmann( shape=None, **kwargs ): """ Return a synthetic model with a folded basement. Parameters ---------- shape : tuple The width and height of the generated data. Keywords --------- All keywords are passed to `curlew.synthetic.sample(...)` Returns -------- M : curlew.geology.geomodel.GeoModel Geomodel of the synthetic model with sampled constraints bound to events. """ G = _make_grid(shape) extent = _physical_extent(shape) ndim = _model_ndim(shape) if ndim == 3: s0 = strati('s0', C=PeriodicField('f0', input_dim=ndim, gradient=np.array([0., 0., 1.]), axialPlane=np.array([1., 0., 0.]))) else: s0 = strati('s0', C=PeriodicField('f0', input_dim=ndim)) M = GeoModel([s0], grid=G, name='lehmann' ) for seed, name in _isosurface_seeds(extent, ndim, 5): s0.addIsosurface(name, seed=seed) s = M.predict(G) sample(s, M, pv='rgb', **kwargs) return MReturn a synthetic model with a folded basement.
Parameters
shape:tuple- The width and height of the generated data.
Keywords
All keywords are passed to <code><a title="curlew.synthetic.sample" href="#curlew.synthetic.sample">sample()</a>(...)</code>Returns
M:GeoModel- Geomodel of the synthetic model with sampled constraints bound to events.
def michell(shape=None, offset=100, **kwargs)-
Expand source code
def michell( shape=None, offset=100, **kwargs ): """ Return a synthetic model with a slightly curved layer-cake stratigraphy cut by a thrust fault. Parameters ---------- shape : tuple The width and height of the generated data. offset : tuple The offset of the fault. Keywords --------- All keywords are passed to `curlew.synthetic.sample(...)` Returns -------- M : curlew.geology.geomodel.GeoModel Geomodel of the synthetic model with sampled constraints bound to events. """ G = _make_grid(shape) extent = _physical_extent(shape) ndim = _model_ndim(shape) o = _p2((1000, 500), ndim) s0 = strati('s0', C=QuadraticField( 'f0', input_dim=ndim, curve=_g2((-0.00005, 0), ndim), origin=o ) ) s1 = fault( 's1', C=LinearField( 'f1', input_dim=ndim, origin=o, gradient=_g2((0.5, 0.5), ndim) ), offset=offset, shortening=_g2((-1, 0), ndim) ) for seed, name in _isosurface_seeds(extent, ndim, 5): s0.addIsosurface(name, seed=seed) M = GeoModel( [s0,s1], grid=G, name='michell' ) s = M.predict(G) Cs = sample(s, M, pv='rgb', bind=False, **kwargs) kwargs['pval'] = kwargs.get('pval', 1.0) kw_fault = dict(kwargs) kw_fault.pop('breaks', None) Cf = sample(s1.predict(G), M, pv='rgb', breaks=[0.5], bind=False, **kw_fault) ordered = _csets_in_order(Cs) strat = ordered[0] fault_c = _csets_in_order(Cf)[0] prop = ordered[-1] if 'property' in Cs else None gv = strat.gv if gv is not None: mask = gv[:, 0] > -0.68 strat.gp = strat.gp[mask] strat.gv = strat.gv[mask] strat.gop = strat.gop[mask] strat.gov = strat.gov[mask] if fault_c.vv is not None: fault_c.vv = fault_c.vv * 0 M['s0'].field.bind(strat) M['s1'].field.bind(fault_c) if prop is not None: M.bind(prop) return MReturn a synthetic model with a slightly curved layer-cake stratigraphy cut by a thrust fault.
Parameters
shape:tuple- The width and height of the generated data.
offset:tuple- The offset of the fault.
Keywords
All keywords are passed to <code><a title="curlew.synthetic.sample" href="#curlew.synthetic.sample">sample()</a>(...)</code>Returns
M:GeoModel- Geomodel of the synthetic model with sampled constraints bound to events.
def playfair(shape=None, width=50, addFault=False, **kwargs)-
Expand source code
def playfair( shape=None, width=50, addFault=False, **kwargs ): """ Return a synthetic model with a layer-cake stratigraphy cut by a dyke. Parameters ---------- shape : tuple The width and height of the generated data. width : float The half-width of the added dyke. addFault : bool If True, add a fault crosscutting the dykes and stratigraphy. Default is False. Keywords --------- All keywords are passed to `curlew.synthetic.sample(...)` Returns -------- M : curlew.geology.geomodel.GeoModel Geomodel of the synthetic model with sampled constraints bound to events. """ G = _make_grid(shape) extent = _physical_extent(shape) ndim = _model_ndim(shape) o = _p2((1000, 500), ndim) s0 = strati('s0', C=QuadraticField( 'f0', input_dim=ndim, curve=_g2((-0.00005, 0), ndim), origin=o ) ) s1 = sheet( 's1', C=LinearField( 'f1', input_dim=ndim, origin=o, gradient=_g2((0.5, 0.5), ndim) ), contact=(-width, width) ) for seed, name in _isosurface_seeds(extent, ndim, 5): s0.addIsosurface(name, seed=seed) if addFault: s2 = fault( 's2', C=LinearField( 'f2', input_dim=ndim, origin=_p2((1050, 500), ndim), gradient=_g2((-np.cos( np.deg2rad(35) ), np.sin( np.deg2rad(35) )), ndim) ), offset=100, shortening=_g2((0, -1), ndim) ) M = GeoModel( [s0, s1, s2], grid=G, name='newcastle' ) else: M = GeoModel( [s0, s1], grid=G, name='playfair' ) kwargs['pval'] = kwargs.get('pval', 1.0) # change default to sample all value constraints Cs = sample(M.predict(G), M, pv='rgb', bind=False, **kwargs) C1 = sample(s1.predict(G), M, pv='rgb', breaks=[-width, width], bind=False, **kwargs) ordered = _csets_in_order(Cs) dyke = _csets_in_order(C1)[0] M['s0'].field.bind(ordered[1]) M['s1'].field.bind(dyke) if len(ordered) > 2: M.bind(ordered[2]) return MReturn a synthetic model with a layer-cake stratigraphy cut by a dyke.
Parameters
shape:tuple- The width and height of the generated data.
width:float- The half-width of the added dyke.
addFault:bool- If True, add a fault crosscutting the dykes and stratigraphy. Default is False.
Keywords
All keywords are passed to <code><a title="curlew.synthetic.sample" href="#curlew.synthetic.sample">sample()</a>(...)</code>Returns
M:GeoModel- Geomodel of the synthetic model with sampled constraints bound to events.
def sample(G, M, pv=None, breaks=19, init=100, xstep=300, pval=0.6, cmap='tab20', seed=42, bind=True)-
Expand source code
def sample( G, M, pv=None, breaks=19, init=100, xstep=300, pval=0.6, cmap='tab20', seed=42, bind=True ): """ Sample value, orientation, equality (contact trace), and property constraints from a scalar field and associated gradients. Parameters ---------- G : curlew.core.Geode A Geode object containing the results from a GeoEvent or GeoModel. M : curlew.geology.geomodel.GeoModel The geomodel used to construct the geode G. pv : np.ndarray or str A (N, d) array of n-dimensional property vectors (e.g., color). Can also be 'rgb' to create synthetic colors. breaks : list A set of scalar values at which contacts (changes in color) should be placed. init : int The index of the first "drillhole" in the x-direction. xstep : int The separation between "drillholes" in the x-direction. pval : float The probability that an observation is a scalar value (rather than just a gradient) constraint. cmap : str The name of the Matplotlib colormap to use for sampling colors that determine where geological contacts are. Must be a discrete colormap. seed : int Random seed to facilitate reproducible results. bind : bool If True, the sampled constraints will be added to their respective fields (and global constraints bound to the GeoModel). Notes ----- For 3D grids, constraints are sampled on x-z sections at y = 25%, 50% and 75% of the model y extent (1500 m). Returns ------- dict Sampled constraints keyed by structure ID (int) and ``'property'`` when present. Empty when ``bind=True`` (constraints are attached to ``M`` and its events). """ np.random.seed(seed) sf = G.grid.reshape(curlew._numpy(G.scalar)) sid = G.grid.reshape(curlew._numpy(G.structureID).astype(int)) xy = G.grid.reshape(G.grid.coords()) constraints = { int(k): {"vp": [], "vv": [], "gp": [], "gv": [], "gop": [], "gov": [], "eq": []} for k in np.unique(sid) } eq_accum = {int(k): {} for k in np.unique(sid)} if G.grid.ndim == 3: y_global = xy[0, :, 0, 1] for y_frac in (0.25, 0.5, 0.75): j = int(np.argmin(np.abs(y_global - G.grid.dims[1] * y_frac))) sf2 = sf[:, j, :] sid2 = sid[:, j, :] xy2 = xy[:, j, :, :] gx = np.diff(sf2, axis=0) gz = np.diff(sf2, axis=1) c = colour(sf2, breaks=breaks, cmap=cmap) breaks_arr = _breaks_array(sf2, breaks) contacts = np.sum(np.abs(np.diff(c, axis=1, append=0)), axis=-1) > 0 domains = np.sum( [np.abs(np.diff(sid2, axis=1, append=0)), np.abs(np.diff(sid2, axis=0, append=0))], axis=0 ) > 0 pv2 = c if pv == "rgb" else pv _sample_section( sf2, sid2, xy2, contacts, domains, gx, gz, init, xstep, pval, pv2, constraints, eq_accum, breaks_arr, ) else: gx = np.diff(sf, axis=0) gy = np.diff(sf, axis=1) c = colour(sf, breaks=breaks, cmap=cmap) breaks_arr = _breaks_array(sf, breaks) contacts = np.sum(np.abs(np.diff(c, axis=1, append=0)), axis=-1) > 0 domains = np.sum( [np.abs(np.diff(sid, axis=1, append=0)), np.abs(np.diff(sid, axis=0, append=0))], axis=0 ) > 0 if pv == "rgb": pv = c _sample_section( sf, sid, xy, contacts, domains, gx, gy, init, xstep, pval, pv, constraints, eq_accum, breaks_arr, ) for i, levels in eq_accum.items(): traces = [np.array(pts) for pts in levels.values() if len(pts) >= 2] if traces: constraints[i]["eq"].extend(traces) out = _finalize_constraints(constraints, M, bind=bind) return {} if bind else outSample value, orientation, equality (contact trace), and property constraints from a scalar field and associated gradients.
Parameters
G:Geode- A Geode object containing the results from a GeoEvent or GeoModel.
M:GeoModel- The geomodel used to construct the geode G.
pv:np.ndarrayorstr- A (N, d) array of n-dimensional property vectors (e.g., color). Can also be 'rgb' to create synthetic colors.
breaks:list- A set of scalar values at which contacts (changes in color) should be placed.
init:int- The index of the first "drillhole" in the x-direction.
xstep:int- The separation between "drillholes" in the x-direction.
pval:float- The probability that an observation is a scalar value (rather than just a gradient) constraint.
cmap:str- The name of the Matplotlib colormap to use for sampling colors that determine where geological contacts are. Must be a discrete colormap.
seed:int- Random seed to facilitate reproducible results.
bind:bool- If True, the sampled constraints will be added to their respective fields (and global constraints bound to the GeoModel).
Notes
For 3D grids, constraints are sampled on x-z sections at y = 25%, 50% and 75% of the model y extent (1500 m).
Returns
dict- Sampled constraints keyed by structure ID (int) and
'property'when present. Empty whenbind=True(constraints are attached toMand its events).
def seuss(shape=None, nlayers=6, **kwargs)-
Expand source code
def seuss(shape=None, nlayers=6, **kwargs): """ Return a synthetic model with layered stratigraphy, a dyke, an intrusion domain boundary, and two listric faults (one older, one younger), matching the "Seuss" GeoModel from the Building Analytical Models tutorial. Parameters ---------- shape : tuple The width and height of the generated data. Default (1500, 700) matches the tutorial notebook. nlayers : int Number of stratigraphic layers (isosurfaces) in each package. Default 6. Keywords --------- All keywords are passed to `curlew.synthetic.sample(...)` when generating constraints. Returns -------- M : curlew.geology.geomodel.GeoModel Geomodel with listric faults and domain boundaries (name='Seuss'). """ shape = shape or EXTENT_2D G = _make_grid(shape) dims = _physical_extent(shape) # First stratigraphic package (layer-cake) s0 = strati( "s0", C=LinearField("f0", input_dim=2, gradient=(0.1, 0.9)), ) sy1 = np.linspace(0, dims[1], nlayers) sx1 = np.full_like(sy1, dims[0] / 10) for i, (x, y) in enumerate(zip(sx1, sy1)): s0.addIsosurface("S%d" % (i + 1), seed=np.array([x, y])) # Dyke s1 = sheet( "s1", C=LinearField( "f1", input_dim=2, origin=np.array([750.0, 300.0]), gradient=np.array([-0.5, 0.5]), ), contact=(-20, 20), aperture=2, ) # Domain boundary: intrusion (constant -2) below, s0 and s1 above intrusion = GeoEvent("i0", field=-2, type=int) # quick and easy way to define a constant scalar field d1 = domainBoundary( "d1", C=QuadraticField( "d1f", input_dim=2, origin=np.array([400.0, 50.0]), gradient=np.array([0.4, 1.0]), curve=(0, 0.002), ), bound=0, lt=[intrusion], gt=[s0, s1], ) # Older listric fault f1 = fault( "f1", C=ListricField( "f1f", input_dim=2, origin=np.array([650.0, 700.0]), fault_floor=0.0, fault_ceil=700.0, curvature_rate=0.006, ), width=1e-9, n_steps=3, offset=100, shortening=np.array([1, -1]), ) # Second stratigraphic package (unconformable cover) s2 = strati( "s2", C=LinearField( "f2", input_dim=2, origin=np.array([0.0, 500.0]), gradient=np.array([0.0, 1.0]), ), ) sy2 = np.linspace(350, dims[1], nlayers) sx2 = np.full_like(sy2, 9 * dims[0] / 10) for i, (x, y) in enumerate(zip(sx2, sy2)): s2.addIsosurface("S%d" % (i + 1), seed=np.array([x, y])) # Unconformity domain boundary: d1 and f1 below, s2 above d2 = domainBoundary( "d2", C=LinearField( "d2f", input_dim=2, origin=np.array([700.0, 500.0]), gradient=np.array([0.2, 0.9]), ), bound=0, lt=[d1, f1], gt=[s2], ) f2 = fault('f2', type=ListricField, contact=0.0, offset=100, # The displacement magnitude n_steps=3, # apply displacement in several steps, due to high curvature shortening=[1, -1], input_dim=2, origin=np.array([250.0, 700.0]), fault_ceil=700., # Defines the ceiling of the fault fault_floor=0., # Defines the floor of the fault width=1e-9, curvature_rate=0.006 # Defines the steepness of the decay ) M = GeoModel([d2, f2], grid=G, name="Seuss") s = M.predict(G) sample(s, M, pv="rgb", **kwargs) return MReturn a synthetic model with layered stratigraphy, a dyke, an intrusion domain boundary, and two listric faults (one older, one younger), matching the "Seuss" GeoModel from the Building Analytical Models tutorial.
Parameters
shape:tuple- The width and height of the generated data. Default (1500, 700) matches the tutorial notebook.
nlayers:int- Number of stratigraphic layers (isosurfaces) in each package. Default 6.
Keywords
All keywords are passed to <code><a title="curlew.synthetic.sample" href="#curlew.synthetic.sample">sample()</a>(...)</code> when generating constraints.Returns
M:GeoModel- Geomodel with listric faults and domain boundaries (name='Seuss').
def steno(shape=None, **kwargs)-
Expand source code
def steno( shape=None, **kwargs ): """ Return a synthetic model with a slightly curved layer-cake stratigraphy. Parameters ---------- shape : tuple The width and height of the generated data. Keywords --------- All keywords are passed to `curlew.synthetic.sample(...)` Returns -------- M : curlew.geology.geomodel.GeoModel Geomodel of the synthetic model with sampled constraints bound to events. """ G = _make_grid(shape) extent = _physical_extent(shape) ndim = _model_ndim(shape) s0 = strati('s0', C=QuadraticField( 'f0', input_dim=ndim, gradient=_g2((0.00001, 1), ndim), curve=_g2((-0.00005, 0), ndim), origin=_p2((1000, 500), ndim) ) ) for seed, name in _isosurface_seeds(extent, ndim, 5): s0.addIsosurface(name, seed=seed) M = GeoModel([s0], grid=G, name="steno") s = s0.predict(G) sample(s, M, pv='rgb', **kwargs) return MReturn a synthetic model with a slightly curved layer-cake stratigraphy.
Parameters
shape:tuple- The width and height of the generated data.
Keywords
All keywords are passed to <code><a title="curlew.synthetic.sample" href="#curlew.synthetic.sample">sample()</a>(...)</code>Returns
M:GeoModel- Geomodel of the synthetic model with sampled constraints bound to events.
def walker(shape=None,
width=[60, 50, 40, 50, 40],
pos=[0, 100, 200, 400, 600],
addFault=True,
**kwargs)-
Expand source code
def walker( shape=None, width=[60,50,40,50,40], pos=[0,100,200,400,600], addFault=True, **kwargs ): """ Return a synthetic model with a layer-cake stratigraphy cut by several parallel dykes. Parameters ---------- shape : tuple The width and height of the generated data. width : float A list specifying the dyke widths. pos : float A list specifying the dyke positions in the x-axis. Must have the same length as width. addFault : bool If True, add a fault crosscutting the dykes and stratigraphy. Default is False. Keywords --------- All keywords are passed to `curlew.synthetic.sample(...)` Returns -------- M : curlew.geology.geomodel.GeoModel Geomodel of the synthetic model with sampled constraints bound to events. """ G = _make_grid(shape) extent = _physical_extent(shape) ndim = _model_ndim(shape) o = _p2((0, extent[1] / 2 if ndim == 2 else extent[2] / 2), ndim) s0 = strati('s0', C=QuadraticField( 'f0', input_dim=ndim, curve=_g2((-0.00005, 0), ndim), origin=o ) ) contact = [(pos[i], pos[i]+width[i]) for i in range(len(pos))] s1 = sheet( 's1', C=LinearField( 'f1', input_dim=ndim, origin=o, gradient=_g2((0.5, 0.5), ndim) ), contact=contact ) for seed, name in _isosurface_seeds(extent, ndim, 5): s0.addIsosurface(name, seed=seed) if addFault: s2 = fault( 's2', C=LinearField( 'f2', input_dim=ndim, origin=_p2((1050, 500), ndim), gradient=_g2((-np.cos( np.deg2rad(35) ), np.sin( np.deg2rad(35) )), ndim) ), offset=100, shortening=_g2((0, -1), ndim) ) M = GeoModel( [s0, s1, s2], grid=G, name='newcastle' ) else: M = GeoModel( [s0, s1], grid=G, name='walker' ) kwargs['pval'] = kwargs.get('pval', 1.0) # change default to sample all value constraints Cs = sample(M.predict(G), M, pv='rgb', bind=False, **kwargs) C1 = sample(s1.predict(G), M, pv='rgb', breaks=np.hstack(contact), bind=False, **kwargs) ordered = _csets_in_order(Cs) dyke = _csets_in_order(C1)[0] M['s0'].field.bind(ordered[1]) M['s1'].field.bind(dyke) if len(ordered) > 2: M.bind(ordered[2]) return MReturn a synthetic model with a layer-cake stratigraphy cut by several parallel dykes.
Parameters
shape:tuple- The width and height of the generated data.
width:float- A list specifying the dyke widths.
pos:float- A list specifying the dyke positions in the x-axis. Must have the same length as width.
addFault:bool- If True, add a fault crosscutting the dykes and stratigraphy. Default is False.
Keywords
All keywords are passed to <code><a title="curlew.synthetic.sample" href="#curlew.synthetic.sample">sample()</a>(...)</code>Returns
M:GeoModel- Geomodel of the synthetic model with sampled constraints bound to events.