Files
dougal-software/etc/qc/default/definitions.yaml
D. Berge df3a0b4c50 Be explicit about what type of data is being QC'ed.
The source deviation QCs now tell the user whether raw
or final data is being QC'ed.
2021-05-07 21:29:39 +02:00

255 lines
12 KiB
YAML

# QC definition file
-
name: "Missing shots"
iterate: "sequences"
labels: [ "QC" ]
id: missing_shots
check: |
const sequence = currentItem;
const sp0 = Math.min(sequence.fsp, sequence.lsp);
const sp1 = Math.max(sequence.fsp, sequence.lsp);
const missing = preplots.filter(r => r.line == sequence.line &&
r.point >= sp0 && r.point <= sp1 &&
!sequence.shots.find(s => s.point == r.point)
);
missing.length == 0 || missing.map(r => `Missing shot: ${r.point}`).join("\n")
-
name: "Gun QC"
disabled: false
labels: [ "QC", "QCGuns" ]
children:
-
name: "Sequences without gun data"
iterate: "sequences"
id: seq_no_gun_data
check: |
const sequence = currentItem;
currentItem.has_smsrc_data || "Sequence has no gun data"
-
name: "Missing gun data"
id: missing_gun_data
check: |
sequences.some(s => s.sequence == currentItem.sequence && s.has_smsrc_data)
? (!!currentItem._("raw_meta.smsrc.guns") || "Missing gun data")
: true
-
name: "No fire"
id: no_fire
check: |
const currentShot = currentItem;
const gunData = currentItem._("raw_meta.smsrc");
(gunData && gunData.num_nofire != 0)
? `Source ${gunData.src_number}: No fire (${gunData.num_nofire} guns)`
: true;
-
name: "Pressure errors"
id: pressure_errors
check: |
const pressure=11;
const gunData = currentItem._("raw_meta.smsrc");
const results = gunData &&
gunData
.guns
.filter(gun => ((gun[2] == gunData.src_number) && (gun[pressure]/parameters.gunPressureNominal - 1) > parameters.gunPressureToleranceRatio))
.map(gun =>
`source ${gun[2]}, string ${gun[0]}, gun ${gun[1]}, pressure: ${gun[pressure]} / ${parameters.gunPressureNominal} = ${(Math.abs(gunData.manifold/parameters.gunPressureNominal - 1)*100).toFixed(1)}% > ${(parameters.gunPressureToleranceRatio*100).toFixed(1)}%`
);
results && results.length
? results
: true
-
name: "Single gun / cluster"
children:
-
name: "Source depth"
id: source_depth
check: |
const currentShot = currentItem;
let _result_;
_depth=10;
const gunData = currentShot._("raw_meta.smsrc.guns");
if (!gunData) {
// We check for missing data elsewhere, so don't fail this test
_result_ = true
} else if (gunData.every(gun => Math.abs(gun[_depth]-parameters.gunDepth) <= parameters.gunDepthTolerance)) {
_result_ = true;
} else {
const bad_guns = gunData.filter(gun => Math.abs(gun[_depth]-parameters.gunDepth) > parameters.gunDepthTolerance).map(gun => {
return `source ${gun[2]}, string ${gun[0]}, gun ${gun[1]}, depth: ${gun[10]}`;
});
_result_ = `Depth error: ${bad_guns.join("; ")}`;
}
_result_
-
name: "Synchronisation (error)"
id: sync_error
check: |
const currentShot = currentItem;
const gunData = currentShot._("raw_meta.smsrc");
let result = [];
if (gunData && gunData.num_nofire == 0) {
// These are the indices into the gun array for the different
// values of interest.
const subarray = 0;
const aimpoint = 7;
const firetime = 8;
// We only care about the source which actually fired (or was supposed to)
const sourceFired = gunData.guns.filter(g => g[2] == gunData.src_number);
// Let us check if the average delta for each string is within spec
let subarrayAverages = [];
sourceFired.forEach(g => {
const idx = g[subarray]-1;
const delta = g[firetime]-g[aimpoint];
if (!subarrayAverages[idx]) {
subarrayAverages[idx] = [];
}
subarrayAverages[idx].push(delta);
});
subarrayAverages = subarrayAverages.map(s => s.reduce( (a, b) => a+b, 0 ) / s.length);
subarrayAverages.forEach((value, idx) => {
if (value > parameters.gunTimingSubarrayAverage) {
result.push(`Average delta error: string ${idx+1}: ${value.toFixed(2)} > ${parameters.gunTimingSubarrayAverage}`);
}
});
// Let us see about individual guns
sourceFired
.filter(gun => Math.abs(gun[firetime]-gun[aimpoint]) > parameters.gunTiming)
.forEach(gun => {
const value = Math.abs(gun[firetime]-gun[aimpoint]);
result.push(`Delta error: source ${gun[2]}, string ${gun[0]}, gun ${gun[1]}: ${value.toFixed(2)} > ${parameters.gunTiming}`);
});
}
if (result.length) {
result.join("; ");
} else {
// Either there were no error or gun data was missing, which we take care of elsewhere
true;
}
-
name: "Synchronisation (warning)"
id: sync_warn
check: |
const currentShot = currentItem;
const gunData = currentShot._("raw_meta.smsrc");
let result = [];
if (gunData && gunData.num_nofire == 0) {
// These are the indices into the gun array for the different
// values of interest.
const subarray = 0;
const aimpoint = 7;
const firetime = 8;
// We only care about the source which actually fired (or was supposed to)
const sourceFired = gunData.guns.filter(g => g[2] == gunData.src_number);
sourceFired
.filter(gun => Math.abs(gun[firetime]-gun[aimpoint]) >= parameters.gunTimingWarning && Math.abs(gun[firetime]-gun[aimpoint]) <= parameters.gunTiming)
.forEach(gun => {
const value = Math.abs(gun[firetime]-gun[aimpoint]);
result.push(`Delta error: source ${gun[2]}, string ${gun[0]}, gun ${gun[1]}: ${parameters.gunTimingWarning} ≤ ${value.toFixed(2)} ≤ ${parameters.gunTiming}`);
});
}
if (result.length) {
result.join("; ");
} else {
// Either there were no error or gun data was missing, which we take care of elsewhere
true;
}
-
name: "Autofire"
id: autofire
check: |
const currentShot = currentItem;
let _result_;
_autofire=5;
const gunData = currentShot._("raw_meta.smsrc.guns");
if (!gunData) {
// We check for missing data elsewhere, so don't fail this test
_result_ = true;
} else if (gunData.every(gun => gun[_autofire] == false)) {
_result_ = true;
} else {
const bad_guns = gunData.filter(gun => gun[_autofire]).map(gun => {
return `source ${gun[2]}, string ${gun[0]}, gun ${gun[1]}, depth: ${gun[10]}`;
});
_result_ = `Depth error: ${bad_guns.join(";\n")}`;
}
_result_
-
name: "Centre of source preplot deviation (single shots)"
labels: [ "QC", "QCNav" ]
disabled: false
children:
-
name: "Crossline"
id: crossline
check: |
const currentShot = currentItem;
Math.abs(currentShot.error_i) <= parameters.crosslineError
|| `Crossline error (${currentShot.type}): ${currentShot.error_i.toFixed(1)} > ${parameters.crosslineError}`
-
name: "Inline"
id: inline
check: |
const currentShot = currentItem;
Math.abs(currentShot.error_j) <= parameters.inlineError
|| `Inline error (${currentShot.type}): ${currentShot.error_j.toFixed(1)} > ${parameters.inlineError}`
-
name: "Centre of source preplot deviation (moving average)"
labels: [ "QC", "QCNav" ]
children:
-
name: "Crossline"
iterate: "sequences"
parameters: [ "crosslineErrorAverage" ]
id: crossline_average
check: |
const currentSequence = currentItem;
const i_err = currentSequence.shots.filter(s => s.error_i != null).map(a => a.error_i);
if (i_err.length) {
const avg = i_err.reduce( (a, b) => a+b)/i_err.length;
avg <= parameters.crosslineErrorAverage ||
`Average crossline error: ${avg.toFixed(1)} > ${parameters.crosslineErrorAverage}`
} else {
`Sequence ${currentSequence.sequence} has no shots within preplot`
}
-
name: "Inline"
iterate: "sequences"
parameters: [ "inlineErrorRunningAverageShots" ]
id: inline_average
check: |
const currentSequence = currentItem;
const n = parameters.inlineErrorRunningAverageShots; // For brevity
const results = currentSequence.shots.slice(n/2, -n/2).map( (shot, index) => {
const shots = currentSequence.shots.slice(index, index+n).map(i => i.error_j).filter(i => i !== null);
if (!shots.length) {
// We are outside the preplot
// Nothing to see here, move along
return true;
}
const mean = shots.reduce( (a, b) => a+b ) / shots.length;
return Math.abs(mean) <= parameters.inlineErrorRunningAverageValue ||
`Running average inline error: shot ${shot.point}, ${mean.toFixed(1)} > ${parameters.inlineErrorRunningAverageValue}`
}).filter(i => i !== true);
results.length == 0 || results.join("\n");