Skip to main content

DFN: Matching Interpretations

This guide describes how to build a Discrete Fracture Network (DFN) whose statistics are derived from interpreted geological data — orientation measurements, trace polylines, faults, outcrop meshes — rather than from hand-typed parameters. The end-to-end pipeline lets you:

  1. Group orientation measurements (manually or by automatic clustering).
  2. Create a DFN whose Fisher mean direction, Fisher K, and (where data permit) size distribution are computed from those groups.
  3. Override the synthetic intensity with a P32 value derived from real interpreted traces over a chosen outcrop mesh.
  4. Bias placement so synthetic fracture centres concentrate near a user-chosen region (typically the outcrop the traces were drawn on).
  5. Score the resulting DFN against the observed traces along three independent axes — length, orientation, intensity — plus a composite fitness score.
  6. Re-derive the DFN whenever the underlying interpretation changes.

If you are new to VRGS DFNs, read the main DFN user guide first; this document complements it and assumes familiarity with the basics of fracture sets, P32, and the project tree layout.

When to use this workflow

Reach for the interpretation-matching workflow when you have:

  • One or more outcrop meshes (CTriangulatedMesh from photogrammetry, lidar, or surface fits).
  • Interpreted trace polylines drawn on those meshes — either as Polyline objects in the polyline tree or as Geo Object items of Plane Type = Fracture in the orientations tree.
  • Optionally, structural orientation measurements (Orientation Object items in the orientations tree).

The workflow keeps the synthetic DFN tied to the underlying interpretation in three ways that pure parameter editing cannot:

Tied to interpretation?Pure parametersInterpretation matching
Mean direction & KManually typedComputed from selected orientation groups
Trace-length distributionManually typedMLE-fitted from circumcircle radii of measurements
Intensity (P32)Manually typedDerived from observed P21 over a real outcrop
Spatial placementUniform PoissonGaussian falloff around a chosen mesh
ProvenanceNonePer-set tag stored in the .dfn file
ValidationNoneKS-based composite score against observations

Tree layout & terminology

Throughout this guide we refer to the project-tree containers used by the workflow:

  • Orientation Group — a folder under the Orientations tree node containing one or more Orientation Object measurements. One group becomes one fracture set in the DFN.
  • Polyline / Geo Object — interpreted trace polylines. Either the generic Polyline items or Geo Object items with Plane Type = Fracture are accepted as trace input.
  • Triangulated Mesh — the outcrop the traces are drawn on. Used both for surface-area computations (P21) and as the source for the region mask.

The "mesh" in this guide is always exactly one mesh per command. If your outcrop is split across several meshes, merge them before scoring.

Quick start

A typical session looks like this:

  1. Right-click a polyline group → Auto-cluster Selected Orientations… (optional; only if you have raw measurements that aren't yet in groups).
  2. Right-click one or more Orientation Group items → Create DFN from Selected Groups. Confirm the per-group Fisher preview.
  3. Ctrl-click the new DFN, the outcrop mesh, and the trace folder → Override P32 from Selected Traces…. Apply the derived P32.
  4. Ctrl-click the DFN + the mesh → Set Region Mask Source from Selection. Switch each set's Spatial Model to Region Mask, tune Region Mask Falloff, then right-click → Regenerate.
  5. Ctrl-click the DFN, the mesh, and the trace folder → Score DFN against Selected Traces…. Read the composite + per-axis scores in the Match Quality group of the property bar.

Each of those steps is described in detail below.

1 — Estimating virtual orientations from traces

If you have trace polylines but no orientation measurements, VRGS can synthesise orientations by combining each trace's tangent direction with the local outcrop normal at its centroid.

To run it:

  1. Select the parent Polyline Group that contains your traces, and ctrl-click the outcrop mesh they were drawn on.
  2. Right-click and choose Estimate Orientations from Selected Traces.

What happens:

  • The trace polyline is reduced to its principal axis (PCA largest eigenvector) — this is the trace tangent.
  • A BSP tree over the mesh's LOD-0 triangles finds the closest triangle to the trace centroid; its surface normal becomes the outcrop normal.
  • The fracture-plane pole = normalize(tangent × outcrop_normal).
  • A dip / azimuth pair is derived from the pole using geological convention (lower-hemisphere flip).
  • A new Orientation Group is created named "Virtual orientations from <mesh name>", populated with one Orientation Object per accepted trace.

Diagnostics in the result message box:

Reject reasonMeaning
Off meshTrace centroid is too far from any mesh triangle to sample a normal
Empty / shortPolyline has fewer than 2 vertices
Zero lengthAll vertices coincident
Degenerate tangentPCA could not extract a stable principal axis
Trace parallel to outcrop normalCross-product is near zero (trace runs perpendicular to the outcrop face)

These virtual orientations flow into Create DFN from Selected Groups exactly like any other orientation group.

2 — Auto-clustering orientations

Clustering picks out fracture sets from a busy stereonet without manual selection. v1 uses k-means on the unit pole vectors with axial folding — anti-parallel poles (+v, -v) represent the same plane and are treated as identical.

To run it:

  1. Select one or more Orientation Group items, or any combination of loose Orientation Object items.
  2. Right-click → Auto-cluster Selected Orientations….
  3. Confirm. v1 produces 3 clusters; future versions will prompt for k.

What you get:

Three new orientation groups, each named "Cluster N (M measurements)". The originals are not modified — clustering creates fresh copies, so you can run it multiple times with different inputs.

Edge cases:

  • If fewer than 6 measurements are selected, the command refuses (need ≥2 per cluster × 3 clusters).
  • Empty clusters can occur on small or pathological inputs; they are reported in the iteration count but produce no group.

3 — Creating a DFN from orientation groups

The Create flow is the entry point that ties all the orientation-side work together. It accepts one or more Orientation Group items and produces a fully-generated DFN.

To run it:

  1. Select the orientation groups you want — typically one per fracture set.
  2. Right-click → Create DFN from Selected Groups.
  3. A confirmation dialog summarises each group:
    • Number of measurements
    • Computed Fisher mean dip and azimuth
    • Fisher concentration K
    • A note about which groups will be skipped (need ≥3 measurements)
  4. Click Yes to proceed; No aborts without creating anything.

What gets computed:

  • Mean orientation & K per group via Fisher statistics on unit poles.
  • Size distribution when ≥10 trace lengths (circumcircle diameters of the input measurements) are available — fitted to power-law, lognormal, or exponential families with a Kolmogorov-Smirnov goodness-of-fit test. The best-fitting family is selected automatically.
  • A default P32 = 0.3 and Spatial Model = Poisson are applied initially; both are easy to override afterwards (Sections 4 & 5).

What gets recorded:

  • The source orientation group names are stored on the DFN. They drive the Re-derive action (Section 7) and survive save/reload.
  • Per-set provenance tags ("default" / "fitted from trace circumcircles") are written so the property bar shows where each value came from.

4 — Overriding P32 from observed traces

The default P32 of 0.3 is rarely the right number for a real outcrop. This action measures P21 (trace length per outcrop area) from your interpreted traces and converts it to P32 stereologically.

To run it:

  1. Ctrl-click the DFN, the outcrop mesh, and the trace folder/polylines.
  2. Right-click → Override P32 from Selected Traces….
  3. The dialog shows:
    • Total trace count and length
    • Outcrop area
    • Measured P21
    • Derived P32 (= (4/π)·P21·ξ with ξ = 1 isotropic)
    • Current vs proposed P32 for each set
  4. Click Yes to apply; the DFN is regenerated with the new intensity.

Stereological assumption:

The conversion uses the isotropic-orientation factor ξ = 1. This is exact when the fracture orientation distribution is isotropic; in practice it is a serviceable approximation for most natural fracture populations. A future release will compute orientation-aware ξ per set from each set's Fisher K.

Provenance:

After applying, each set's P32 Source field reads "from traces: <mesh name>". Edit the P32 manually in the property bar and the field stays at the trace-derived label until you re-run the command — there is no automatic switch to "manual" on inline edits in v1, but the numeric value still reflects the edit.

5 — Region-mask placement biasing

By default, fracture centres are placed via Poisson process across the full DFN domain. The region-mask spatial model concentrates centres near a user-chosen mesh — typically the outcrop the traces were drawn on, so the synthetic network honours the geometry of the area you actually mapped.

Setting the source mesh

  1. Ctrl-click the DFN and the outcrop mesh.
  2. Right-click → Set Region Mask Source from Selection.

The Region Mask Source row at the top of the DFN property bar now shows the chosen mesh's name. The mask itself is built lazily from the mesh on the next regeneration.

Activating the mask per set

In the property bar for each fracture set:

  1. Set Spatial Model to Region Mask.
  2. Adjust Region Mask Falloff — the half-width of the placement- likelihood envelope around the mesh surface.

Regenerating

Right-click the DFN → Regenerate. Centres are now sampled with acceptance probability:

P_accept(p) = exp(-d² / falloff²) when d <= 5 · falloff
= 0 otherwise

where d is the distance from the candidate point to the nearest mesh triangle.

Tuning falloff

Falloff (m)Effect
≤ 1Sharp envelope; fractures hug the mesh surface
5 – 50Comfortable for typical outcrop scales
≥ 100Soft envelope; fractures fade into the surrounding domain

If the rejection sampler can't fill the requested fracture count within its attempt cap (50× count), it tops up the remainder with plain Poisson sampling and writes a warning to the debug log. This means you will always get the configured number of fractures; the placement — not the count — is what degrades when the mask is too tight.

Mask source persistence

The mesh name is persisted with the DFN. On project reload, the next Regenerate rebuilds the SDF mask from the resolved mesh. If the mesh has been renamed or removed, the mask silently falls back to plain Poisson; the user gets a warning in the debug log.

6 — Scoring against observed traces

Once the DFN is generated (matched or not), score it against the same observed traces to quantify how well the synthetic network reproduces the observed one.

To run it:

  1. Ctrl-click the DFN, the outcrop mesh, and the trace folder/polylines.
  2. Right-click → Score DFN against Selected Traces….

Three axes are computed:

AxisDefinitionScore formula
LengthTrace-length distribution agreement1 - KS_D from a two-sample Kolmogorov-Smirnov D statistic
OrientationFisher-mean alignment + K agreementGeometric mean of 1 - 2θ/π and `1 -
IntensityP21 agreement`1 -

Where θ is the axial angle between the two Fisher means (in [0, π/2]; perpendicular planes therefore score 0).

Composite fitness:

composite = exp(Σ wᵢ · log(scoreᵢ))

Weighted geometric mean across the three axes. Default weights are 1:1:1; a single zero-scoring axis drags the composite to zero (the intent: a hopeless mismatch on any one axis means a bad overall fit).

Reading the property bar:

The Match Quality group on the DFN property bar shows:

  • Composite + per-axis scores
  • KS statistic D (raw)
  • Mean angle between Fisher means (in degrees)
  • Fisher K observed and synthetic
  • P21 observed and synthetic
  • Number of observed traces and synthetic fractures

v1 simplification:

The synthetic side currently uses 2 × radius for length and (π/4) × P32 for synthetic P21 — i.e., fracture diameters and the DFN's stored P32 — rather than the rigorous fracture-mesh trace map that would account for outcrop truncation. The misfit math itself is correct; a follow-up phase will swap in true synthetic traces. In the meantime, expect the synthetic length distribution to be biased high relative to the observed one (since real traces are chords through fractures, not full diameters).

7 — Re-deriving from source orientation groups

When you change the underlying orientation data — adding measurements, re-clustering, fixing typos — re-derive the DFN to refresh its stats without manually selecting in the tree.

Prerequisite: the DFN must have been created via the standard Create from Selected Groups flow (this captures the source group names automatically).

To run it:

  1. Right-click the DFN.
  2. Choose Re-derive from Source Orientation Groups.

What happens:

  1. The recorded group names are looked up in the live orientation tree.
  2. If every name resolves uniquely, the create flow runs again with exactly those groups against the current project map limits.
  3. The previous Match Quality result is cleared (it would be stale against the new fractures).
  4. A summary message reports the new set/fracture count.

Failure modes:

  • Missing: the named group no longer exists in the tree (renamed, deleted).
  • Ambiguous: more than one group has that name.

In either case the action aborts without modifying the DFN, with a message listing the offending names.

Property-bar reference

After creating a DFN, the property bar groups everything in one place:

  • Fracture Network (top level)
    • Name, total fractures, fracture sets, P32 intensity, domain
    • Region Mask Source (Phase 4)
  • Domain (subgroup)
  • Proximity Filter (subgroup) — see the main DFN guide
  • Slicers (subgroup) — see the main DFN guide
  • Fracture Sets (subgroup, per-set)
    • Mean orientation (dip, azimuth, Fisher K)
    • Size distribution (min/max radius, exponent or lognormal mean/stddev)
      • Size Distribution combo (Power Law / Lognormal / Exponential / Uniform)
      • P32 Intensity + provenance label
      • Size Distribution Source (read-only provenance)
    • Aspect ratio
    • Spatial Model combo (Poisson / Clustered / Stratified / Region Mask)
    • Cluster Radius (used when Spatial = Clustered)
    • Region Mask Falloff (used when Spatial = Region Mask)
    • Termination Rule combo (None / Truncate at Boundary / Abutting)
    • Abutting Probability (used when Termination = Abutting)
  • Source Orientation Groups (Phase 6)
  • Match Quality (Phase 5)

Provenance tags

Every fracture set carries two free-form provenance strings, persisted with the DFN:

P32 Source valueMeaning
defaultConstructor default (0.3); never overridden
legacyLoaded from a .dfn file written before provenance existed
from traces: <mesh name>Set by Override P32 from Selected Traces
manualReserved for explicit "I edited this" workflows (future)
Size Distribution Source valueMeaning
defaultNo data-driven fit
fitted from trace circumcirclesMLE+KS fit produced an acceptable distribution from the input measurements' circumcircle diameters
legacyPre-provenance file

Save / reload behaviour

The .dfn binary file is extended with three optional trailing blocks that pre-existing readers happily ignore:

  • PROV — per-set provenance strings
  • RGNM — per-set region-mask falloff + the network-wide mesh name
  • SRCG — the names of the orientation groups used to build the DFN

The mask SDF grid itself is not persisted — it is rebuilt from the named source mesh on the next regeneration. The misfit (Match Quality) is also not persisted; re-score after loading.

Older .dfn files load unchanged: missing trailing blocks default to legacy / default values without warnings.

Troubleshooting

"Could not estimate any orientations from N trace(s)"

The most common cause is Off mesh — the centroid of each trace is too far from any triangle in the chosen mesh. Verify you picked the right outcrop and that the traces were drawn on it (not on a hidden LOD or a deleted parent).

"Cannot derive P32: outcrop area = 0"

The selected mesh has no LOD-0 triangles, or the BSP tree could not be built. Re-import the mesh, or ensure the mesh has a non-empty m_triangle_List.m_TRList[0].

Score composite is near zero on an obviously good fit

A single axis scoring zero will drag the composite to zero. Check the per-axis breakdown:

  • If Length Score is low, the synthetic-side simplification (full fracture diameter vs trace chord) is likely the culprit. The Length Score will improve when the rigorous mesh-intersection synthetic side ships in a follow-up phase.
  • If Orientation Score is low, check that the synthetic and observed pole sets are genuinely different (use the stereonet to overlay).
  • If Intensity Score is low, the P32 override may have produced a value far from the observed P21·(4/π) — possibly because traces and mesh weren't co-located.

Region-mask regeneration looks identical to plain Poisson

The fallback message in the debug log will say "Region-mask rejection sampler fell back to Poisson" if the falloff was too tight. Increase Region Mask Falloff until the rejection sampler succeeds without falling back.

Re-derive reports "missing" or "ambiguous" group

The orientation group's name must be unique in the tree at re-derive time. Rename clashes or recreate any deleted groups before retrying.

Mathematical reference

Fisher statistics

Given unit pole vectors p_i:

mean_pole = normalize(Σ p_i) (axial: flip to common hemisphere first)
R = |Σ p_i| / n ∈ [0, 1]
K ≈ EstimateConcentration(R) per Mardia & Jupp (2000), piecewise

K is clamped to [0.1, 1000] for numerical stability.

Trace tangent (PCA)

For a polyline of vertices v_i:

mean = (1/n) Σ v_i
C[i,j] = (1/n) Σ (v_k - mean)_i (v_k - mean)_j

The tangent is the eigenvector of C with the largest eigenvalue. The plane normal (used for diagnostic only) is the eigenvector with the smallest eigenvalue.

Fracture pole from trace + outcrop

pole = normalize(tangent × outcrop_normal)

Rejected when |tangent × outcrop_normal| < 0.05 (≈ trace within 3° of the outcrop normal, no plane recoverable).

P21 → P32 (isotropic)

P32 ≈ (4/π) · P21 · ξ

with ξ = 1 in v1. ξ corrects for the orientation of the fracture population relative to the sampling surface; a future release will compute it per set from the Fisher K.

Region-mask acceptance

P_accept(p) = exp(-d² / falloff²) when d <= 5 · falloff
= 0 otherwise

d is queried by trilinear interpolation from a sdf::DenseSdfGrid<float> built once per mesh selection.

Misfit composite

composite = exp( w_l · log(L) + w_o · log(O) + w_i · log(I) ) (weights normalised to sum 1)

with each per-axis score in [0, 1]:

L = 1 - KS_D (two-sample Kolmogorov-Smirnov)
O = sqrt(direction_similarity · K_similarity)
direction_similarity = 1 - 2·θ/π (θ = axial angle between Fisher means)
K_similarity = 1 - |dK| / max(K_obs, K_syn)
I = clamp(1 - |P21_obs - P21_syn| / P21_obs, 0, 1)

Zero-weighted axes drop out of the composite; a single zero-scored axis forces the composite to zero (the geometric mean's defining property).

See also

  • DFN User Guide — the foundational doc on generation, visualisation, intersections, attributes, propagation, slicers, and stress analysis.
  • Structural Measurements — how orientation objects and groups are created from data.
  • Polylines — how trace polylines are drawn on outcrop meshes.