4. Data cleaning and validation

Raw CAE results are rarely ready for reporting the moment a solver finishes. A CFD run may leave NaN in cells where turbulence quantities diverged, a structural analysis may export displacements in millimeters while the report template expects meters, and a mesh may contain negative-volume elements that silently corrupt every derived quantity downstream. This chapter treats data cleaning and validation as a first-class engineering step: a repeatable pipeline that runs between "solver finished" and "plot generated." The goal is not cosmetic tidiness but defensible numbers — every value that reaches a report should be traceable, dimensionally correct, and computed on a mesh that passed basic quality gates.

Why Validation Must Precede Visualization

Postprocessing tools are permissive by design. pandas.read_csv() will happily ingest a column of empty strings, numpy.mean() will propagate NaN without complaint, and a contour plot will render a mesh with inverted elements as if nothing were wrong. The failure mode is therefore silent: a mean outlet temperature computed over 3% missing samples, or a pressure drop reported in the wrong unit system, looks perfectly plausible on a slide. The discipline is to insert explicit checks that fail loudly before any aggregation or plotting happens.

A practical validation stage answers three questions in order: (1) Is the data complete enough to aggregate? (2) Are all quantities in a consistent, declared unit system? (3) Is the mesh geometrically valid for the quantities being derived? Each question maps to a distinct set of checks, and each check should produce a machine-readable verdict rather than a human eyeball.

Handling Missing Values

Missing data in CAE exports appears in several disguises: empty CSV fields, sentinel values such as -999 or 1e30, literal NaN from a diverged solver, and rows dropped entirely by a failed export. The first task is detection, not imputation.

import pandas as pd
import numpy as np

SENTINELS = [-999.0, -9999.0, 1e30]

df = pd.read_csv("outlet_probe.csv")
df = df.replace(SENTINELS, np.nan)

report = df.isna().sum().to_frame("missing_count")
report["missing_pct"] = 100 * report["missing_count"] / len(df)
print(report.sort_values("missing_pct", ascending=False))

Once the missing pattern is visible, the decision to drop, interpolate, or flag depends on the physical meaning of the column. A time-history probe with a few isolated gaps can be safely interpolated; a spatially distributed field with a contiguous missing patch usually indicates a solver or export problem that imputation would only hide. A defensible rule set:

  • Drop the sample when missingness is isolated (< 1% of rows) and the quantity is used only for a global statistic such as a mean.
  • Interpolate only along a monotonic independent variable (time or a spatial coordinate), and record the interpolation in the report metadata.
  • Reject the dataset when missingness exceeds a threshold you declare up front — 5% is a common default for scalar monitors, but the threshold should be justified by the sensitivity of the reported quantity.
Never impute a value that will be used to compute a residual, a conservation balance, or a safety margin. In those cases a missing sample is a finding, not a gap to be filled.

Unit Consistency

Unit errors are the most expensive class of postprocessing mistake because they survive every numerical check. A displacement of 2.5 is valid whether the unit is millimeters or inches; only the label distinguishes them. The robust approach is to attach units to data at the point of ingestion and convert explicitly, rather than trusting column headers.

import pint
ureg = pint.UnitRegistry()

def to_si(value, unit_str):
    return (value * ureg(unit_str)).to_base_units().magnitude

# Example: mixed-unit export from two solvers
disp_mm = 2.5
disp_m  = to_si(disp_mm, "mm")      # -> 0.0025
stress_psi = 14500.0
stress_pa = to_si(stress_psi, "psi")  # -> 9.997e7

For a full dataset, declare a canonical unit system (SI is the safe default) and convert every column on load. Keep the original unit string in a sidecar dictionary so the report can state, for example, "displacements converted from mm to m." Two checks catch most unit problems before they propagate:

  • Magnitude sanity: compare each converted column against a physically plausible range for the application. A steel stress above 2 GPa or a displacement above the model's bounding-box diagonal is almost always a unit error.
  • Dimensional consistency: verify that derived quantities carry the expected dimensions. A pressure drop computed as rho * v**2 / 2 must reduce to pascals; if it reduces to pascals per meter, a length term is missing.

Mesh Quality Checks

Mesh quality is not a postprocessing concern in the abstract — it directly determines which derived quantities are trustworthy. The checks below are cheap to run on an exported mesh and catch the defects that most often invalidate results.

CheckTypical thresholdConsequence if violated
Negative or zero volumeany elementInverted Jacobian; solver results unreliable
Minimum orthogonality< 0.1 (CFD)Poor gradient reconstruction, false diffusion
Maximum aspect ratio> 100 (boundary layer excepted)Ill-conditioned stiffness matrix
Skewness> 0.85Reduced accuracy near the element
Growth rate between adjacent cells> 1.5Reflection errors at interfaces

A minimal mesh audit reads the element volume array and the quality metrics the mesher already computed, then reports counts and locations of violations:

vol = mesh.cell_volumes          # shape (n_cells,)
ortho = mesh.orthogonality       # 0..1, higher is better

bad_vol = np.where(vol <= 0)[0]
bad_ortho = np.where(ortho < 0.1)[0]

print(f"negative/zero volume cells: {len(bad_vol)}")
print(f"low-orthogonality cells:    {len(bad_ortho)}")
if len(bad_vol):
    print("first offending cell ids:", bad_vol[:10])

When violations are found, the correct response depends on severity. A handful of low-orthogonality cells in a far-field region may be acceptable if a mesh-sensitivity study shows the reported quantity is insensitive to them. Any negative-volume cell is a hard stop: the mesh must be repaired or regenerated before results are used. Record the audit outcome — cell counts, worst metric values, and the decision — in the report's validation section so a reviewer can see the basis for accepting the results.

CAE Postprocessing Validation Pipeline Three gates between solver output and reportable numbers 1 · Ingestion read_csv / mesh reader replace sentinels → NaN attach unit strings convert to SI output: typed DataFrame 2 · Missing Values count per column isolated → drop / interpolate contiguous → reject threshold: 5% default output: completeness verdict 3 · Mesh Quality volume > 0 orthogonality ≥ 0.1 aspect ratio ≤ 100 skewness ≤ 0.85 output: pass / repair / reject Gate Outcomes PASS → proceed to aggregation REPAIR → fix mesh / impute REJECT → regenerate / re-run Validation Record (attached to report) missing counts · unit conversions applied · worst mesh metrics · gate decision · reviewer Every reported number traces back to a passed gate

Putting It Together: A Validation Record

The three gates should emit a single structured record that travels with the report. A minimal schema, stored as JSON alongside the figures, makes the validation auditable without re-running the pipeline:

{
  "dataset": "outlet_probe.csv",
  "rows_ingested": 12000,
  "missing": {"T_outlet": {"count": 42, "pct": 0.35, "action": "interpolated"}},
  "units": {"displacement": "mm -> m", "pressure": "psi -> Pa"},
  "mesh": {"cells": 184320, "negative_volume": 0,
           "min_orthogonality": 0.14, "max_aspect_ratio": 87.3},
  "gate_decision": "PASS",
  "reviewer": "jdoe",
  "timestamp": "2025-01-15T09:42:00Z"
}

Two habits make this pipeline durable. First, run it as a script, not as ad-hoc notebook cells, so the same checks execute on every dataset. Second, fail loudly: raise an exception when a hard gate is violated rather than printing a warning that scrolls off screen. A validation stage that can be ignored will be ignored, and the cost of a unit error or an inverted element discovered after a report is issued is far higher than the cost of a pipeline that refuses to produce a figure until the data earns it.

Start with the cheapest checks — missing-value counts and unit conversion — because they catch the majority of real-world defects. Add mesh quality gates once the basic pipeline is stable, and tune thresholds against a known-good baseline case rather than adopting textbook values blindly.