Initial commit: establish deterministic rod-string solver stack.
Set up the C solver core, Node API orchestration, TS GUI workflow, and engineering documentation with cleaned repo hygiene for private Git hosting. Made-with: Cursor
This commit is contained in:
83
gui-ts/src/state/engineeringChecks.ts
Normal file
83
gui-ts/src/state/engineeringChecks.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import type { CaseState } from "./caseModel";
|
||||
import { computeDoglegSeverityDegPer100 } from "./trajectoryMetrics";
|
||||
|
||||
export const PUMP_ROD_MISMATCH_M = 15;
|
||||
export const DLS_BAD_SECTION_THRESHOLD = 15;
|
||||
|
||||
export type EngineeringIssue = {
|
||||
severity: "warning" | "error";
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type EngineeringChecks = {
|
||||
issues: EngineeringIssue[];
|
||||
hasBlockingError: boolean;
|
||||
};
|
||||
|
||||
export function runEngineeringChecks(state: CaseState): EngineeringChecks {
|
||||
const issues: EngineeringIssue[] = [];
|
||||
|
||||
const activeTaper = state.taper.filter((t) => Number.isFinite(t.length) && t.length > 0);
|
||||
const rodTotal = activeTaper.reduce((acc, t) => acc + t.length, 0);
|
||||
const pumpDepth = state.pumpDepth;
|
||||
if (rodTotal > 0 && pumpDepth > 0) {
|
||||
const diff = Math.abs(pumpDepth - rodTotal);
|
||||
if (diff > PUMP_ROD_MISMATCH_M) {
|
||||
issues.push({
|
||||
severity: "error",
|
||||
code: "PUMP_ROD_MISMATCH_15M",
|
||||
message: `Pump depth (${pumpDepth.toFixed(1)}) and total rod length (${rodTotal.toFixed(
|
||||
1
|
||||
)}) differ by ${diff.toFixed(1)} m (> ${PUMP_ROD_MISMATCH_M} m limit).`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (state.survey.length < 2) {
|
||||
issues.push({
|
||||
severity: "error",
|
||||
code: "SURVEY_TOO_SHORT",
|
||||
message: "Trajectory needs at least 2 survey stations."
|
||||
});
|
||||
} else {
|
||||
let nonMonotonic = false;
|
||||
let maxDls = 0;
|
||||
for (let i = 1; i < state.survey.length; i += 1) {
|
||||
if (state.survey[i].md <= state.survey[i - 1].md) nonMonotonic = true;
|
||||
maxDls = Math.max(maxDls, computeDoglegSeverityDegPer100(state.survey[i - 1], state.survey[i]));
|
||||
}
|
||||
if (nonMonotonic) {
|
||||
issues.push({
|
||||
severity: "error",
|
||||
code: "SURVEY_MD_NON_MONOTONIC",
|
||||
message: "Measured depth must strictly increase between survey stations."
|
||||
});
|
||||
}
|
||||
if (maxDls > DLS_BAD_SECTION_THRESHOLD) {
|
||||
issues.push({
|
||||
severity: "warning",
|
||||
code: "DLS_HIGH",
|
||||
message: `High dogleg severity detected (max ${maxDls.toFixed(
|
||||
2
|
||||
)} deg/100 > ${DLS_BAD_SECTION_THRESHOLD} deg/100 bad-section threshold).`
|
||||
});
|
||||
}
|
||||
const maxMd = state.survey[state.survey.length - 1].md;
|
||||
if (pumpDepth > 0 && maxMd > 0 && maxMd < pumpDepth - 10) {
|
||||
issues.push({
|
||||
severity: "warning",
|
||||
code: "SURVEY_BELOW_PUMP_MISSING",
|
||||
message: `Trajectory ends at MD ${maxMd.toFixed(
|
||||
1
|
||||
)}, shallower than pump depth ${pumpDepth.toFixed(1)}.`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
issues,
|
||||
hasBlockingError: issues.some((issue) => issue.severity === "error")
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user