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).
- Validate the resulting DFN by cutting it with the outcrop mesh and comparing the synthetic traces against the observed ones along three independent axes — length, orientation, intensity — per set and pooled.
- 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 | Trace-to-trace comparison against observations, with a visual report |
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…. The DFN is cut with the mesh, the synthetic traces are compared against yours, and a visual report opens. See DFN — Validating Against Outcrop.
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–6).
What gets recorded:
- The source orientation group names are stored on the DFN. They drive the Re-derive action (Section 8) 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 — Deriving statistics from measured sources
Derive DFN Statistics… is the fuller alternative to the quick paths below. It lets each fracture set take each statistic from a named source, shows every set side by side, and cross-checks the intensity routes against one another.
To run it: select the DFN, right-click → Derive DFN Statistics…, then choose the outcrop mesh at the top of the dialog.
Why it matters: size and intensity are confounded
Derived the traditional way, both fracture size and P32 come from the same mapped traces. So when a model produces too little trace length you cannot tell whether the fractures are too small or too few — the two explanations are indistinguishable from the data you used.
Taking intensity from a scanline or a P21 attribute breaks that. The measured intensity fixes the total fracture area, so the size distribution only has to supply the shape. That matters because shape is the part that survives the outcrop cut, and scale is not.
What each source can tell you
| Source | Supplies | Is it a measurement? |
|---|---|---|
| Orientation group | orientation | Yes — Fisher statistics on your measured poles |
| Trace group | orientation, intensity (P21), size | Yes — measured per set on the mapped area |
| Scanline | orientation, intensity (P10) | Yes — a count along a known length, Terzaghi-weighted |
Mesh attribute P21 | intensity | A measured whole-mesh total, apportioned across sets |
Mesh attribute P32 | intensity | No — see below |
Its numerator is the area of your orientation glyphs, so it already encodes whatever fracture size those glyphs were drawn at. Using it to constrain fracture size closes a loop: the model would only ever agree with what you assumed. VRGS marks it [assumed, not measured] and refuses it for the size route. It remains useful as a cross-check.
Attribute layers come from Fracture Intensity Mapping; if none exist, run that first.
Is this source really sampling this set?
A scanline measured across a different structure will still hand you a number for any set you point it at. Whichever of its fractures happen to fall inside the match window get counted, the arithmetic proceeds, and the result looks exactly like a measurement of that set.
So every source that carries orientations — scanline, trace group — is checked against the set it is being used for, normally the set whose orientation came from an orientation group. Two things are reported, because they mean different things:
| Signal | Meaning |
|---|---|
| % of its measurements match | How much of the source actually belongs to this set. A low figure means the estimate rests on a handful of fractures that could as easily be strays from a neighbour. |
| degrees off | Where the matching measurements sit relative to the set's mean pole. They are inside the window by construction, so it is their position within it that is informative — a population centred 18° off a 30° window is probably a different set that overlaps it. |
A flagged source is still shown, with its numbers, and is excluded from the cross-check's agreement band — it would otherwise be evidence about a different set. If it is flagged, tighten the set match angle, or check that you have paired the scanline with the right set.
A P21/P32 layer is a scalar field per mesh vertex. It carries no orientations, so there is nothing to compare against the set and this check cannot be made at all. The dialog says [orientation cannot be checked] rather than staying silent, because "not checked" and "checked and fine" must not look the same. It is one more reason an attribute is weaker evidence than a scanline.
The cross-check panel
Select a single set and the panel lists every intensity source that can inform it, each converted to P32 so they are directly comparable, with the raw P21 or P10 shown alongside.
Agreement between two independent routes is worth far more than either alone. Disagreement is a finding no single source can produce — it usually means one sampler is barely seeing that set. Check the set match angle first, then whether the scanline direction or the outcrop face lies close to the set's own plane. Values that rested on the blind-zone cap are flagged, and only genuine measurements set the agreement band: letting an assumed value widen the band would let the assumption set the tolerance on the check meant to test it.
Trace length to fracture size
A mapped trace is the chord where a fracture meets the outcrop, not the fracture's diameter. The outcrop rarely cuts through a fracture's centre, so traces are shorter than the fractures that made them. Fitting trace lengths and handing them to the generator as radii therefore makes every fracture too small.
| Model | Meaning |
|---|---|
| Random chords (default) | The outcrop cut fractures at random positions — correct for an exposure large relative to the fractures. Scales sizes by 4/π ≈ 1.27. |
| Traces are full diameters | No correction. Only right if traces happen to cut fracture centres; reproduces models built before this setting existed. |
| Calibrate against outcrop | Reserved; not yet implemented. Selecting it leaves sizes uncorrected and says so. |
Only the scale is corrected. A power-law exponent is left alone, because the cut preserves the tail exponent but not the mean — which is exactly why the exponent is the parameter worth trusting from an outcrop, and the scale is the one that needs measured intensity to pin it.
Applying
Apply to DFN writes the derived values to the sets and records where each came from. Sets whose chosen source produced no value are named, not silently skipped — a half-derived network that reads as fully derived is worse than one that plainly is not.
Statistics are not fractures, so you are then offered a regenerate. The previous match score is cleared either way, since it describes a network that no longer exists.
Selecting several sets at once applies your choices to all of them; the source boxes read (leave unchanged) so that touching nothing changes nothing.
Bed-confined sets: do not apply the same truncation twice
If your outcrop is bedded and the traces you fitted sizes from were themselves stopping at bedding planes, those trace lengths already carry the bedding truncation. Attaching the same bedding planes as model surfaces then applies it a second time, and the generated fractures come out shorter than the ones you mapped.
Two coherent ways to model a bedded outcrop, and it is worth being explicit about which you are doing:
| Approach | Size distribution | Surfaces |
|---|---|---|
| Let the surfaces do it | Fit sizes from traces measured within one bed, or supply the unconfined size distribution you believe in | Attach the bedding planes; fracture height comes out as bed height |
| Let the data do it | Fit sizes from the bedded outcrop as mapped — the truncation is already in the numbers | Attach the bedding planes as transparent (crossing probability 1), or not at all |
The first is the better model when you want to change bed thickness and see what happens, or extrapolate to a bed you did not map. The second is simpler and matches the outcrop by construction — but only for the bed thicknesses you actually measured.
Mixing them is the failure case, and it is quiet: the validation report shows synthetic traces systematically shorter than the mapped ones, which reads like a size-distribution problem rather than a double-counted truncation.
5 — Overriding P32 from observed traces (quick path)
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 for the network (=
(4/π)·P21·ξ), and each set's share of it - Current vs proposed P32 for each set
- Click Yes to apply; the DFN is regenerated with the new intensity.
Stereological conversion:
P21 and P32 are related by P21 = P32 · E[sin θ], where θ is the angle
between a fracture's pole and the outcrop's. A fracture lying parallel to the
face leaves no trace on it; one cutting perpendicular leaves the longest. For
an isotropic population E[sin θ] = π/4, which gives the familiar
P32 = (4/π) · P21.
VRGS computes ξ = (π/4) / E[sin θ] per set, from that set's own mean pole
and Fisher K against the outcrop's mean pole, so a set oblique to the face is
correctly credited with contributing less of what you mapped. ξ = 1
reproduces the isotropic conversion exactly.
A set lying nearly parallel to the face is barely sampled by it, and its derived intensity is unreliable however the arithmetic is done — the 2D analogue of the Terzaghi blind zone. The factor is capped, and the dialog says so when the cap binds.
Apportionment across sets:
The derived P32 is the intensity of the whole network implied by the observed P21, and it is shared between the sets, not written into each of them. Giving every set the full value would give an N-set model N times the intensity the outcrop showed. With no information about how the traces divide between sets, the sets are taken to share a common P32, weighted by how visible each is on that face.
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.
6 — 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.
7 — Validating against observed traces
Once the DFN is generated, validate it against the same observed traces to quantify how well the synthetic network reproduces the interpretation.
To run it:
- Ctrl-click the DFN, the outcrop mesh, and the trace folder/polylines.
- Right-click → Score DFN against Selected Traces….
The DFN is cut against the mesh, a summary dialog appears, and a visual report is written next to the project and opened.
DFN — Validating Against Outcrop covers the report panel by panel, how to read the statistics, and what to change when an axis fails. This section covers only what the command does.
Trace-to-trace, not fracture-to-trace
The synthetic side comes from intersecting the DFN with the outcrop mesh you selected and chaining the intersection segments into traces. Both populations are then chord lengths, measured the same way, over the same surface.
This matters because a fracture and a trace are different quantities. A fracture is a disc in 3D; a trace is the chord where it meets the outcrop. Chords are shorter than diameters, most fractures never reach the outcrop at all, and traces reaching the edge of the exposure are cut short. Comparing diameters against trace lengths — as earlier versions did — penalises a model that is completely correct.
What is compared
| Axis | How |
|---|---|
| Length | Two-sample Kolmogorov-Smirnov D and p-value on the trace-length populations, plus empirical CDF and Q-Q curves |
| Orientation | Orientation-tensor principal axes, plus an equal-area pole-density misfit comparing the whole distribution rather than a mean and a concentration |
| Intensity | P21 and P10 measured on both sides, plus realized against target P32 |
Every comparison is reported per fracture set as well as pooled. Observed traces are attributed to the set whose mean pole they best match within 30°; those matching no set are counted and reported rather than discarded, because a large residual means the model is missing a set.
Verdicts come from p-values
A KS D of 0.08 is unremarkable with 50 traces and decisive with 5000, so a similarity score computed from D alone is not interpretable. The verdict is taken from the p-value instead, with D reported alongside so you can see how big the difference is as well as whether it is real.
Reading the property bar:
The Match Quality group on the DFN property bar still shows the composite and per-axis scores, now computed from the like-for-like populations. The report is the fuller picture; the property bar is the at-a-glance version.
8 — 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].
Validation reports a poor match on an obviously good fit
Check the per-axis panels in the report rather than the composite; a single weak axis drags a geometric mean down.
- Length — read the Q-Q plot. Bending only at the right-hand end is a large-fracture problem (exponent or size maximum); a uniform offset is a whole-distribution problem.
- Orientation — a high density misfit with well-aligned principal axes means the fabric shape differs (cluster vs girdle), which is Fisher K.
- Intensity — a P21 ratio far from 1 usually means the traces and mesh are not co-located, or a set lies nearly parallel to the face and is barely sampled by it.
- Many unassigned traces — your interpretation contains a fracture set the model does not have. That is a modelling gap, not a scoring artefact.
Validation reports no synthetic traces
The fracture network and the outcrop do not overlap in space, or the fractures are too small to reach the surface. Check the DFN's domain against the mesh's extent.
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 (axial)
Poles are axes: p and -p describe the same plane. Averaging them as
vectors lets two descriptions of one plane cancel, which is not a corner case —
because dip is reported in [0, 90], a subvertical set has two valid
descriptions sitting at opposite ends of one near-horizontal axis, and a
dispersed sample splits between them.
The mean direction therefore comes from the orientation (scatter) tensor, which is invariant to the sign of each pole:
T = (1/n) Σ p_i p_iᵀ (3×3, symmetric)
mean_pole = principal eigenvector of T
R = |Σ ±p_i| / n ∈ [0, 1] (each p_i folded into mean_pole's hemisphere)
K ≈ R(3 - R²) / (1 - R²) Fisher (1953); Banerjee et al. (2005), d = 3
K is clamped to [0.1, 1000] for numerical stability.
The eigenvalues of T also give the Woodcock fabric parameters:
K_w = ln(e₁/e₂) / ln(e₂/e₃) (above 1 a cluster, below 1 a girdle) and
C = ln(e₁/e₃) (fabric strength).
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
A sampling surface only exposes a fracture in proportion to how steeply the fracture cuts it:
P21 = P32 · E[sin θ] θ = angle between fracture pole and surface pole
For an isotropic population E[sin θ] = π/4, giving the familiar
P32 = (4/π) · P21. The orientation factor carries the departure from that:
ξ = (π/4) / E[sin θ]
P32 = (4/π) · P21 · ξ
E[sin θ] is computed per set by quadrature over that set's Fisher
distribution against the outcrop's area-weighted mean pole, so ξ = 1 exactly
reproduces the isotropic case. It is capped at 10 (and 1/10): a set lying
nearly parallel to the face is barely sampled by it, so its derived intensity
cannot be trusted whatever the arithmetic says — the 2D analogue of the
Terzaghi blind zone.
The derived P32 is the network total implied by the observation, shared between the sets in proportion to how visible each is on that face. Writing the full value into every set would give an N-set model N times the intensity the outcrop actually showed.
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).
These are the property-bar scores. They are convenient at a glance but
lossy: 1 - D is not interpretable on its own, because the same D means
different things at different sample sizes, and collapsing a pole population to
a mean direction and a concentration cannot distinguish a cluster from a girdle.
The validation report computes the richer statistics — KS p-values, a pole density misfit, and measured P21 on both sides — per set as well as pooled. See DFN — Validating Against Outcrop.
Virtual outcrop sampling
Synthetic traces come from intersecting each fracture polygon with the outcrop triangulation (Möller triangle-triangle, accelerated by the fracture BVH), deduplicating the resulting segments and chaining them into polylines. A trace endpoint lying on a boundary edge of the mesh — an edge belonging to only one triangle — is flagged censored, because the fracture continues beyond the exposure and the measured length is a lower bound.
This is the same code path as the Mesh Intersections command, so the traces drawn in the 3D view and the traces the validator scores are identical by construction.
See also
- DFN — Validating Against Outcrop — reading the validation report and acting on it.
- 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.