04. GPR Fracture & Cavity Mapping¶
This chapter covers the geophysical front end of the repository: the
ground-penetrating-radar (GPR) processing chain that turns a raw B-scan into a
fracture map, the surface fitting and uncertainty ladder that turn that map into
a quantified keep-out volume, and the earthworks reducers that lift a bedrock
surface out of the same picks. The subsystem lives under Quarry/Processing,
Quarry/Ingestion, and Earthworks in the Core assembly, with seven Grasshopper
adapters on the Frahan > Quarry ribbon. It is the geological front end that
chapter 3 (Quarry Block-Cutting) forward-references: chapter 3 takes the fracture
mesh as given input, and this chapter builds it
(docs/thesis/chapters/03_quarry-blockcut.md:21-22).
The whole pipeline is pure managed code. There is no MathNet, no Python runtime,
no native shim on the processing path; the FFT, the Hilbert transform, the
kriging, and the error function are all in-tree. This is a deliberate
constraint: the prototype that the C# Core mirrors was a Python/numpy/scikit-learn
script, and the port to dependency-light managed code is what makes the chain
installable beside the .gha without forcing scipy onto every machine
(RadargramProcessor.cs:9-30, Kriging.cs:8-29). The derivations below reuse
the mathematics validated in the submitted BoEGE paper (Murugean 2026), which
this subsystem underlies.
The core physical model is the constant-velocity time-to-depth conversion. A GPR records two-way travel time \(t\); with electromagnetic velocity \(v\) in the rock, the reflector depth is
where \(c=0.299792458\,\mathrm{m/ns}\) and \(\varepsilon_r\) is the relative
permittivity (marble \(\varepsilon_r\approx9\), \(v\approx0.10\,\mathrm{m/ns}\);
granite \(\varepsilon_r\approx6\), \(v\approx0.12\,\mathrm{m/ns}\)). Velocity is the
single highest-leverage value in the whole chain because every depth scales with
it linearly (RadargramProcessor.cs:27-28; GprPresets.cs:18-19). The
[Algorithm] attribute on the front-end component states the model in one line:
"v=c/sqrt(eps_r); depth=v*t/2. Energy E=|s+iH{s}|^2; fractures are high-E
continuous reflectors, intact stone is low-E"
(GprFractureExtractComponent.cs:43-45).
4.1 Ingestion and the file dispatcher¶
The single canvas-side entry point is GprFileReader.Load, which dispatches by
extension to the format reader: CSV, SEG-Y (.sgy/.segy), MALA (.rd3),
Sensors & Software pulseEKKO (.dt1), IDS GeoRadar (.dt), and GSSI (.dzt)
(GprFileReader.cs:23-46). The proprietary Geoscanners AKULA .gsf format is
explicitly not guessed: the reader raises a NotSupportedException that tells
the user to convert to SEG-Y with GPRSoft or RGPR first, because the binary spec
is closed (GprFileReader.cs:46-51). This is the bridge-not-guess posture: a
wrong header guess on a proprietary container would silently corrupt the depth
axis.
RadargramProcessor.ToGrid builds the regular [samples, traces] amplitude grid
and recovers the true two-way sample interval \(\mathrm{d}t\) in nanoseconds,
velocity-independent, so the caller scales depth with the stone velocity rather
than baking a velocity into ingest. It prefers the reader-supplied
SampleIntervalNs; only when that is unknown does it fall back to recovering
\(\mathrm{d}t\) from the metres-per-sample step at vacuum velocity
(\(\mathrm{d}z=c\,\mathrm{d}t/2\)) (RadargramProcessor.cs:42-75).
Originality.
GprFileReaderand the per-format readers are vendored-library / clean-room depending on the format: each reader implements a published or open binary spec (pulseEKKO DT1/HD is the public-domain USGS OFR 02-166 spec, Lucius and Powers 1999; SEG-Y is the SEG standard). The dispatcher itself is a thin switch and adds no algorithm. No proprietary spec is reverse-engineered.
4.2 The B-scan processing chain¶
RadargramProcessor.Run is the validated chain, mirroring the Python prototype
stage for stage (RadargramProcessor.cs:334-357):
The early stages are elementary 1-D filters: dewow is a high-pass running-mean
subtraction that removes the low-frequency "wow" baseline drift
(Dewow, :100-115); background removal subtracts the mean trace to kill
horizontal banding and the direct air-wave (:152-163); the time-zero mute
zeroes the air-wave / antenna-coupling band (:165-170); t-power gain
multiplies by \((i\,\mathrm{d}t+1)^p\) to compensate spherical divergence and
absorption (:172-182). Each box-mean uses an \(O(\mathrm{len})\) running sum with
numpy 'same' edge semantics so the C# output is bit-comparable to the
prototype (BoxMeanSame, :83-98). The column operations are independent, so
they run as deterministic Parallel.For with thread-local scratch (a fixed
output partition makes the parallel result bit-identical to the serial loop,
:104-114, :295-307).
Originality. The dewow / background / mute / gain / AGC primitives are clean-room standard GPR processing (Annan 2009; Neal 2004), implemented from published signal-processing definitions with no upstream code in the tree. The contribution is the validated ordering and parameterisation, not the filters.
4.2.1 The FFT and Hilbert transform (clean-room numerics)¶
The spectral stages need an exact-length forward and inverse transform that
matches numpy.fft. The in-tree Fft is a radix-2 Cooley-Tukey transform
(Cooley and Tukey 1965) for power-of-two lengths, with a Bluestein chirp-z
fallback for arbitrary lengths so the 2-D Stolt migration and the Hilbert
envelope operate on the exact sample and trace counts; zero-padding would
shift the frequency grid and bias the migration (Fft.cs:30-94). The Bluestein
plan (the chirp and the precomputed kernel spectrum) is length-only, so it is
built once per distinct length and reused across every trace, the single biggest
speed-up for the 986-same-length envelope loop (Fft.cs:96-149).
The instantaneous-energy attribute is the analytic-signal magnitude. For a real trace \(s\), the analytic signal is \(s+i\,\mathcal{H}\{s\}\), where \(\mathcal{H}\) is the Hilbert transform; the instantaneous amplitude (envelope) is its magnitude and the instantaneous energy is the square:
The envelope is computed by the spectral method (Taner, Koehler and Sheriff
1979): forward-FFT the trace, apply the one-sided weighting
\(H=[1,2,2,\dots,2,1,0,\dots,0]\) that doubles the positive frequencies and zeroes
the negatives, inverse-FFT, take the magnitude (AnalyticEnvelope,
Fft.cs:159-185; HilbertEnergy, RadargramProcessor.cs:293-308). The physical
reading is the literature consensus: a fracture or cavity is an impedance
contrast that reflects strongly, while intact stone is the low-energy background
(Porsani et al. 2006; Isakova 2021), so high instantaneous energy is the fracture
proxy.
Originality.
Fftis clean-room: a numerical method (radix-2 + Bluestein) is not copyrightable and the file says so (Fft.cs:16-18). The Hilbert-envelope attribute is the textbook Taner et al. (1979) complex-trace analysis, cited in the front-end[Algorithm](GprFractureExtractComponent.cs:44).
4.2.2 Stolt f-k migration with half-velocity (original derivation)¶
Diffraction hyperbolae and dipping reflectors are mispositioned in the raw B-scan; migration collapses diffractions and moves dipping events to true position. The repository implements Stolt (1978) f-k migration in the exploding-reflector model. The key step is the constant-velocity dispersion relation that maps the recorded temporal frequency \(\omega\) to the vertical wavenumber \(k_z\).
Derivation. A monochromatic plane wave in the exploding-reflector model travels at the migration velocity \(v_m=v/2\) (the half-velocity that converts two-way time to one-way depth). Its dispersion relation links the temporal frequency \(\omega\) to the spatial wavenumbers \((k_x,k_z)\):
Solving for the source frequency at a target output wavenumber \(k_z=\omega/v_m\) gives the Stolt remap: each output cell \((k_z,k_x)\) samples the recorded spectrum at
and because the remap stretches the frequency axis non-uniformly, energy must be rescaled by the Stolt Jacobian \(\partial\omega'/\partial k_z\):
The implementation builds the 2-D spectrum on the exact grid, applies the
remap by linear interpolation in \(\omega\), multiplies by \(J\), and inverse-FFTs
(StoltMigration, RadargramProcessor.cs:199-291; remap and Jacobian at
:259-267). The half-velocity \(v_m=v/2\) is set explicitly at :208, the depth
floor that the rest of the chain depends on.
The repository adds a cosine dip-taper that the bare Stolt operator lacks. Steep-dip events near the evanescent boundary \(|v_m k_x|/|\omega|\to1\) alias; the taper smoothly zeroes the spectrum there:
which suppresses steep-dip aliasing before the remap (:235-244). The
frequency grids \(\omega\) and \(k_x\) follow the numpy.fftfreq ordering exactly
(FftFreq, :361-370) so the migrated section matches the validated prototype.
Originality. Stolt migration is clean-room from Stolt (1978), cited in the
[Algorithm]attribute (GprFractureExtractComponent.cs:44). The half-velocity exploding-reflector model and the Jacobian are the published method. The cosine dip-taper is a small evolved anti-alias addition on top of the bare operator; it is an engineering delta, not a new migration.
4.2.3 Depth equalisation¶
A locally strong deep reflector still reads weaker than a shallow one because
absolute energy decays with depth. DepthEqualizeEnergy normalises each depth row
by a smoothed per-row median, so a deep fracture surfaces at the same relative
energy as a shallow one (:310-332). This is the relative-amplitude display
behind the energy section; it is a display normalisation, not a detector, and it
is preset-toggleable.
4.3 Fracture extraction: high energy plus dip-aware continuity¶
FractureExtractor.Extract consumes the instantaneous-energy section and applies
two rules from the reviewed literature (FractureExtractor.cs:8-24).
Rule 1, high-energy local maxima. A sample is a candidate if its normalised
energy exceeds a high quantile (default \(0.985\)) and it is a per-column local
maximum (:64-74). The quantile is a robust threshold: the top 1.5% of energy is
the reflector population, the rest is intact-stone background.
Rule 2, the USGS lateral-continuity criterion. A genuine reflector is
laterally continuous; an isolated bright spot is clutter or a point
diffraction. The USGS Mirror Lake protocol keeps a pick only if at least a
minimum number of like picks fall within a horizontal window (the granite default
is \(\ge40\) traces \(\approx1\,\mathrm{m}\)) in a narrow depth band
(:18-21, :30-35). The repository evolves the flat-horizon version of this
test into a dip-aware filter.
Original derivation: dip-aware continuity. A horizontal running-sum counts
support only along sub-horizontal reflectors and rejects dipping shear zones that
are real. To follow a dip, the extractor shears the mask so a reflector of slope
\(\sigma\) (samples per trace) becomes horizontal, counts support along the now-flat
event over the trace window, unshears, and keeps the maximum support over a
set of candidate slopes (:76-130). The slope range is bounded by the maximum
dip the filter follows. Mapping a dip angle \(\theta\) to a sample slope uses the
depth-per-sample \(\Delta=v\,\mathrm{d}t/2\) and the trace spacing \(\mathrm{d}x\):
with \(\theta_{\max}=45^\circ\) by default; events steeper than the gate find no
matching slope and are rejected, enforcing the USGS \(<45^\circ\) continuity gate
(:80-91, DipMaxDeg :36-40). The kept picks carry depth \(v(i\,\mathrm{d}t)/2\)
and a normalised-energy confidence (:132-143), then convert to
world-coordinate GprReflectorPick records using the trace positions
(:146-159).

The granite spine (example 3) runs this chain end-to-end on the real Grimsel ISC
data (MALA GX160, AU and VE tunnels, CC-BY-4.0): with the granite_160 preset it
extracts 1472 picks on AU and 1485 on VE, at \(\mathrm{d}t=0.4464\,\mathrm{ns}\)
and \(\mathrm{d}x=0.0498\,\mathrm{m}\)
(examples/03_gpr_fracture_granite/README.md:14).
Originality.
FractureExtractoris evolved-fork. The high-energy + USGS-continuity base is clean-room from the cited literature (USGS Mirror Lake WRIR 99-4018C; Porsani 2006; Isakova 2021,GprFractureExtractComponent.cs:44). The dip-aware shear-count continuity that follows dipping shear zones while gating steep events is the measured delta over the flat-horizon USGS test. Fronted by GPR Fracture Extract (GUIDA7E0B0F1-0C0F-4A16-9E3D-0FACE0FACE02,GprFractureExtractComponent.cs:66),Exposure=secondary.
4.3.1 Stone-by-frequency presets¶
GprPresets holds the parameter sets that produced the validated 3-D models, one
per stone-type x antenna-frequency, with window sizes as fractions of the
trace sample count so a preset transfers across acquisitions (GprPresets.cs:7-25).
Two presets are empirically tuned on real data (marble_600 on Bondua Botticino,
granite_160 on Doetsch Grimsel); a granite frequency family (25-1200 MHz) and
the travertine / andesite / limestone presets carry paper-measured velocities but
extrapolated filter windows. The IsEmpirical flag records which is which so the
GH component can warn the user (GprPresets.cs:22-24, :86, :111). The marble
preset notably narrows the continuity span to 27 traces (\(\approx0.65\,\mathrm{m}\))
because marble fractures (stylolites, veins) are shorter than granite shear zones
(\(\approx0.9\,\mathrm{m}\) measured), surfacing marble's genuine short reflectors
from the same energy bar (GprPresets.cs:90-94).
Originality. clean-room parameter catalogue, no algorithm. The presets are calibration data; the
IsEmpiricalhonesty flag distinguishes validated from literature-default values.
4.4 From picks to surfaces¶
FractureSurface builds fracture surfaces from picks by two paths
(FractureSurface.cs:8-25). The managed loft path extrudes an ordered fracture
polyline along strike, or lofts adjacent parallel section-lines across a survey
grid onto a common X grid; the surface orientation follows the reflector
(sub-horizontal stays sub-horizontal, dipping stays dipping) rather than forcing a
vertical sheet (Loft, :42-70; LoftAcrossLines, :77-110). The reconstruct
path takes an unordered fracture point cloud and runs geogram screened-Poisson
(Kazhdan and Hoppe 2013) first, falling back to CGAL advancing-front for open
sheets (TryReconstructFromCloud, :112-139). The heavy 3-D reconstruction is
the only place this chapter touches a native shim, and it is optional with a clear
error when absent.
Originality. The loft path is clean-room elementary surface construction. The reconstruction path is wrapper-of-native over the geogram (BSD-3, with bundled Kazhdan PoissonRecon MIT) and CGAL (GPL) shims, reached out-of-process; only the dispatch is ours, and the CGAL route is quarantined per the licensing register.
4.5 The uncertainty ladder and safe yield¶
The deliverable is not a fracture surface, it is an honest fracture surface:
how far the reconstructed surface can deviate from the true fracture, propagated
through the pipeline, so a quarry can set a keep-out margin and pack blocks only
into provably-intact rock. FractureUncertainty is that tolerance ladder
(FractureUncertainty.cs:6-33). The per-location 1-sigma position uncertainty
combines three independent contributions in quadrature:
Reconstruction sigma (original derivation). The GPR time-to-depth conversion \(\mathrm{depth}=v\,t/2\) with \(v=c/\sqrt{\varepsilon_r}\) has three error sources. First, a relative velocity error that grows with depth: differentiating \(v\propto\varepsilon_r^{-1/2}\) gives
so the depth term is \(\mathrm{depth}\cdot\sigma_v/v\) (VelocityRelUncertainty,
:48-53). Second, the vertical-resolution floor \(\lambda/4\), with
\(\lambda/4=v/(4f)\) (LambdaQuarter, :39-45). Third, the time-zero pick
ambiguity \(v\,\sigma_{t_0}/2\), where the first-break-to-first-apex window is a
rectangular distribution \(\sigma_{t_0}=(t_{\text{apex}}-t_{\text{break}})/(2\sqrt3)\)
(TimeZeroSigma, RectTimeZeroSigma, :55-65). The combined reconstruction
sigma is
The velocity term leads at quarry depth (Porsani 2006 reports
\(\pm8.5\text{-}9.5\%\) at 25 m); the time-zero term leads near the surface (Xie,
Lai and Derobert 2021); \(\lambda/4\) is a floor, not the dominant term
(DepthSigma, :67-81). Passing \(\sigma_{t_0}=0\) reproduces the original
two-term form, so the time-zero rung is an additive evolution of the earlier
ladder (:67-72).
Interpolation sigma. Between scan lines the surface is interpolated, and the
interpolation has its own uncertainty: zero at a pick, growing in the gaps. This
is supplied by the kriging posterior standard deviation. The Kriging class is
simple kriging on mean-centred data with a Gaussian covariance
\(C(h)=\text{sill}\cdot e^{-(h/\text{range})^2}\) and a nugget; the posterior
variance at a query point is
i.e. the prior variance minus what the data explain, via the Cholesky factor
(Kriging.cs:19-29). It is the managed replacement for the prototype's
scikit-learn GaussianProcessRegressor, exact and shim-free because kriging is
linear algebra (Cressie 1993; Rasmussen and Williams 2006).
Mesh sigma. The triangulation cuts the true curved surface by the chord
sagitta, \(\sigma_{\text{mesh}}=(h^2/8)\,\kappa\) for edge length \(h\) and curvature
\(\kappa\) (MeshSigma, :83-85).
The confidence metric. The optimisation target is not sigma itself but the confidence: the probability that the fracture lies within a fabrication tolerance \(T\), assuming a zero-mean Gaussian deviation,
averaged over the surface (ConfidenceWithin, :95-100). The error function is
the Abramowitz-Stegun 7.1.26 rational approximation (\(|\text{error}|<1.5\times10^{-7}\)),
again to avoid a MathNet dependency (Erf, :222-230). Lowering sigma (calibrate
velocity, denser scan lines, higher frequency, finer mesh) raises confidence; the
ladder quantifies each trade.
The detection rung (original derivation). A position sigma only matters for a
fracture that is seen. A missed fracture has no sigma but is the real yield
risk, so the ladder adds a detection model grounded in the imaging literature
(Molron et al. 2020 Aspo; Dorn et al. 2012). The minimum detectable area is
Fresnel-zone limited and grows with depth, \(A_{\min}\approx(\lambda/4)\cdot
\mathrm{depth}/2\) above a shallow resolution floor (MinDetectableArea,
:111-128). The detection probability factorises over dip, aperture, and size:
with \(\eta\) the imaging ceiling (\(\approx0.80\) open, Molron; \(0.91\) transmissive,
Dorn), \(p_{\text{dip}}=1\) for sub-horizontal fractures smoothstepping to \(0.1\) by
\(75^\circ\) (surface GPR poorly images sub-vertical fractures), and a sealed-factor
penalty for mineral-filled fractures (DetectionProbability, :130-150). The
effective confidence caps position confidence by detection completeness,
\(C_{\text{eff}}=P_{\text{det}}\cdot\textsf{confidence}\), so a low detection
probability limits trust however precisely the seen fractures are located
(EffectiveConfidence, :152-160; Summarise, :191-220).

Example 9 is the full quarry decision. The GPR fracture surfaces (from the granite
spine) bound the intact zones; Fracture Block Pack (GUID
A7E0B0F3-0C0F-4A16-9E3D-0FACE0FACE04) packs fixed-size dimension blocks into
each zone with an inward Fracture Clearance wired to the GPR position sigma,
so no block sits within the measured uncertainty of a fracture. Toggling
uncertainty-safe off gives the optimistic geometric yield; on gives the
uncertainty-safe yield (FractureBlockPackComponent.cs:10-25;
examples/09_uncertainty_safe_yield/README.md:12-17).
Originality.
FractureUncertaintyis original-research (A-candidate). The three-rung position ladder, the depth-growing velocity term plus time-zero plus \(\lambda/4\) decomposition, and the detection rung with the depth-aware Fresnel floor and the \(P_{\text{det}}\) factorisation are the Frahan contribution; the underlying physics is cited (Porsani 2006; Xie 2021; Molron 2020; Dorn 2012).Krigingis clean-room ordinary kriging (Cressie 1993; Rasmussen and Williams 2006). The surface-and-ladder front end is GPR Fracture Surfaces 3D (GUIDA7E0B0F2-0C0F-4A16-9E3D-0FACE0FACE03,GprFractureSurface3DComponent.cs:30), which clusters the pick cloud, kriges each fracture, and colour-maps \(\sigma_{\text{total}}\) green-to-red.
4.6 RecoveryCascade: multi-scale crack-aware recovery¶
The fracture map feeds the block-cutting solver of chapter 3, but a single-scale
packer discards every block a fracture crosses. RecoveryCascade recovers value
from those blocks by running the cutter at progressively finer scales: at each
scale BlockCutOpt is solved on the region, the non-intersected blocks are
recovered, and every cracked block is fed back into the same engine at the
next finer scale, cutting around the fracture, until the remnant falls below the
smallest marketable size (RecoveryCascade.cs:9-37).
Original derivation: the recovery recursion. The value recovered from a tested region \(R\) at scale \(s\) is
where the kept / cracked partition of the winning grid is decided by
!bvh.AnyTriangleIntersects against a single shared immutable fracture BVH
(RecoveryCascade.cs:25-29, :91-119). The recursion depth is capped at the
number of scales, so it cannot run unbounded (:74). Crucially, with a single
ScaleSpec the cascade recovers exactly the non-intersected blocks
BlockCutOptSolver.Solve finds, with the same winning pose and the same
intersection predicate, so it reduces to BlockCutOpt 2020 exactly at scale 1
and is a faithful superset (:21-24). It is grounded in the conditional
two-scale (Yarahmadi 2018), usable-leftover (Cherri 2009), and staged-guillotine
(Gilmore and Gomory 1965) literatures.
Originality.
RecoveryCascadeis evolved-fork. The 3-D recursive reject-recover cascade extends the single-scale BlockCutOpt baseline (chapter 3) to which it provably reduces. The header now credits the companion paper (Murugean 2026) for the unified cascade rather than self-labelling "novel"; the earlier unsoftened "novel" wording was flagged E9 in the originality audit (docs/thesis/90_originality.md:188). No GH consumer wires it yet (see Status). The separateFractureBlockPackGH component is a facade-over-primitives self-contained recovery engine that does not callRecoveryCascade, a silent-disagreement risk also tracked in the register.
4.7 Earthworks: bedrock surface and the overburden strip¶
The same GPR picks lift a bedrock surface for the overburden strip. The
deepest continuous strong reflector below the weathered cover is the top of fresh
rock (Porsani profiles; Bondua bedrock). BedrockSurface.DeepestReflectorPoints
reduces a pick set to the deepest qualifying reflector per \((x,y)\) column and
converts depth to world elevation \(z_r=z_{\text{ground}}(x,y)-\mathrm{depth}\)
(BedrockSurface.cs:7-19, :51-93). The picks come from one or more survey
lines, scattered in \((x,y)\).
TinMerge.ResampleOntoVertices fuses those sparse bedrock picks onto the dense
ground TIN so a downstream prism-difference can compute the overburden volume,
because that consumer requires both surfaces sampled on the same
triangulation (TinMerge.cs:8-27). It resamples by k-nearest inverse-distance
weighting (Shepard 1968):
over the k nearest source picks within a scale-relative radius (a multiple of
the median source spacing), with a uniform-grid spatial index, and flags target
vertices with no source inside the radius as NaN so the caller can clip them
(TinMerge.cs:54-122). Coordinates are recentered first so UTM / quarry-scale
\((x,y)\) do not lose mantissa precision (the GeometryNumerics T1 rule, :20-23).
TinPeelFilter is the upstream scan-cleaner. A raw Delaunay or Poisson
reconstruction fills the whole convex hull, so concave shorelines and data gaps
grow long thin cap triangles and near-vertical gap webs that are not real
terrain. The filter iteratively peels border triangles satisfying any of three
predicates, then drops connected components below a minimum size
(TinPeelFilter.cs:7-29): a long edge \(\max(e_0,e_1,e_2)^2>(k\,m)^2\) where
\(m\) is the median 2-D edge and \(k=3\) aggressive / 10 careful; a near-vertical
facet with normal tilt \(>85^\circ\); and a cap / sliver with an interior
angle opposite the border edge \(>140^\circ\) (:18-23, ShouldPeel, :139-163).
The thresholds are relative to the local median edge, so the same filter works at
any survey scale (the scale-relative-epsilon principle).
Originality.
TinPeelFilteris clean-room (the border-peel logic ported from the Fade2D land-survey reference'speelOffIf, no upstream code, cited in the Clean Scan Mesh[Algorithm],CleanScanMeshComponent.cs:29-31, GUIDA7E0B0F1-0C0F-4A16-9E3D-0FACE0FACE03).TinMergeis clean-room k-NN IDW (Shepard 1968) with a scale-relative radius.BedrockSurfaceis clean-room: pure reduction and datum shift, no FFT. The bedrock front end is GPR Bedrock Surface (GUIDA7E0B0F1-0C0F-4A16-9E3D-0FACE0FACE04,GprBedrockSurfaceComponent.cs:33-35, citing Porsani 2006 / Isakova 2021 for the top-of-rock reflector and Shepard 1968 for IDW).
4.8 The vector counterpart: surface fracture maps¶
GPR sees fractures with depth; a shapefile carries the mapped surface trace
network, the cheapest fracture data a quarry has (drone photo plus tracing).
Example 26 reads a real ESRI Shapefile of Loviisa rapakivi-granite fracture traces
through the Frahan > Quarry > Ingestion vector reader and renders the strike map.

The reader returns 708 traces / 6483 vertices, total length 1593.5 m, CRS
EUREF_FIN_TM35FIN (EPSG:3067), with two conjugate sets peaking at \(\approx15^\circ\)
(NNE) and \(\approx105\text{-}120^\circ\) (ESE) (Chudasama 2022, CC-BY-4.0;
examples/26_loviisa_surface_fractures/README.md:26-30). The strike histogram is
the input a quarry needs to orient block cuts away from the dominant joint set,
and combined with a GPR depth survey it constrains the 3-D intact-block volume.
Originality. vendored-library reader (NetTopologySuite.IO.Esri, ESRI Shapefile / OGC Simple Features); the strike binning and rendering are clean-room, no new algorithm.
4.9 Status & what's left¶
- Example 3 figure is a radargram, not the extracted-pick overlay. The folder
ships
03_gpr_radargram_AU.png(the migrated section) plus the two.ghcanvases, but no rendered fracture-pick / 3-D surface PNG; the README marks the example "pending live regeneration" with repath, stage groups, and a shaded viewport capture still to do (examples/03_gpr_fracture_granite/README.md:22-26). Severity: medium (documentation / figure gap, the chain itself is validated to the 1472 / 1485 pick counts). RecoveryCascadehas no GH consumer. The recursion is implemented and tested in Core but no canvas component wires it, and the shippedFractureBlockPackcomponent runs its own self-contained recovery engine instead, a silent disagreement risk if the two diverge (docs/thesis/90_originality.md:66,:68). Severity: high..gsfis read-only via conversion. Geoscanners AKULA stays unsupported by design; the user must export to SEG-Y with GPRSoft or RGPR first (GprFileReader.cs:46-51). This blocks any dataset that ships only.gsf. Severity: medium (a real Tamil Nadu charnockite data path depends on it).- Literature-default presets are unvalidated end-to-end. Only
marble_600andgranite_160areIsEmpirical=true; the granite frequency family and the travertine / andesite / limestone presets carry paper velocities but extrapolated filter windows (GprPresets.cs:22-24,:110-111). The component warns, but a user running an unvalidated preset gets uncalibrated continuity spans. Severity: medium. - Reconstruction path needs native shims.
TryReconstructFromCloudreturns a clear error when geogram / CGAL are absent, and the CGAL route is GPL, quarantined out-of-process (FractureSurface.cs:131-138; licensing register E3/E4). The default install has no reconstruction; loft-only surfaces are the fallback. Severity: low (managed loft path covers the common ordered-line case). - AABB child region in the cascade is exact only for axis-aligned blocks.
AabbOfis exact for psi-only (axis-aligned) oriented blocks; a fully tilted pose feeds the finer scale a slightly loose axis-aligned bound (RecoveryCascade.cs:122-123). Severity: low (conservative, never drops a real block).
References (this chapter)¶
- Stolt, R.H. (1978). Migration by Fourier transform. Geophysics 43(1):23-48. DOI 10.1190/1.1440826. [R146]
- Taner, M.T., Koehler, F., Sheriff, R.E. (1979). Complex seismic trace analysis. Geophysics 44(6):1041-1063. DOI 10.1190/1.1440994. [R147]
- Cooley, J.W., Tukey, J.W. (1965). An algorithm for the machine calculation of complex Fourier series. Mathematics of Computation 19(90):297-301. DOI 10.1090/S0025-5718-1965-0178586-1.
- Porsani, J.L., Sauck, W.A., Junior, A.O.S. (2006). GPR for mapping fractures and as a guide for the extraction of ornamental granite from a quarry. Journal of Applied Geophysics 58:177-187. DOI 10.1016/j.jappgeo.2005.05.010. [R34]
- Molron, J., Linde, N., Baron, L., Selroos, J.O., Darcel, C., Davy, P. (2020). Which fractures are imaged with ground penetrating radar? Engineering Geology 273:105674. DOI 10.1016/j.enggeo.2020.105674. [R36]
- Dorn, C., Linde, N., Doetsch, J., Le Borgne, T., Bour, O. (2012). Fracture imaging within a granitic rock aquifer using multiple-offset single-hole and cross-hole GPR reflection data. Journal of Applied Geophysics 78:123-132. DOI 10.1016/j.jappgeo.2011.01.010. [R37]
- Xie, F., Lai, W.W.L., Derobert, X. (2021). GPR-based depth measurement of buried objects based on constrained least-square fitting. Measurement 168:108330. DOI 10.1016/j.measurement.2020.108330. [R41]
- Annan, A.P. (2009). Electromagnetic principles of ground penetrating radar. In: Jol, H.M. (ed.) Ground Penetrating Radar: Theory and Applications. Elsevier, pp 3-40. [R39]
- Neal, A. (2004). Ground-penetrating radar and its use in sedimentology. Earth-Science Reviews 66:261-330. DOI 10.1016/j.earscirev.2004.01.004. [R40]
- Bondua, S., Monteiro Klen, A., Pilone, M., Asimopolos, L., Asimopolos, N.S. (2024). A set of ground penetrating radar measures from quarries. Data 9(3):42. DOI 10.3390/data9030042. [R44]
- Huber, E., Hans, G. (2018). RGPR — an open-source package to process and visualize GPR data. 17th International Conference on GPR, IEEE. DOI 10.1109/ICGPR.2018.8441658. [R43]
- Lucius, J.E., Powers, M.H. (1999). USGS Open-File Report 02-166: GPR data-format documentation (pulseEKKO DT1/HD spec). [R45]
- Shepard, D. (1968). A two-dimensional interpolation function for irregularly-spaced data. Proc. 23rd ACM National Conference, pp 517-524. DOI 10.1145/800186.810616.
- Cressie, N.A.C. (1993). Statistics for Spatial Data. Wiley. DOI 10.1002/9781119115151. [R119]
- Rasmussen, C.E., Williams, C.K.I. (2006). Gaussian Processes for Machine Learning. MIT Press. DOI 10.7551/mitpress/3206.001.0001. [R120]
- Kazhdan, M., Hoppe, H. (2013). Screened Poisson surface reconstruction. ACM Transactions on Graphics 32(3):29. DOI 10.1145/2487228.2487237. [R91]
- Yarahmadi, R., Bagherpour, R., Taherian, S.G., Sousa, L.M.O. (2018). Discontinuity modelling and rock block geometry identification to optimize production in dimension stone quarries. Engineering Geology 232:22-33. DOI 10.1016/j.enggeo.2017.11.006. [R20]
- Cherri, A.C., Arenales, M.N., Yanasse, H.H. (2009). The one-dimensional cutting stock problem with usable leftover. European Journal of Operational Research 196:897-908. DOI 10.1016/j.ejor.2008.04.039. [R12]
- Gilmore, P.C., Gomory, R.E. (1965). Multistage cutting stock problems of two and more dimensions. Operations Research 13:94-120. DOI 10.1287/opre.13.1.94. [R11]
- Chudasama, B. (2022). Loviisa rapakivi-granite fracture and lineament dataset, southern Finland. Zenodo, CC-BY 4.0. [R53]
- Murugean, L. (2026). GPR-to-block-yield optimization for fractured dimension-stone quarries (submitted, Bulletin of Engineering Geology and the Environment; reproducibility deposit). DOI 10.5281/zenodo.20608279. [R144]
- USGS (1999). Mirror Lake GPR continuity protocol, Water-Resources Investigations Report 99-4018C (>=40-trace lateral-continuity criterion).
- Isakova, E. (2021). GPR survey of fractured Karelia granite (OKO-2, 150 / 1200 MHz antennas).