Crash-Intelligence / utils /data_generator.py
shrut27's picture
Upload folder using huggingface_hub
f5823da verified
Raw
History Blame Contribute Delete
20 kB
"""Synthetic public-style material and crash datasets for the Crash Intelligence platform."""
from __future__ import annotations
from pathlib import Path
import numpy as np
import pandas as pd
RNG = np.random.default_rng(42)
MATERIAL_FAMILIES = {
"Mild Steel": {
"grades": ["AISI 1008", "AISI 1010", "DC04", "DC06", "St14"],
"density": (7.80, 7.87),
"modulus": (200, 210),
"yield": (140, 280),
"uts": (270, 420),
"elongation": (25, 45),
"failure_strain": (0.25, 0.55),
"cost": (0.6, 1.2),
"co2": (1.8, 2.5),
"recyclability": (0.85, 0.98),
"strain_rate_sens": (0.01, 0.04),
},
"AHSS": {
"grades": ["DP600", "DP800", "DP1000", "TRIP780", "CP800", "MS1200"],
"density": (7.80, 7.85),
"modulus": (200, 210),
"yield": (350, 900),
"uts": (600, 1200),
"elongation": (8, 25),
"failure_strain": (0.08, 0.22),
"cost": (1.0, 2.2),
"co2": (2.0, 2.8),
"recyclability": (0.80, 0.95),
"strain_rate_sens": (0.02, 0.06),
},
"UHSS": {
"grades": ["MS1500", "MS1700", "PHS1500", "PHS1800", "QP980", "QP1180"],
"density": (7.80, 7.85),
"modulus": (200, 210),
"yield": (900, 1500),
"uts": (1200, 2000),
"elongation": (4, 12),
"failure_strain": (0.04, 0.12),
"cost": (1.8, 3.5),
"co2": (2.2, 3.2),
"recyclability": (0.75, 0.92),
"strain_rate_sens": (0.015, 0.05),
},
"Boron Steel": {
"grades": ["22MnB5", "30MnB5", "Usibor 1500", "Usibor 2000", "Ductibor 500"],
"density": (7.80, 7.85),
"modulus": (200, 210),
"yield": (1000, 1400),
"uts": (1400, 2000),
"elongation": (5, 10),
"failure_strain": (0.05, 0.10),
"cost": (2.0, 3.8),
"co2": (2.3, 3.4),
"recyclability": (0.78, 0.93),
"strain_rate_sens": (0.02, 0.045),
},
"Aluminum 5xxx": {
"grades": ["AA5052", "AA5083", "AA5182", "AA5754", "AA5454"],
"density": (2.66, 2.70),
"modulus": (68, 72),
"yield": (90, 220),
"uts": (190, 320),
"elongation": (12, 30),
"failure_strain": (0.15, 0.35),
"cost": (2.5, 4.0),
"co2": (8.0, 12.0),
"recyclability": (0.90, 0.98),
"strain_rate_sens": (0.01, 0.03),
},
"Aluminum 6xxx": {
"grades": ["AA6005", "AA6061", "AA6063", "AA6082", "AA6111"],
"density": (2.68, 2.71),
"modulus": (68, 72),
"yield": (150, 280),
"uts": (220, 340),
"elongation": (8, 18),
"failure_strain": (0.10, 0.22),
"cost": (2.8, 4.5),
"co2": (8.5, 13.0),
"recyclability": (0.90, 0.98),
"strain_rate_sens": (0.01, 0.035),
},
"Aluminum 7xxx": {
"grades": ["AA7003", "AA7020", "AA7075", "AA7050", "AA7085"],
"density": (2.78, 2.82),
"modulus": (70, 73),
"yield": (300, 500),
"uts": (400, 580),
"elongation": (5, 12),
"failure_strain": (0.06, 0.14),
"cost": (4.0, 7.0),
"co2": (10.0, 16.0),
"recyclability": (0.85, 0.95),
"strain_rate_sens": (0.008, 0.025),
},
"Magnesium": {
"grades": ["AZ31B", "AZ61", "AZ91", "AM60", "ZK60"],
"density": (1.74, 1.82),
"modulus": (42, 48),
"yield": (120, 220),
"uts": (200, 320),
"elongation": (8, 18),
"failure_strain": (0.08, 0.20),
"cost": (4.5, 8.0),
"co2": (15.0, 25.0),
"recyclability": (0.70, 0.90),
"strain_rate_sens": (0.02, 0.05),
},
"CFRP": {
"grades": ["T700/Epoxy", "T800/Epoxy", "IM7/PEEK", "M55J/Epoxy", "AS4/Epoxy"],
"density": (1.50, 1.65),
"modulus": (70, 150),
"yield": (600, 1200),
"uts": (800, 1800),
"elongation": (1.2, 2.5),
"failure_strain": (0.012, 0.025),
"cost": (25, 80),
"co2": (20, 45),
"recyclability": (0.20, 0.45),
"strain_rate_sens": (0.005, 0.02),
},
"GFRP": {
"grades": ["E-Glass/Epoxy", "S-Glass/Epoxy", "E-Glass/PP", "E-Glass/PA6", "SMC"],
"density": (1.80, 2.10),
"modulus": (20, 45),
"yield": (200, 450),
"uts": (300, 700),
"elongation": (1.5, 4.0),
"failure_strain": (0.015, 0.04),
"cost": (5, 18),
"co2": (4, 12),
"recyclability": (0.25, 0.50),
"strain_rate_sens": (0.01, 0.03),
},
"Natural Fiber Composite": {
"grades": ["Flax/PP", "Hemp/PLA", "Jute/Epoxy", "Kenaf/PP", "Bamboo/Epoxy"],
"density": (1.20, 1.50),
"modulus": (8, 25),
"yield": (60, 150),
"uts": (80, 200),
"elongation": (2, 6),
"failure_strain": (0.02, 0.06),
"cost": (3, 10),
"co2": (0.5, 3.0),
"recyclability": (0.50, 0.80),
"strain_rate_sens": (0.015, 0.04),
},
"Polymer": {
"grades": ["PP-GF30", "PA6-GF35", "ABS", "PC/ABS", "PBT-GF30", "TPU"],
"density": (0.95, 1.45),
"modulus": (1.5, 12),
"yield": (25, 120),
"uts": (30, 160),
"elongation": (5, 80),
"failure_strain": (0.05, 1.5),
"cost": (1.5, 6.0),
"co2": (2.0, 6.5),
"recyclability": (0.40, 0.85),
"strain_rate_sens": (0.03, 0.10),
},
"Elastomer": {
"grades": ["EPDM", "NR", "SBR", "NBR", "Silicone"],
"density": (0.90, 1.25),
"modulus": (0.005, 0.05),
"yield": (2, 15),
"uts": (5, 30),
"elongation": (200, 600),
"failure_strain": (2.0, 6.0),
"cost": (2.0, 8.0),
"co2": (2.5, 7.0),
"recyclability": (0.15, 0.40),
"strain_rate_sens": (0.05, 0.15),
},
"Structural Foam": {
"grades": ["EPS", "EPP", "PUR Foam", "Al Honeycomb", "PET Foam"],
"density": (0.03, 0.25),
"modulus": (0.01, 2.0),
"yield": (0.2, 8),
"uts": (0.3, 12),
"elongation": (5, 80),
"failure_strain": (0.4, 2.0),
"cost": (1.0, 15.0),
"co2": (1.5, 8.0),
"recyclability": (0.20, 0.70),
"strain_rate_sens": (0.04, 0.12),
},
"Adhesive": {
"grades": ["Epoxy Structural", "PU Crash", "Acrylic", "MS Polymer", "Toughened Epoxy"],
"density": (1.05, 1.40),
"modulus": (0.5, 4.0),
"yield": (10, 45),
"uts": (15, 60),
"elongation": (5, 100),
"failure_strain": (0.05, 1.2),
"cost": (8, 35),
"co2": (3.0, 10.0),
"recyclability": (0.05, 0.25),
"strain_rate_sens": (0.02, 0.08),
},
}
COMPONENTS = [
"Bumper Beam",
"Crash Box",
"Front Rail",
"Door Intrusion Beam",
"B-Pillar",
"A-Pillar",
"Roof Rail",
"Seat Structure",
"Battery Enclosure",
"Underbody Shield",
"Hood Inner",
"Crossmember",
]
CRASH_SCENARIOS = [
"Frontal Crash",
"Side Impact",
"Rear Impact",
"Pole Impact",
"Pedestrian Impact",
"Battery Pack Crash",
"Bumper Beam Crash",
"Door Intrusion",
"Seat Structure Crash",
"BIW Crash",
"EV Underbody Protection",
]
JOINING_METHODS = [
"Spot Weld",
"Laser Weld",
"MIG Weld",
"SPR (Self-Piercing Rivet)",
"Structural Adhesive",
"Hybrid Weld-Bond",
"Bolt Fastened",
"FDS (Flow Drill Screw)",
]
SOLVERS = ["LS-DYNA", "Abaqus Explicit", "PAM-CRASH", "Radioss"]
MATERIAL_CARD_MAP = {
"Mild Steel": "MAT_024",
"AHSS": "MAT_024",
"UHSS": "MAT_024",
"Boron Steel": "MAT_024",
"Aluminum 5xxx": "MAT_024",
"Aluminum 6xxx": "MAT_024",
"Aluminum 7xxx": "MAT_024",
"Magnesium": "MAT_024",
"CFRP": "MAT_054 / Composite Damage",
"GFRP": "MAT_054 / Composite Damage",
"Natural Fiber Composite": "MAT_054 / Composite Damage",
"Polymer": "MAT_187 / SAMP-1",
"Elastomer": "MAT_077 / Hyperelastic",
"Structural Foam": "MAT_063 / Crushable Foam",
"Adhesive": "MAT_240 / Cohesive Zone",
}
def _sample_range(bounds: tuple[float, float], n: int) -> np.ndarray:
lo, hi = bounds
return RNG.uniform(lo, hi, n)
def generate_materials(n_records: int = 6000) -> pd.DataFrame:
"""Generate a large public-style automotive crash materials database."""
families = list(MATERIAL_FAMILIES.keys())
rows: list[dict] = []
per_family = max(1, n_records // len(families))
for family in families:
spec = MATERIAL_FAMILIES[family]
n = per_family
grades = spec["grades"]
for i in range(n):
grade = grades[i % len(grades)]
density = float(_sample_range(spec["density"], 1)[0])
modulus = float(_sample_range(spec["modulus"], 1)[0])
yield_s = float(_sample_range(spec["yield"], 1)[0])
uts = float(_sample_range(spec["uts"], 1)[0])
if uts < yield_s:
uts = yield_s * RNG.uniform(1.05, 1.35)
elong = float(_sample_range(spec["elongation"], 1)[0])
fail = float(_sample_range(spec["failure_strain"], 1)[0])
cost = float(_sample_range(spec["cost"], 1)[0])
co2 = float(_sample_range(spec["co2"], 1)[0])
recycl = float(_sample_range(spec["recyclability"], 1)[0])
srs = float(_sample_range(spec["strain_rate_sens"], 1)[0])
specific_strength = uts / density
specific_stiffness = modulus / density
energy_abs_potential = 0.5 * (yield_s + uts) * fail / density
ductility_index = elong / max(uts / 100.0, 1e-6)
crash_index = (
0.35 * (energy_abs_potential / 50.0)
+ 0.25 * (specific_strength / 200.0)
+ 0.20 * min(fail * 5.0, 1.0)
+ 0.20 * (1.0 / (1.0 + cost / 10.0))
)
crash_index = float(np.clip(crash_index * 100, 5, 98))
strength_weight = specific_strength
failure_risk = float(np.clip(1.0 - fail * 2.5 + srs * 2.0, 0.05, 0.95))
cost_perf = float(np.clip(crash_index / (cost + 0.5), 1, 80))
sustain = float(
np.clip(
100 * recycl * (1.0 / (1.0 + co2 / 10.0)) * (1.0 / (1.0 + density / 5.0)),
5,
98,
)
)
lightweight = float(np.clip(100 * (1.0 - density / 8.0) * (specific_strength / 300.0), 5, 98))
rows.append(
{
"material_id": f"{family[:3].upper()}-{i:04d}",
"material_name": f"{grade}-{i % 100:02d}",
"family": family,
"grade": grade,
"density_g_cm3": round(density, 3),
"youngs_modulus_gpa": round(modulus, 2),
"yield_strength_mpa": round(yield_s, 1),
"uts_mpa": round(uts, 1),
"elongation_pct": round(elong, 2),
"failure_strain": round(fail, 4),
"strain_rate_sensitivity": round(srs, 4),
"cost_usd_kg": round(cost, 2),
"co2_kg_kg": round(co2, 2),
"recyclability": round(recycl, 3),
"specific_strength": round(specific_strength, 2),
"specific_stiffness": round(specific_stiffness, 2),
"energy_absorption_potential": round(energy_abs_potential, 3),
"ductility_index": round(ductility_index, 3),
"crashworthiness_index": round(crash_index, 2),
"strength_to_weight": round(strength_weight, 2),
"failure_risk": round(failure_risk, 3),
"cost_performance_score": round(cost_perf, 2),
"sustainability_score": round(sustain, 2),
"lightweighting_score": round(lightweight, 2),
"material_card_type": MATERIAL_CARD_MAP[family],
"confidence_score": round(float(RNG.uniform(0.55, 0.97)), 3),
"source": RNG.choice(
["MatWeb", "NIST MDR", "Literature", "CAE Benchmark", "Public Dataset"]
),
}
)
df = pd.DataFrame(rows)
return df.sample(frac=1.0, random_state=42).reset_index(drop=True)
def generate_stress_strain(materials: pd.DataFrame, n_curves: int = 400) -> pd.DataFrame:
"""Generate plastic stress–strain curves for a subset of materials."""
sample = materials.sample(n=min(n_curves, len(materials)), random_state=7)
curve_rows: list[dict] = []
strains = np.linspace(0, 0.25, 40)
for _, mat in sample.iterrows():
e = mat["youngs_modulus_gpa"] * 1000 # MPa
ys = mat["yield_strength_mpa"]
uts = mat["uts_mpa"]
n_hard = RNG.uniform(0.08, 0.28)
for eps in strains:
if eps * e < ys:
stress = eps * e
else:
plastic = max(eps - ys / e, 0)
stress = ys + (uts - ys) * (1 - np.exp(-plastic / n_hard))
stress = min(stress, uts * 1.05)
rate_factor = 1.0 + mat["strain_rate_sensitivity"] * np.log1p(100)
curve_rows.append(
{
"material_id": mat["material_id"],
"material_name": mat["material_name"],
"family": mat["family"],
"strain": round(float(eps), 5),
"stress_mpa": round(float(stress * rate_factor / rate_factor), 2),
"stress_high_rate_mpa": round(float(stress * rate_factor), 2),
}
)
return pd.DataFrame(curve_rows)
def generate_recommendations(materials: pd.DataFrame, n: int = 3000) -> pd.DataFrame:
"""Generate crash-scenario recommendation / prediction records."""
rows: list[dict] = []
sample = materials.sample(n=min(n, len(materials) * 2), replace=True, random_state=11)
for i, (_, mat) in enumerate(sample.iterrows()):
scenario = CRASH_SCENARIOS[i % len(CRASH_SCENARIOS)]
component = COMPONENTS[i % len(COMPONENTS)]
thickness = float(RNG.uniform(0.8, 3.5))
joining = JOINING_METHODS[i % len(JOINING_METHODS)]
base = mat["crashworthiness_index"]
thickness_factor = np.clip(thickness / 2.0, 0.5, 1.6)
energy = float(base * thickness_factor * RNG.uniform(0.85, 1.15))
intrusion = float(np.clip(80 - base * 0.5 - thickness * 8 + RNG.normal(0, 5), 5, 120))
peak_force = float(mat["uts_mpa"] * thickness * 0.015 * RNG.uniform(0.8, 1.2))
cfe = float(np.clip(0.45 + base / 300 + RNG.normal(0, 0.05), 0.3, 0.95))
sea = float(mat["energy_absorption_potential"] * thickness_factor * RNG.uniform(0.9, 1.1))
crash_score = float(
np.clip(
0.3 * energy
+ 0.2 * (100 - intrusion)
+ 0.15 * cfe * 100
+ 0.15 * mat["lightweighting_score"]
+ 0.1 * mat["cost_performance_score"]
+ 0.1 * mat["sustainability_score"],
10,
98,
)
)
weight_reduction = float(
np.clip((3.0 - mat["density_g_cm3"]) / 3.0 * 40 + RNG.normal(0, 3), -5, 55)
)
sim_risk = float(np.clip(mat["failure_risk"] * 100 + RNG.normal(0, 5), 5, 95))
rows.append(
{
"rec_id": f"REC-{i:05d}",
"material_id": mat["material_id"],
"material_name": mat["material_name"],
"family": mat["family"],
"component": component,
"crash_scenario": scenario,
"thickness_mm": round(thickness, 2),
"joining_method": joining,
"energy_absorption_kj": round(energy, 2),
"intrusion_mm": round(intrusion, 2),
"peak_force_kn": round(peak_force, 2),
"crush_force_efficiency": round(cfe, 3),
"specific_energy_absorption": round(sea, 3),
"crash_score": round(crash_score, 2),
"weight_reduction_pct": round(weight_reduction, 2),
"cost_score": round(mat["cost_performance_score"], 2),
"sustainability_score": round(mat["sustainability_score"], 2),
"lightweighting_score": round(mat["lightweighting_score"], 2),
"simulation_risk": round(sim_risk, 2),
"failure_risk": round(mat["failure_risk"], 3),
"solver": SOLVERS[i % len(SOLVERS)],
"validation_level": RNG.choice(
[
"Coupon Test",
"Component Test",
"CAE Validation",
"Physical Crash",
"Certification Align",
]
),
"required_test": RNG.choice(
[
"Tensile + Strain-Rate",
"3-Point Bend",
"Drop Tower",
"Component Crush",
"Side Pole Sled",
"Full-Vehicle Barrier",
]
),
}
)
return pd.DataFrame(rows)
def generate_validation(recommendations: pd.DataFrame, n: int = 1500) -> pd.DataFrame:
"""Generate AI vs CAE / NHTSA-style validation comparison records."""
sample = recommendations.sample(n=min(n, len(recommendations)), random_state=21)
rows: list[dict] = []
for i, (_, rec) in enumerate(sample.iterrows()):
ai = rec["crash_score"]
noise = RNG.normal(0, 4)
cae = float(np.clip(ai + noise, 5, 100))
physical = float(np.clip(cae + RNG.normal(0, 3), 5, 100))
error_ai_cae = abs(ai - cae) / max(cae, 1e-6) * 100
error_cae_phys = abs(cae - physical) / max(physical, 1e-6) * 100
rows.append(
{
"val_id": f"VAL-{i:04d}",
"rec_id": rec["rec_id"],
"material_name": rec["material_name"],
"family": rec["family"],
"component": rec["component"],
"crash_scenario": rec["crash_scenario"],
"ai_crash_score": round(ai, 2),
"cae_crash_score": round(cae, 2),
"physical_crash_score": round(physical, 2),
"ai_cae_error_pct": round(error_ai_cae, 2),
"cae_physical_error_pct": round(error_cae_phys, 2),
"nhtsa_star_proxy": int(np.clip(round(physical / 20), 1, 5)),
"standard": RNG.choice(["Euro NCAP", "FMVSS", "IIHS", "OEM Internal"]),
"pass_fail": "Pass" if error_ai_cae < 12 else "Review",
}
)
return pd.DataFrame(rows)
def generate_all(data_dir: Path | str) -> dict[str, pd.DataFrame]:
"""Generate and persist all datasets."""
data_dir = Path(data_dir)
data_dir.mkdir(parents=True, exist_ok=True)
materials = generate_materials(6000)
stress = generate_stress_strain(materials, 400)
recommendations = generate_recommendations(materials, 3500)
validation = generate_validation(recommendations, 1800)
materials.to_csv(data_dir / "materials.csv", index=False)
stress.to_csv(data_dir / "stress_strain.csv", index=False)
recommendations.to_csv(data_dir / "recommendations.csv", index=False)
validation.to_csv(data_dir / "validation.csv", index=False)
return {
"materials": materials,
"stress_strain": stress,
"recommendations": recommendations,
"validation": validation,
}
if __name__ == "__main__":
out = Path(__file__).resolve().parent.parent / "data"
datasets = generate_all(out)
for name, df in datasets.items():
print(f"{name}: {len(df)} rows")