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:
- Group orientation measurements (manually or by automatic clustering).
- Create a DFN whose Fisher mean direction, Fisher K, and (where data permit) size distribution are computed from those groups.
- Override the synthetic intensity with a P32 value derived from real interpreted traces over a chosen outcrop mesh.
- Bias placement so synthetic fracture centres concentrate near a user-chosen region (typically the outcrop the traces were drawn on).
- Score the resulting DFN against the observed traces along three independent axes — length, orientation, intensity — plus a composite fitness score.
- 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 (
CTriangulatedMeshfrom photogrammetry, lidar, or surface fits). - Interpreted trace polylines drawn on those meshes — either as
Polylineobjects in the polyline tree or asGeo Objectitems ofPlane Type = Fracturein the orientations tree. - Optionally, structural orientation measurements (
Orientation Objectitems 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 parameters | Interpretation matching |
|---|---|---|
| Mean direction & K | Manually typed | Computed from selected orientation groups |
| Trace-length distribution | Manually typed | MLE-fitted from circumcircle radii of measurements |
| Intensity (P32) | Manually typed | Derived from observed P21 over a real outcrop |
| Spatial placement | Uniform Poisson | Gaussian falloff around a chosen mesh |
| Provenance | None | Per-set tag stored in the .dfn file |
| Validation | None | KS-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
Polylineitems orGeo Objectitems withPlane Type = Fractureare 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:
- Right-click a polyline group → Auto-cluster Selected Orientations… (optional; only if you have raw measurements that aren't yet in groups).
- Right-click one or more Orientation Group items → Create DFN from Selected Groups. Confirm the per-group Fisher preview.
- Ctrl-click the new DFN, the outcrop mesh, and the trace folder → Override P32 from Selected Traces…. Apply the derived P32.
- Ctrl-click the DFN + the mesh → Set Region Mask Source from
Selection. Switch each set's
Spatial Modelto Region Mask, tuneRegion Mask Falloff, then right-click → Regenerate. - 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:
- Select the parent Polyline Group that contains your traces, and ctrl-click the outcrop mesh they were drawn on.
- 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 / azimuthpair 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 reason | Meaning |
|---|---|
| Off mesh | Trace centroid is too far from any mesh triangle to sample a normal |
| Empty / short | Polyline has fewer than 2 vertices |
| Zero length | All vertices coincident |
| Degenerate tangent | PCA could not extract a stable principal axis |
| Trace parallel to outcrop normal | Cross-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:
- Select one or more Orientation Group items, or any combination of loose Orientation Object items.
- Right-click → Auto-cluster Selected Orientations….
- 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:
- Select the orientation groups you want — typically one per fracture set.
- Right-click → Create DFN from Selected Groups.
- 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)
- 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:
- Ctrl-click the DFN, the outcrop mesh, and the trace folder/polylines.
- Right-click → Override P32 from Selected Traces….
- 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
- 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
- Ctrl-click the DFN and the outcrop mesh.
- 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:
- Set Spatial Model to Region Mask.
- 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 |
|---|---|
| ≤ 1 | Sharp envelope; fractures hug the mesh surface |
| 5 – 50 | Comfortable for typical outcrop scales |
| ≥ 100 | Soft 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:
- Ctrl-click the DFN, the outcrop mesh, and the trace folder/polylines.
- Right-click → Score DFN against Selected Traces….
Three axes are computed:
| Axis | Definition | Score formula |
|---|---|---|
| Length | Trace-length distribution agreement | 1 - KS_D from a two-sample Kolmogorov-Smirnov D statistic |
| Orientation | Fisher-mean alignment + K agreement | Geometric mean of 1 - 2θ/π and `1 - |
| Intensity | P21 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:
- Right-click the DFN.
- Choose Re-derive from Source Orientation Groups.
What happens:
- The recorded group names are looked up in the live orientation tree.
- If every name resolves uniquely, the create flow runs again with exactly those groups against the current project map limits.
- The previous Match Quality result is cleared (it would be stale against the new fractures).
- 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 value | Meaning |
|---|---|
default | Constructor default (0.3); never overridden |
legacy | Loaded from a .dfn file written before provenance existed |
from traces: <mesh name> | Set by Override P32 from Selected Traces |
manual | Reserved for explicit "I edited this" workflows (future) |
Size Distribution Source value | Meaning |
|---|---|
default | No data-driven fit |
fitted from trace circumcircles | MLE+KS fit produced an acceptable distribution from the input measurements' circumcircle diameters |
legacy | Pre-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 Scoreis 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 Scoreis low, check that the synthetic and observed pole sets are genuinely different (use the stereonet to overlay). - If
Intensity Scoreis 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.