Table of Contents

Workflow: NC Optimization

This workflow describes how to generate optimized NC files from a physics-based simulation. The optimizer adjusts feed rates to keep physical quantities (spindle power, torque, thermal stress, cutting force) within specified safety limits while maximizing machining efficiency.

flowchart TD
    Prereq["Prerequisites<br>(simulation with physics,<br>cutting parameters)"]
    Config["Configure optimization options"]
    Simulate["Run simulation"]
    Output["Generate optimized NC files"]
    Verify["Verify optimization results"]

    Prereq --> Config --> Simulate --> Output --> Verify

1. Prerequisites

NC optimization requires a simulation environment with physics enabled and valid cutting parameters:

EnablePhysics = true;
LoadCuttingParaByFile("Material.mp");
Prerequisite Description
Physics enabled EnablePhysics must be true
Cutting parameters Workpiece must have loaded milling coefficients (see Workflow: Milling Force Parameter Training)
Valid tool definitions Tool geometry, flute count, and material properties configured
Note

Optimization is based on an ideal geometric model. If the workpiece is a casting or has installation errors, configure a conservatively larger workpiece geometry to prevent misidentification of cutting vs. non-cutting regions.


2. Configure Optimization Options

Feed Rate Control

Property Description Default
OptEnableFeedrate Enable sequential feed rate optimization true
OptEnableInterpolation Re-interpolation for smoother acceleration/deceleration —
OptRapidFeed_mmdmin Feed rate for non-cutting regions (mm/min) —
OptMinFeedrate_mmdmin Minimum cutting-region feed rate (mm/min) —
EnableSourceFeedrateFloor Floor each step's feed rate at a share of the feed rate the source program commanded on it false
SourceFeedrateFloorRatio That share, 0 to 1 0.8
OptMaxFeedrate_mmdmin Maximum cutting-region feed rate (mm/min) —
OptMaxAcceleration_mmds2 Acceleration/deceleration limit (mm/s²) —
OptFeedrateAssignmentRatio Re-interpolation trigger threshold —

The two source-feedrate options have no Opt* shortcut; a script sets them on NcOptOption, before the program is played:

NcOptOption.EnableSourceFeedrateFloor = true;
NcOptOption.SourceFeedrateFloorRatio = 0.8;

Below 1 the physics-based constraints can still slow a peak-load step down to that share of its source feed, which is where the tool-life gain comes from; at 1 the optimizer only speeds steps up. See Bounding the Slow-Down of a Proven Program.

Extended Distance

Property Description
OptExtendedPreDistance_mm Pre-distance for equivalent calculation of cutting regions (mm)
OptExtendedPostDistance_mm Post-distance for equivalent calculation of cutting regions (mm)

Safety Factors (Physics-Based Constraints)

Property Description
OptSpindlePowerSafetyFactor Spindle power safety factor (0 = ignore)
OptSpindleTorqueSafetyFactor Spindle torque safety factor (0 = ignore)
OptThermalYieldSafetyFactor Thermal yield safety factor (0 = ignore)
OptPreferedForce_N Target cutting force (N)
Note

Target value = 100% / Safety factor. For example, a safety factor of 1.5 means the physical quantity targets ~67% of the limit.

Constraint Priority

In cutting regions, constraints are applied in this order:

  1. Direct feed rate constraints (min/max feed rate, min/max feed per tooth from tool settings, the source-feedrate floor)
  2. Acceleration/deceleration constraints (OptMaxAcceleration_mmds2)
  3. Physics-based constraints (spindle power, torque, thermal yield, preferred force)

Where several ceilings apply, the lowest feed rate is used.

A floor above the ceiling neither fails nor lowers the feed: where the composed minimum feed per tooth exceeds the composed maximum — a minimum feed rate, a minimum feed per tooth, the cutter's minimum uncut chip thickness or the source-feedrate floor sitting above the maximum — the boundary collapses onto the minimum, and the step is solved there. The physics-based constraints do not go below a floor either: a step held up by one runs above the load its target asked for, and for the source-feedrate floor the run reports how many (NcOpt-Feed--SourceFloorOverTarget).

Script Command Example

OptEnableFeedrate = true;
OptEnableInterpolation = true;
OptRapidFeed_mmdmin = 4000;
OptMinFeedrate_mmdmin = 100;
OptMaxFeedrate_mmdmin = 4000;
OptMaxAcceleration_mmds2 = 10;
OptExtendedPreDistance_mm = 3;
OptExtendedPostDistance_mm = 2;
OptSpindlePowerSafetyFactor = 1.5;
OptSpindleTorqueSafetyFactor = 1.5;
OptThermalYieldSafetyFactor = 0;
OptPreferedForce_N = double.PositiveInfinity;

Settings Embedded in NC Code

Optimization settings can be set from a script embedded in the program's own comments, so a setting changes at the line it applies from. The @@ marker runs the script before that block's motion, and the comment character is the controller's own — parentheses below, on a Fanuc-family controller (Script Commands in NC Code):

N0110 X-3.064 Y6.378 (@@OptMaxAcceleration_mmds2=10;)
N0150 G01 X-3.068 Y40.776 (@@OptMaxAcceleration_mmds2=100; OptMaxFeedrate_mmdmin=12000;)

An embedded optimization setting changes how the steps from that line on are optimized, not how they are simulated: the play records each change against the step it happened at, and OptimizeToFiles solves each step against the setting in force where that step was built.


3. Run Simulation

Configuration can be interleaved between NC files. Settings apply to the files that follow:

OptRapidFeed_mmdmin = 4000;
PlayNcFile("NC/file1.nc");

OptRapidFeed_mmdmin = 8000;
PlayNcFile("NC/file2.nc");
Note

One OptimizeToFiles covers every file played since the steps were last cleared, by ResetRuntime or the transport's Reset. A PowerReset, or a switch of the controller brand or preset, after the first of those files makes it write nothing (What the Run Reports): optimize before the reset.

Excluding Lines from Optimization

To keep one NC line at the feed rate its program commanded:

N0140 G03 X-2.66 Y38.193 I-103.796 J7.172 (@@Preserve();)

Preserve covers the line it is written on and nothing else: it releases the optimizer again at that line's end, on its own, so it takes no closing command.

A range is opened with BeginPreserve before the first line's motion, and closed with EndPreserve after the last line's — the @@^ marker:

N0140 G03 X-2.66 Y38.193 (@@BeginPreserve();)
N0150 G01 X-3.068 Y40.776
N0160 X-3.555 Y43.338 (@@^EndPreserve();)

EndPreserve() releases the optimizer the moment it runs, which is what the marker on its line decides. After the block, as above, the range covers all three lines. Before the block — written @@ — it would release the optimizer ahead of its own line's motion, and N0160 would be optimized like any other line while N0140 and N0150 stayed preserved.

A range still open when its program's play ends — no EndPreserve() reached, or a Stop inside the range — closes there, so the next program is optimized as its own lines say.

Warning

Do not combine UpdateNcOptOption inside the SessionStepBuilt event with NC-embedded optimization commands. This may cause undefined behavior due to parallel computation.


4. Generate Optimized NC Files

OptimizeToFiles writes the optimized NC programs:

OptimizeToFiles("Cache/Opt-[NcName]");

The [NcName] template is replaced with each input NC file name.

What the Run Reports

An optimization reports itself to the Shell tab of the Session Messages panel, as a fixed sequence of rows:

Row Stage
Start NC optimization. the pass opens, and the scan of the program begins
NC line to step segment mapped. Un-optable lines are filtered. the scan closed: each line matched to the steps it made, the lines that cannot be optimized left out of the solve
Computing Optimized Feed by indivisual step.. the per-step feed solve
Optimization Feedrate built. the feed solve closed
Constrain feedrate By expaneded segment.. the extended-distance constraint
Constrain Feedrate By Acceleration.. the acceleration constraint
Build Compensation.. the compensation build
Regenerate NC commands.. the NC text is rewritten
File optimized: <path> one row per written file
Total N files optimized. the pass is over

When the session's steps were played across a reset of the NC pipeline — a script's PowerReset, a switch of the controller brand or preset, or a session ended (EndSession()) and begun again with its steps kept — the optimizer cannot match the programs played since the reset to their steps, so it writes nothing, and one error row stands in place of the whole ladder: The NC pipeline was reset while the strip held N positions … (OptimizeNc--StripSpansPipelineReset). Optimize before the reset, or clear the steps first — ResetRuntime in the script, or the transport's Reset — and play the programs to optimize again. The script or mission that asked for the optimization goes on.

Other rows can join the ladder. While the program is scanned, a tool change to a tool the optimizer cannot use is a warning: Tool Not Found: <id> at each change to a tool the tool house holds no milling cutter for, and, once per tool, Tool <id>: its cutter carries no optimization limit; … or Tool <id>: optimization is off in its cutter's optimization limit; …. Two errors end the pass at the scan, with no mapping row and straight to a green Total 0 files optimized.: NC optimization aborted: the piece SentenceIndex stream is not strictly increasing … (NcOpt-Pieces--NonMonotonic), when the program calls a subprogram or a macro after a jump — a GOTO, or a loop going back to its head or out past its end — and NcOpt--Exception, when the scan itself threw. During the feed solve, a step whose solve threw is an error row, NC optimization step <i> (<file and line>) failed; …, at most twenty of them in a run; a failed read of the step cache is an error row too (StepLuggage-Read--Fault or StepBrief-Read--Fault, below); and the spindle capability adds a warning (NcOpt-SpindleSpeed--Warning) once per run for each of its two wordings, Continuous condition not set in Spindle Capability. when it has no continuous torque or power contour, and The spindle limits and heat parameters cannot be read … when a contour cannot be read at some steps' speed. Two warnings come just before the feed solve's closing row: N step(s) failed while computing the optimized feed … when a step's solve threw (see When a Step Cannot Be Solved), and The source-feedrate floor holds N step(s) above a load target … (NcOpt-Feed--SourceFloorOverTarget) when the source-feedrate floor kept steps above a force or safety target, with the count per target. With a deflection compensation on, Build Compensation.. is followed by Deflection compensation built on N of M steps; …. While Regenerate NC commands.. prepares the edits — before the first File optimized: row — a block whose text the optimizer cannot read when it decides whether to restate an F on it, or to put the file's first F on it, adds an error row (NcOpt-Parse--Error) each time it is asked: a block left out of the solve then keeps its text, and an optimized block is still edited as the solve decided — its F written or its own F dropped, a split block still cut into fragments — but the file's first F is not put on it and goes to the next block that can take it.

A StepLuggage-Read--Fault or StepBrief-Read--Fault row means a stretch of the step cache could not be read: the steps' recorded engagement for the first, their recorded physics for the second. Either way the touched steps of that stretch are left at their simulated feed (still held within the feed limits) rather than being solved without that record, so the output can be used; the stretch is left unoptimized but for its non-cutting regions, whose feed is raised as usual. To have it optimized, optimize again, and if the row comes back, Reset and play again to rebuild the step cache before optimizing. With a deflection compensation on, the compensation build reads the step cache again — a step whose feed is still its simulated one when the build runs is compensated from its recorded physics, one whose feed the run changed (by the solve, or after it by the feed limits or the extended-distance and acceleration constraints) from its recorded engagement — so either row can also follow Build Compensation..: the steps of that stretch that needed the record it names then get no compensation, and their feeds are not changed by it. A step a StepLuggage-Read--Fault in the feed solve left at its simulated feed is therefore still compensated, unless a later constraint changed its feed and the compensation build cannot read its recorded engagement either; one a StepBrief-Read--Fault left there is compensated from its recorded physics only if the compensation build can read that record, or from its recorded engagement when a later constraint changed its feed.

Three of those stages tick while they work, and they are the only instrument for telling a slow optimization from a stopped one. The scan adds Scanning NC for optimization.. FileNo:<n>, LineNo:<m> every thousand blocks scanned; the feed solve adds Computing Optimized Feed by indivisual step.. FileNo:<n>, LineNo:<m> every thousand steps solved — StepIndex:<i> instead, for a step that carries no source line — and the writer adds Now optimizing to: FileNo.<n>, LineNo.<m> every thousand lines written. Each names the source file and line the run has reached, so a ladder whose last row keeps advancing is a run still moving through the program, and one that has gone quiet without reaching Total N files optimized. is not. A stage with fewer than a thousand blocks, steps or lines to get through adds no counting row between its first row and its last — for the scan, which has no opening row of its own, between Start NC optimization. and the mapping row — so a short program crosses the whole ladder in near-silence.

Stop reaches inside an optimization. The transport's Stop is tested before each block while the program is scanned, between steps during the per-step feed solve, and before each output file and each destination piece while files are written, so a stopped optimization halts at the next block, step or piece rather than running to the end. The stages between are not interrupted: a Stop that lands in the extended-distance constraint or the compensation build takes effect when that stage ends, and one that lands in the acceleration constraint lets the compensation build after it run too. One that lands after the compensation build — in the last constraint pass, which has no row of its own, or while Regenerate NC commands.. prepares the edits — is seen only at the first file, so Regenerate NC commands.. can still follow the Stop. The Shell ladder says so: an optimization canceled. row is added before the pass ends, and a Stop during the feed solve also closes the solve with Optimization Feed Process canceled. in place of Optimization Feedrate built..

Important

A stopped optimization still ends on the green Total N files optimized. row a completed one ends on. That row is not a statement that the optimization finished — read the row above it. N counts the files the run had begun writing, so a Stop before the writing begins ends on Total 0 files optimized., and a Stop during writing counts the file it was in the middle of, which is left short. The files after it are not begun: an output an earlier run wrote for one of them stays as that run left it.

An optimization that cannot write an output file — one held open by another program, or read-only — stops there, with no closing rows: the files written before it stay. Run as a mission's command or from a Post-Execution, the whole run ends with it, nothing below it in the mission runs, and Shell shows The play stopped on an error: … with the reason. Run from a Script, the script ends at that call, Shell shows Script […] faulted: …, and the mission goes on with its next command. A per-step log (below) that cannot be opened ends the optimization the same way, earlier: right after Computing Optimized Feed by indivisual step.., before any step is solved or any file written, with the logs opened before it closed again. Free the file, or turn the log off.


5. Verify Optimization Results

Optimization Logs

The per-step log is written by default. Every optimization drops one .IndependentStepAdjustment.log beside each optimized NC file that had steps to solve, named after that optimized file and written afresh, by a run that reaches the feed solve, over the one an earlier run left there, and it records which constraint limited each step. EnableIndividualStepAdjustmentLog is the switch that stops it being written:

EnableIndividualStepAdjustmentLog = false;

An optimization stopped or failed during the feed solve leaves that file short. Its buffered tail is written out only when the feed solve runs to completion; when the pass is cut off, the lines still in the buffer are dropped, and so is any step line held back in the ordering window waiting for a lower step index that never arrived. Batches reach the file at most once a second, so the missing tail can cover the last second of solving as well as the steps that were never reached. Read the log as a complete record only for a run whose feed solve closed with Optimization Feedrate built.

Each row of the .IndependentStepAdjustment.log file opens with the source NC file and line, the step index and the cutter location, then lists the feed per tooth every active criterion allowed:

Field Criterion
FrtBySourceFeedrateFloor_mm not a criterion: the source-feedrate floor as a feed per tooth, stated on every solved step where it lies above the other lower limits and so is the lower limit the criteria are solved from — its presence alone does not mean the floor held the step
FrtByPreferedForce_mm target cutting force
FrtByYieldingStressRatio_mm yielding stress
FrtBySpindleTorqueRatio_mm spindle torque
FrtBySpindlePowerRatio_mm spindle power
FrtByThermalYieldingRatio_mm thermal yield
FrtByCustom_mm(n) the n-th custom criterion the script registered
FrtByReliefAngle_mm relief-face contact — see Relief Face Avoidance

The step's feed per tooth is the lowest of the six criteria from FrtByPreferedForce_mm to FrtByCustom_mm(n); the relief-angle pass then runs on that value, held within the step's upper feed-per-tooth limit — a step with no load row at all is tried at that limit itself — so its row is the last word rather than one vote among the others. A criterion equal to FrtBySourceFeedrateFloor_mm was held up by the floor rather than settled by its own target. The one exception is the target cutting force while EnableDepthSplition is on, its default: a step the force target pins on the floor logs the step's own feed per tooth on the FrtByPreferedForce_mm row instead of the floor, and the step is still written at the floor.

Every criterion row except the target cutting force carries the solver's status for that criterion in brackets. Acceptable means the criterion was met at the feed the row states, without a solve: on the five load rows, the criterion's value is under its target at the upper feed limit, so the criterion does not limit the step and the row states that limit; on the relief-angle row, the relief face was clear at the feed the load criteria chose, or — on a step with no load row — at the upper feed-per-tooth limit, which the row then states. Solved means the bisection landed within the criterion's tolerance of its target (1 % of the target on the load rows). OverIteration means ten halvings did not, and the value is then the trial closest to the target among the acceptable ones — or among all of them when none is acceptable, which is the lower limit when the target is still exceeded there. A load row only reaches the bisection when its value at the upper limit is not under the target — at or over it, or not evaluable there — so an OverIteration value lies between the limits or on the lower one; a criterion that cannot be evaluated at any trial feed ends OverIteration on the upper limit instead. On the relief-angle row the tolerance is 0.0001 mm/s of relief-face colliding speed, so a bisected relief row mostly reads OverIteration: the highest trial feed at which the face was clear, or the lower limit when the face collides there too. The relief-angle row reads ∞ only when the upper feed-per-tooth limit is itself infinite — the option's maximum feed rate, its maximum feed per tooth and the cutter's maximum feed per tooth all Infinity — and the step then sets no limit of its own: the probe is tried once at that infinite feed and reads the face clear. A criterion with no row at all was switched off (its safety factor is 0, or the target cutting force is Infinity), was not evaluable for that tool, read 0 on the step as played (a step with no stored load, or no stored tool heating, is not solved for that criterion), or — on the torque and power rows — met a spindle whose limits could not be read at the step's speed; the relief-angle row is absent when the cutter's EnableLimitByReliefAngle is off. The criteria are solved one at a time and the lowest wins, which assumes that a criterion met at a feed is met at every lower feed; a value that peaks between the limits — a thin-chip step's thermal yield ratio at its minimum chip thickness — is the known exception.

Four rows replace that list rather than joining it:

Field Meaning
FrtByUnTouched: inf the step cuts nothing, so no physical criterion applies and the step itself sets no limit
FrtByNoForceBrief: inf the step cuts but has no force brief to solve on — its tool's cutter is not a milling cutter, or the stored brief is missing beside its luggage — so no physical criterion applies and the step itself sets no limit
FeedrateByNoData_mmdmin: <feed> no luggage stored for the step — no tool or no milling coefficients on it, or the physics switched off for the play — or its stretch of the step cache could not be read (StepLuggage-Read--Fault, StepBrief-Read--Fault), so it keeps its feed
StepFailed: <exception> the solve threw; the step keeps its simulated feed and is reported as an error — see When a Step Cannot Be Solved

Embedded Log Comments

Control embedded log verbosity with EmbeddedLogMode:

Mode Description
None No log comments
SimpleLog StepIndex on re-interpolated lines; LineNo on last interpolated line per original line
FullLog StepIndex and LineNo on all lines
Important

The mode is read by the legacy optimization path. While EnableSoftNcRunner is on and the session holds played NC — the default — the optimizer writes the SimpleLog shape whatever the mode says: every re-interpolated fragment carries its StepIndex, the last fragment of each source line adds that line's LineNo, and a line the optimizer did not split carries no note.

The note is written in the controller's comment grammar, so the optimized file stays legal for the control that reads it. On the Fanuc family it is parenthesized:

G01 X10.0 Y20.0 F500 (src(LineNo: 140, StepIndex: 256))

On Heidenhain it is a ; comment, because a TNC reads parentheses as code:

120 L X+35 Y-11.7 R0 F500 ;src(LineNo: 140, StepIndex: 256)

On a Heidenhain project a feed word the optimizer has to insert is also placed in the element order a TNC enforces — after the coordinate words, after the rotation direction DR+ / DR-, and after the radius compensation RL / RR / R0. A block that read L X+10 Y+20 RL comes back as L X+10 Y+20 RL F500.

Tracking Individual Step Constraints

To isolate which physical quantity limits each step, disable smoothing:

OptMaxAcceleration_mmds2 = double.PositiveInfinity;
OptFeedrateAssignmentRatio = 0;
OptExtendedPreDistance_mm = 0;
OptExtendedPostDistance_mm = 0;

The four smoothing settings are what this block changes. The per-step log is already on, and EmbeddedLogMode does not reach the pipeline that runs by default — see above.

Post-Optimization Simulation Differences

Optimized feed rates produce different interpolation points, causing:

  • Different simulation mesh errors
  • Surface morphology changes at the surface roughness level (more pronounced at corners)

Simulated physical quantities after optimization may be slightly above target values due to these differences.

Tip

For abnormally low optimized feed rates at corners, refer to Corner Feed Rate Optimization.


Tool Breakage Solutions

If the simulation shows yielding stress ratio, max spindle torque ratio, or max spindle power ratio above 100%, consider:

  1. Modify the toolpath to reduce cutting width/depth
  2. Use HiNC optimization to adjust feed rates, bringing these ratios below 100%

For thermal edge chipping, reduce the spindle speed to allow heat dissipation.


Complete Script Example

EnablePhysics = true;
LoadCuttingParaByFile("Material.mp");

OptEnableFeedrate = true;
OptEnableInterpolation = true;
OptRapidFeed_mmdmin = 4000;
OptMinFeedrate_mmdmin = 100;
OptMaxFeedrate_mmdmin = 4000;
OptMaxAcceleration_mmds2 = 10;
OptExtendedPreDistance_mm = 3;
OptExtendedPostDistance_mm = 2;
OptSpindlePowerSafetyFactor = 1.5;
OptSpindleTorqueSafetyFactor = 1.5;
OptThermalYieldSafetyFactor = 0;
OptPreferedForce_N = double.PositiveInfinity;

PlayNcFile("NC/file1.nc");

OptimizeToFiles("Cache/Opt-[NcName]");
WriteStepFiles("Output/[NcName].step.csv");

See Also