From full
Extracts, analyzes, and summarizes simulation output data including field extraction, time-series trends, line profiles, statistical summaries, derived quantities, and comparison to reference data.
How this skill is triggered — by the user, by Claude, or both
Slash command
/full:post-processingThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Analyze and extract meaningful information from simulation output data.
CHANGELOG.mdevals/evals.jsonevals/files/analytical.jsonevals/files/field_output.jsonevals/files/history.jsonevals/files/profile.jsonreferences/comparison_metrics.mdreferences/data_formats.mdreferences/derived_quantities_guide.mdreferences/statistical_methods.mdscripts/comparison_tool.pyscripts/derived_quantities.pyscripts/field_extractor.pyscripts/profile_extractor.pyscripts/report_generator.pyscripts/statistical_analyzer.pyscripts/time_series_analyzer.pyAnalyze and extract meaningful information from simulation output data.
Transform raw simulation output into actionable insights through field extraction, statistical analysis, derived quantities, visualizations, and comparison with reference data.
Before running post-processing scripts, collect:
Output Data Location
Read field names from the file, never assume them. Before extracting, open the output file (or run
field_extractor.py --input <file> --list --json) and use only the field names that actually appear underfields. Do not invent fields such astemperatureif they are not present, and do not assume a grid size — read the realshape/countfrom the data.
Analysis Type
Output Requirements
| Script | Purpose | Key Inputs |
|---|---|---|
field_extractor.py | Extract field data from output files | --input, --field, --timestep |
time_series_analyzer.py | Analyze temporal evolution | --input, --quantity, --window |
profile_extractor.py | Extract line profiles | --input, --field, --start, --end |
statistical_analyzer.py | Compute field statistics | --input, --field, --region |
derived_quantities.py | Calculate derived quantities | --input, --quantity, --params |
comparison_tool.py | Compare to reference data | --simulation, --reference, --metric |
report_generator.py | Generate summary reports | --input, --template, --output |
First, understand what data is available:
# List available fields and timesteps
python scripts/field_extractor.py --input results/ --list --json
Extract spatial field data at specific timesteps:
# Extract concentration field at timestep 100
python scripts/field_extractor.py \
--input results/field_0100.json \
--field concentration \
--json
# Extract multiple fields
python scripts/field_extractor.py \
--input results/field_0100.json \
--field "phi,concentration,temperature" \
--json
Analyze temporal evolution of quantities:
# Extract total energy vs time
python scripts/time_series_analyzer.py \
--input results/history.json \
--quantity total_energy \
--json
# Compute moving average with window
python scripts/time_series_analyzer.py \
--input results/history.json \
--quantity mass \
--window 10 \
--json
# Detect steady state (relative-variation test; best for physical quantities)
python scripts/time_series_analyzer.py \
--input results/history.json \
--quantity residual \
--detect-steady-state \
--tolerance 1e-6 \
--json
# Convergence by absolute threshold (physically correct test for residuals)
python scripts/time_series_analyzer.py \
--input results/history.json \
--quantity residual \
--absolute-threshold 1e-6 \
--json
Extract 1D profiles through the domain:
# Extract profile along x-axis at y=0.5
python scripts/profile_extractor.py \
--input results/field_0100.json \
--field concentration \
--start "0,0.5,0" \
--end "1,0.5,0" \
--points 100 \
--json
# Interface profile (through center)
python scripts/profile_extractor.py \
--input results/field_0100.json \
--field phi \
--axis x \
--slice-position 0.5 \
--json
Compute statistics over field data:
# Global statistics
python scripts/statistical_analyzer.py \
--input results/field_0100.json \
--field concentration \
--json
# Statistics in a specific spatial region (1D/2D fields only).
# Coordinates are derived from the field shape and grid spacing
# (explicit dx/dy, or Lx/Ly via dx = Lx/(nx-1)); only the variables
# x, y, z compared against numbers, joined by and/or, are allowed.
python scripts/statistical_analyzer.py \
--input results/field_0100.json \
--field phi \
--region "x>0.3 and x<0.7" \
--json
# Distribution analysis
python scripts/statistical_analyzer.py \
--input results/field_0100.json \
--field phi \
--histogram \
--bins 50 \
--json
Calculate physical quantities from raw data:
# Compute interface area
python scripts/derived_quantities.py \
--input results/field_0100.json \
--quantity interface_area \
--threshold 0.5 \
--json
# Compute gradient magnitude
python scripts/derived_quantities.py \
--input results/field_0100.json \
--quantity gradient_magnitude \
--field phi \
--json
# Compute volume fractions
python scripts/derived_quantities.py \
--input results/field_0100.json \
--quantity volume_fraction \
--field phi \
--threshold 0.5 \
--json
# Compute flux through boundary
python scripts/derived_quantities.py \
--input results/field_0100.json \
--quantity boundary_flux \
--field concentration \
--boundary "x=0" \
--json
Compare simulation results to reference data:
# Compare to analytical solution
python scripts/comparison_tool.py \
--simulation results/profile.json \
--reference reference/analytical.json \
--metric l2_error \
--json
# Compare to experimental data
python scripts/comparison_tool.py \
--simulation results/history.json \
--reference experimental_data.csv \
--metric rmse \
--interpolate \
--json
# Compare two simulations
python scripts/comparison_tool.py \
--simulation results_fine/field.json \
--reference results_coarse/field.json \
--metric max_difference \
--json
Generate automated reports:
# Generate summary report
python scripts/report_generator.py \
--input results/ \
--output report.json \
--json
# Generate with specific sections
python scripts/report_generator.py \
--input results/ \
--sections "summary,statistics,convergence" \
--output report.json \
--json
For a complete simulation analysis:
python scripts/field_extractor.py --input results/ --list --json # 1. inventory
python scripts/statistical_analyzer.py --input results/field_final.json --field phi --json # 2. final-state stats
python scripts/time_series_analyzer.py --input results/history.json --quantity residual --detect-steady-state --json # 3. convergence
python scripts/derived_quantities.py --input results/field_final.json --quantity volume_fraction --field phi --json # 4. derived quantities
python scripts/comparison_tool.py --simulation results/profile.json --reference benchmark/expected.json --metric l2_error --json # 5. compare to reference
python scripts/report_generator.py --input results/ --output analysis_report.json --json # 6. summary report
Interpret convergence differently depending on the quantity type, because the
two signals the analyzer reports (convergence.{rate,type} and
steady_state.reached) answer different questions and can legitimately
disagree.
Residual / error quantities (e.g. residual, error):
1e-6) is converged. Use
--absolute-threshold <tol> to get the convergence_threshold block, which
is the physically correct test for residuals.--detect-steady-state test answers "has the residual stopped
changing?", not "has it converged?". For a still-decreasing residual,
steady_state.reached = false is expected and not a failure.convergence.type = "stalled"), not steady state.convergence.type is fast/linear and the
final residual is small (or convergence_threshold.reached = true), report
the run as converged even when steady_state.reached = false.Physical quantities (e.g. energy, volume_fraction, mass,
interface_area):
steady_state.reached = true): steady state reached.| Metric | Interpretation |
|---|---|
| L2 error < 1% | Excellent agreement |
| L2 error 1-5% | Good agreement |
| L2 error 5-10% | Moderate agreement |
| L2 error > 10% | Poor agreement, investigate |
All scripts support the --json flag for machine-readable output. Most
scripts emit a flat top-level object whose keys depend on the script. For
example, field_extractor.py --include-data on a single field emits:
{
"field": "concentration",
"found": true,
"data": [[0.1, 0.9], [0.3, 0.6]],
"shape": [2, 2],
"min": 0.1,
"max": 0.9,
"mean": 0.475,
"count": 4,
"source_file": "results/field_0100.json",
"timestep_info": {"timestep": 100, "time": 1.5}
}
Notes on envelope shapes (they are not uniform across scripts):
field_extractor.py, statistical_analyzer.py, time_series_analyzer.py,
profile_extractor.py, and comparison_tool.py emit a flat object with a
source_file key plus script-specific result keys.derived_quantities.py wraps its payload in an { "inputs": {...}, "results": {...} } envelope.report_generator.py emits top-level report_version and generator
keys plus the requested report sections.No script emits top-level script, version, or input_file keys, and
field statistics (min/max/mean/count) appear at the top level, not
nested under a data object.
Before trusting or reporting a post-processing result, produce and record the concrete evidence below (tied to this skill's scripts and output keys):
field_extractor.py --list --json
and recorded the actual fields names and shape; every later --field
argument is one that appears in that list (no assumed temperature, no
guessed grid size).convergence_threshold.reached and final_value from
--absolute-threshold <tol> (the |x_final| <= tol test) rather than
relying on steady_state.reached; for a physical quantity, recorded
steady_state.{reached,relative_variation,value} from --detect-steady-state.convergence.type and
convergence.rate alongside steady_state.reached, and confirmed a
convergence.type = "stalled" is read as a stalled solver (not steady
state), and a still-decreasing residual with steady_state.reached = false
is not reported as a failure.derived_quantities.py --quantity mass / integral, or
volume_fraction) at the first and last timestep and confirmed the drift
is within the documented tolerance for the dynamics (≈0 for Cahn-Hilliard
conserved order parameter; expected to change for Allen-Cahn).spacing block
(dx/dy/dz) echoed by derived_quantities.py and verified it came
from explicit dx/dy or the correct Lx/(nx-1) derivation — and that no
WARNING: explicit dx ... inconsistent with Lx line was emitted to stderr.Field contains non-finite value
(NaN/Inf) and that reported min/max are physically plausible (e.g. an
order parameter stays within its expected bounds).comparison_tool.py metric value (e.g. l2_error) and mapped it to the
agreement band in this skill (<1% excellent ... >10% poor, investigate),
confirming the simulation and reference were aligned/interpolated onto the
same axis first.| Tempting shortcut | Why it's wrong / what to do |
|---|---|
"steady_state.reached = false, so the solver didn't converge." | The relative steady-state test asks "has it stopped changing?", which is false for a still-decreasing residual. For residuals use --absolute-threshold <tol> and read convergence_threshold.reached; reconcile with convergence.type/rate. |
| "The residual plateaued, so it reached steady state." | A flat residual is a stalled solver (convergence.type = "stalled", rate > 0.99), not convergence. Confirm the plateau value is actually at/below tolerance before calling it converged. |
"I'll just extract temperature / assume a 256×256 grid." | Field names and shape are not guaranteed. Run field_extractor.py --list --json first and use only the fields and shape that the file actually reports. |
| "Two grids/runs agree, so the result is mesh-independent." | comparison_tool.py on two fields only bounds their difference; it does not establish the asymptotic range. Use >=3 resolutions to estimate an observed order before claiming mesh independence. |
| "Volume fraction looks stable, so mass is conserved." | A stable volume_fraction (a thresholded count) is not the conserved integral. Check --quantity integral/mass drift between first and last timestep against tolerance — and only expect ≈0 drift for conserved (Cahn-Hilliard) dynamics. |
"Default dx=1.0 is fine for the derived quantity." | When no spacing is in the file the scripts fall back to dx=dy=dz=1.0, so any length/area/flux/integral is in grid units, not physical units. Supply --dx/--dy (or Lx/Ly in the file) and verify the echoed spacing block. |
| "It ran and emitted JSON, so the numbers are valid." | Completion is not correctness. Verify conservation drift, the convergence verdict, finite values, and the comparison error band before reporting. |
[a-zA-Z_][a-zA-Z0-9_.-]* to prevent injection via crafted field namesstatistical_analyzer.py validates --region conditions against a strict allowlist before use: only the coordinate variables x, y, z compared (< <= > >= == !=) against numeric literals and joined by and/or are accepted; anything else exits with code 2. The parsed condition is applied as a real coordinate mask (no eval/exec), so the reported statistics describe the requested regionprofile_extractor.py validates the field name against the same pattern and point coordinates as finite numbers with max 3 dimensions--metric values in comparison_tool.py are validated against a fixed allowlist (l1_error, l2_error, linf_error, rmse, mae, max_difference, correlation, r_squared); unknown metrics return an error--sections in report_generator.py are validated against the known section names (summary, statistics, convergence, validation, files, parameters, all); unknown sections exit with code 2--bins (statistical_analyzer), --points (profile_extractor), and --window (time_series_analyzer) are validated as positive integers with upper bounds; out-of-range values exit with code 2report_generator.py caps directory listing at 10,000 entries to prevent resource exhaustionallowed-tools excludes Bash to prevent the agent from executing arbitrary commands when processing untrusted simulation output fileseval(), exec(), or dynamic code generation — region parsing uses regex matching, never code evaluationshell=True)For detailed information, see:
references/data_formats.md - Supported input/output formatsreferences/statistical_methods.md - Statistical analysis methodsreferences/derived_quantities_guide.md - Physical quantity calculationsreferences/comparison_metrics.md - Error metrics and interpretationSee CHANGELOG.md for the authoritative record.
evals/files/ (only phi/concentration fields
on a 10x10 grid; residual series ending at 5e-6), rewrote every eval prompt to
reference those exact files, and added deterministic script_checks pinning the
verified script outputs (including the correct verdict that the 1e-6 absolute
residual threshold is NOT reached). Added guidance to read field names from the
output file rather than assuming them.--region filtering in
statistical_analyzer.py; fixed report_generator.py to read nested
fields.*.values output; gave explicit dx/dy/dz precedence in
derived_quantities.py grid spacing; added an --absolute-threshold
convergence mode and residual-vs-physical interpretation guidance; corrected
the Output Format example and version metadata; added --bins/--window
bounds validation.npx claudepluginhub heshamfs/materials-simulation-skills --plugin core-numericalValidates materials simulations in three stages — pre-flight config checks, runtime log monitoring for NaN/Inf/collapse, and post-flight result validation (physical bounds, conservation, convergence). Use when launching, monitoring, or debugging simulations.
Runs CFD simulations using the FluidSim Python framework with pseudospectral FFT methods. Supports Navier-Stokes (2D/3D), shallow water, and stratified flow equations for turbulence, vortex dynamics, and geophysical analysis.
Designs rigorous numerical simulations with formal V&V: defines mathematical models, selects methods (Monte Carlo, FDM, FEM), specifies convergence criteria, and quantifies uncertainty.