Physics runs in the native kernel. The per-step milling physics migrated into core.dll in
stages: the engagement is scan-converted natively at the substraction completion point, the force
kernel is reached through the same handle with no managed marshal, and the sequential
cutting-temperature and wear chain runs from a native thermal session held per (tool, cutting
parameter) pack. The switch is
EnableNativeMillingPhysics, surfaced runtime-only (not
persisted to project XML) as EnableNativeMillingPhysics
and EnableNativeMillingPhysics, and it now defaults to true.
Public entry points that used to reach the managed kernel still work: without a session pack they
build an ad-hoc physics pack per live (cutting parameter, tool) pair and run natively.
MillingToolPhysicsPack is an immutable record holding one tool's
scalar derivations for one cutting-parameter set — spindle-buckle-to-tip length, observation height,
effective cutting diameter, the bending/Z-deflection pair, the simplified rake angle, the minimum
uncut chip thickness. MachiningSession owns the packs keyed by tool id
(GetToolPhysicsPack) and invalidation is explicit at
the points that know the state changed: every run-op start and the tool-change act, plus
InvalidateToolPhysicsPacks. The corresponding
MillingTool / MillingCutter members are now deliberately uncached pure computations.
Milling-force waveforms are reproducible again. The parallel per-step force build read lazily
built scalar caches on the shared tool objects; a thread could pass a cache guard and then read a
value a concurrent writer had stored in between, so two plays of the same NC exported different
forces in the thin-chip window of each tooth pass. The caches were first republished as single
immutable references and then removed in favour of the frozen session pack.
Thermal gating and seeding. The sequential cutting-temperature and wear build now checks
EnablePhysics (spindle temperature deliberately
keeps running), and the tool-change thermal seeding re-arms whenever the incoming chain state has no
flute temperature list — which covers fault and cancel re-seeds, stop-then-replay residue, and
EnablePhysics being switched on mid-session. The shank temperature list is seeded to the exact node
count the thermal FEM builds, so trailing shank nodes no longer sit at 0 K after a tool change.
Cutter geometry is validated up front.GetUpperBeamGeometryIssues collects upper-beam and shank
configuration problems as keyed messages — for example an extended-cylinder beam whose full length
sits below the flute height (Cutter-UpperBeam--ExtendedCylinderFullLengthTooShort), which
inverts the shank solid and makes the shank thermal model unbuildable. They are reported once per tool at BeginSession and at
each tool change, instead of surfacing later as a null-reference cascade inside the thermal physics
with nothing naming the beam.
RakeFaceCuttingPara3d no longer throws on a six-field parameter string (the guard read the
seventh element behind a >= 6 check), and the published coefficient index mappings are corrected:
the LocalProfileMillingPara(Vec3d, Vec3d) constructor maps (x,y,z) to (Ksr, Kst, Ksa) /
(Kpr, Kpt, Kpa), and the 2d element index range is 0–3 with 0=Ksc, 1=Ksn, 2=Kpc, 3=Kpn.
Training diagnostics name their cause. The per-step warnings split into
Train-StepLuggage--Unreadable (the step luggage row could not be read back) and
Train-StepEngagement--Missing (the row is present but the engagement was never built because
physics was inactive at simulation time). The gather pass counts both against the eligible steps:
silent at zero, one summary warning at or below
MissingEngagementAbortRatio (default 0.25), and a
configuration error above it. The breakdown in that summary is the diagnosis: misses sitting on
the engagement level mean the play itself ran with physics inactive, while a scattered few on the
luggage level are the read-back path, where a segment that covers a step but lacks its row is
dropped and answers null once before the next read rebuilds it. Above the ratio the training
aborts instead of continuing on what is left, because a parameter file built from a fraction of
the play is indistinguishable from a sound one once it has been written — its coefficients are
read back later as a real effect. Set the ratio to 1 to never abort. A gather pass that produces
no samples at all now reports immediately rather than throwing inside the SVD solve, separating
“not one step touched the workpiece” from “touched steps whose mapped force data yielded no usable
shots”.
New training knobs.EnableDesignMatrixSolver
(default false) solves the least squares on a thin QR of the design matrix instead of forming the
normal equations, which square the condition number;
DesignMatrixSvdRelativeTol is its truncation cutoff.
EnableCwePhasePairing (default false) determines each
step's rotation phase with a cutter-workpiece-engagement block-pairing detector instead of the
self-bootstrapped lead parameter, for one-flute and symmetric two-flute cutters in light radial side
cuts. ReTrainAnchorOutputScale exposes the virtual
anchor weight.
LastMillingParaTrainResult captures the outcome — kind,
sample flags, outlier ratio, success, output file, parameter name and note, correlation R, filtered
sample count, parameter XML, timestamp — so a caller reads it without re-opening the .mp file.
Time mapping is reworked around absolute wall-clock time.AddTimeDataByFile accepts DateTime windows, stored as
IFileTimeSection forms, and the project-scoped
MappingAnchorDateTime — seeded set-once from the date of the first
controller instant seen — converts controller timestamps onto one run-relative axis.
EndTimecode replaces AccumulatedTime as the canonical
end-of-step time.
CSV timing survives midnight. Step durations derive from full date-bearing instants, so a
multi-day recording no longer produces negative durations and a negative chart time axis, and a
non-physical duration from a spliced recording is clamped with a validation warning instead of
stalling physics evaluation.
Wall-clock time is dense. The trio moved into one optional sub-object,
StepActualTime (Timecode / Instant / IsInterpolated), reached through
ActualTime. On CSV plays every built step is stamped: steps
built from a controller row re-anchor, and the steps between extrapolate along the machine timeline
and are marked interpolated, which makes the actual-time mapper window per-step exact instead of
sparse-anchor scaled. Pure NC plays keep null stamps.
An empty step-shot pairing window is a data gap, not something to interpolate across. The window
builder used to expand outward to the rows bracketing the gap, silently pairing such steps with force
values that were never measured — a training run over a file whose transients had been carved out
produced a plausible correlation and a full set of coefficients derived entirely from fabricated
rows. Such a window now skips its step and one Map-ShotGap--StepsSkipped warning per mapping call
carries the count; a window-edge row is interpolated only when its bracketing rows span at most two
spindle revolutions.
“No cut / No data for step” on freshly-simulated steps is fixed. The bulk step-data readers
cached the absence of rows the writer had not committed yet, so a step that had just been
simulated could report no data until the program was re-run. A covered-but-missing index now drops
the stale segment and re-reads.
New end-of-play warnings, each once per session: Play-Touch--None (the play finished without any
step touching the workpiece), Tool-FluteCount--Zero (physics is on and a milling cutter resolves to
zero flutes, so feed per tooth is undefined), and Play-Physics--None (physics is on and at least one
step touched the workpiece but no touched step carries a physics brief — naming the three things that
gate it: a tool bound to the spindle, the spindle actually rotating, and the workpiece cutting
parameter). That last state previously surfaced one process later, as a training run gathering zero
samples.
Performance is collected in its own section below.