feat: gate heavy solver JSON, field traceability API, GUI rod/export depth
- C: emit profiles/diagnostics/fourier only when enable flags are set; null otherwise - API: fieldTraceability on case parse/default and solve; fix GET /solve/default options - Tests: golden fingerprint, quality gates, C diagnostics invariants; cardQa mean empty guard - Makefile: test-solver-sanitize ASan/UBSan target; README and COMPUTE_PLAN updates - GUI: taper weight/MTS/guides/sinker round-trip, rod catalog, solver output toggles, results (profiles/diagnostics/Fourier/traceability), engineering checks and tabs - Restore canonical WellName in base-case for regression; trace TaperGuidesCountArray Made-with: Cursor
This commit is contained in:
@@ -70,17 +70,35 @@ export function hydrateFromParsed(parsed: ParsedCase): CaseState {
|
||||
const length = parseColonArray(raw.TaperLengthArray);
|
||||
const modulus = parseColonArray(raw.TaperModulusArray);
|
||||
const rodType = parseColonArray(raw.RodTypeArray);
|
||||
const taperLen = Math.max(diam.length, length.length, modulus.length, rodType.length);
|
||||
const weight = parseColonArray(raw.TaperWeightArray);
|
||||
const mts = parseColonArray(raw.TaperMTSArray);
|
||||
const guideCounts = parseColonArray(raw.TaperGuidesCountArray);
|
||||
const rgWeights = parseColonArray(raw.RodGuideWeightArray);
|
||||
const rgTypePieces = textOf(raw.RodGuideTypeArray).split(":");
|
||||
const rodGuideSlotCount = Math.max(rgTypePieces.length, rgWeights.length, 1);
|
||||
/** Core rod-string stations (commercial XML often pads these together). */
|
||||
const coreTaperLen = Math.max(diam.length, length.length, modulus.length, rodType.length);
|
||||
const taperParallelSuffix = {
|
||||
weights: weight.length > coreTaperLen ? weight.slice(coreTaperLen) : [],
|
||||
mts: mts.length > coreTaperLen ? mts.slice(coreTaperLen) : [],
|
||||
guidesCounts: guideCounts.length > coreTaperLen ? guideCounts.slice(coreTaperLen) : []
|
||||
};
|
||||
const taper: TaperRow[] = [];
|
||||
for (let i = 0; i < taperLen; i += 1) {
|
||||
// Stop appending "zero" rows once we've passed the meaningful entries;
|
||||
// TaperCount is the authoritative limit but we keep all rows to preserve
|
||||
// round-trip exactly.
|
||||
for (let i = 0; i < coreTaperLen; i += 1) {
|
||||
const gc = guideCounts[i];
|
||||
const guidesEnabled = Number.isFinite(gc) && gc >= 0;
|
||||
const tok = (rgTypePieces[i] ?? "").trim();
|
||||
taper.push({
|
||||
diameter: diam[i] ?? 0,
|
||||
length: length[i] ?? 0,
|
||||
modulus: modulus[i] ?? 0,
|
||||
rodType: rodType[i] ?? 0
|
||||
rodType: rodType[i] ?? 0,
|
||||
weightLbfPerFt: weight[i] ?? 0,
|
||||
mtsLbf: mts[i] ?? 0,
|
||||
guidesEnabled,
|
||||
guideCount: guidesEnabled ? Math.max(0, Math.round(gc)) : 0,
|
||||
guideTypeToken: tok,
|
||||
rodGuideWeightLbfPerFt: rgWeights[i] ?? 0
|
||||
});
|
||||
}
|
||||
|
||||
@@ -130,6 +148,12 @@ export function hydrateFromParsed(parsed: ParsedCase): CaseState {
|
||||
raw.OtherGuideFrictionRatio,
|
||||
model.otherGuideFrictionRatio ?? 1
|
||||
),
|
||||
|
||||
sinkerBarDiameter: numberOf(raw.SinkerBarDiameter, 0),
|
||||
sinkerBarLength: numberOf(raw.SinkerBarLength, 0),
|
||||
rodGuideSlotCount,
|
||||
taperParallelSuffix,
|
||||
|
||||
upStrokeDamping: numberOf(raw.UpStrokeDampingFactor, model.upStrokeDamping ?? 0),
|
||||
downStrokeDamping: numberOf(raw.DownStrokeDampingFactor, model.downStrokeDamping ?? 0),
|
||||
nonDimensionalFluidDamping: numberOf(
|
||||
|
||||
Reference in New Issue
Block a user