{"id":25110,"date":"2026-11-13T02:52:33","date_gmt":"2026-11-13T02:52:33","guid":{"rendered":"https:\/\/www.mechstream.com\/?p=25110"},"modified":"2026-11-13T02:55:38","modified_gmt":"2026-11-13T02:55:38","slug":"welding-distortion-calculator","status":"publish","type":"post","link":"https:\/\/www.mechstream.com\/es\/welding-distortion-calculator\/","title":{"rendered":"Welding Distortion Calculator"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The <strong>Welding Distortion Calculator<\/strong> provides a preliminary estimate of angular distortion or transverse shrinkage based on welding process, material, plate thickness, weld type, and heat input. Use it to compare welding conditions and plan distortion control measures. Actual results vary with weld sequence, joint design, restraint, and shop conditions, so treat the estimate as a planning reference.<\/p>\n\n\n\n<!-- Welding Distortion Calculator: START (paste everything from here to END into a WordPress Custom HTML block) -->\n<style>\n  #wdc-calculator { --bg: #f3f5f8; --surface: #ffffff; --ink: #17202b; --muted: #46505e; --line: #d3d9e1; --line-strong: #aeb7c3; --primary: #1d4e7a; --primary-hover: #153a5c; --primary-ink: #ffffff; --accent: #a8480a; --focus: #f59e0b; --err: #b42318; --err-bg: #fef3f2; --warn: #7a4300; --warn-bg: #fff6e5; --low: #1b6a37; --low-bg: #e9f8ef; --mod: #7a4300; --mod-bg: #fff1d6; --high: #a1251b; --high-bg: #fde9e7; --radius: 10px; --shadow: 0 1px 2px rgba(16, 24, 40, .06), 0 2px 8px rgba(16, 24, 40, .05); }\n  #wdc-calculator * { box-sizing: border-box; }\n  #wdc-calculator { margin: 0; font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif; background: var(--bg); color: var(--ink); line-height: 1.5; font-size: 16px; }\n  #wdc-calculator .wrap { max-width: 1180px; margin: 0 auto; padding: 24px 16px 48px; }\n  #wdc-calculator .layout { display: grid; grid-template-columns: 1fr; gap: 20px; max-width: 860px; margin: 0 auto; }\n  #wdc-calculator .card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); }\n  #wdc-calculator .card-pad { padding: 20px; }\n  @media (max-width: 520px) {\n  #wdc-calculator .card-pad { padding: 16px; }\n  }\n  #wdc-calculator .mode { border: 0; padding: 0; margin: 0 0 18px; }\n  #wdc-calculator .mode legend { font-weight: 700; margin-bottom: 8px; padding: 0; }\n  #wdc-calculator .seg { display: grid; grid-template-columns: 1fr 1fr; gap: 0; border: 1px solid var(--line-strong); border-radius: 8px; overflow: hidden; }\n  #wdc-calculator .seg input { position: absolute; opacity: 0; width: 1px; height: 1px; }\n  #wdc-calculator .seg label { display: block; text-align: center; padding: 11px 10px; cursor: pointer; font-weight: 600; color: var(--muted); background: #f7f9fb; user-select: none; }\n  #wdc-calculator .seg label + input + label, #wdc-calculator .seg input + label ~ label { border-left: 1px solid var(--line-strong); }\n  #wdc-calculator .seg input:checked + label { background: var(--primary); color: var(--primary-ink); }\n  #wdc-calculator .seg input:focus-visible + label { outline: 3px solid var(--focus); outline-offset: -3px; }\n  #wdc-calculator fieldset.group { border: 1px solid var(--line); border-radius: 8px; padding: 14px 14px 4px; margin: 0 0 16px; }\n  #wdc-calculator fieldset.group > legend { font-weight: 700; padding: 0 6px; font-size: .98rem; }\n  #wdc-calculator .grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 14px; }\n  @media (max-width: 560px) {\n  #wdc-calculator .grid { grid-template-columns: 1fr; }\n  }\n  #wdc-calculator .full { grid-column: 1 \/ -1; }\n  #wdc-calculator .field { margin-bottom: 12px; }\n  #wdc-calculator .field > label, #wdc-calculator .field .label { display: block; font-weight: 600; font-size: .93rem; margin-bottom: 4px; }\n  #wdc-calculator .opt { font-weight: 400; color: var(--muted); font-size: .85rem; }\n  #wdc-calculator .hint { font-size: .82rem; color: var(--muted); margin: 3px 0 0; }\n  #wdc-calculator input[type=\"text\"], #wdc-calculator select { width: 100%; font: inherit; color: var(--ink); background: #fff; border: 1px solid var(--line-strong); border-radius: 7px; padding: 9px 10px; min-height: 42px; }\n  #wdc-calculator select { appearance: none; -webkit-appearance: none; padding-right: 32px; background-image: linear-gradient(45deg, transparent 50%, #46505e 50%), linear-gradient(135deg, #46505e 50%, transparent 50%); background-position: calc(100% - 17px) 52%, calc(100% - 12px) 52%; background-size: 5px 5px; background-repeat: no-repeat; }\n  #wdc-calculator input:focus-visible, #wdc-calculator select:focus-visible, #wdc-calculator button:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; }\n  #wdc-calculator input[aria-invalid=\"true\"] { border-color: var(--err); background: var(--err-bg); }\n  #wdc-calculator .with-unit { display: grid; grid-template-columns: minmax(0, 1fr) 112px; gap: 6px; }\n  #wdc-calculator .with-unit.wide-unit { grid-template-columns: minmax(0, 1fr) 124px; }\n  #wdc-calculator .msg { margin: 4px 0 0; font-size: .84rem; }\n  #wdc-calculator .msg:empty { display: none; }\n  #wdc-calculator .msg.error { color: var(--err); font-weight: 600; }\n  #wdc-calculator .msg.warn { color: var(--warn); }\n  #wdc-calculator .msg.error::before { content: \"\u2715 \"; }\n  #wdc-calculator .msg.warn::before { content: \"\u26a0 \"; }\n  #wdc-calculator .radios { display: flex; flex-wrap: wrap; gap: 8px 18px; margin: 2px 0 12px; }\n  #wdc-calculator .radios label { display: inline-flex; align-items: center; gap: 7px; font-weight: 500; cursor: pointer; }\n  #wdc-calculator .radios input { width: 18px; height: 18px; accent-color: var(--primary); }\n  #wdc-calculator .formula { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: .8rem; background: #f3f5f8; padding: 6px 8px; border-radius: 6px; color: #2b3542; overflow-x: auto; }\n  #wdc-calculator .actions { display: flex; justify-content: center; margin-top: 10px; }\n  #wdc-calculator button { font: inherit; font-weight: 700; border-radius: 8px; padding: 11px 18px; cursor: pointer; min-height: 44px; border: 1px solid transparent; }\n  #wdc-calculator .btn-primary { background: var(--primary); color: var(--primary-ink); min-width: 220px; padding: 12px 40px; font-size: 1.05rem; }\n  #wdc-calculator .btn-primary:hover { background: var(--primary-hover); }\n  #wdc-calculator .results h2 { font-size: 1.2rem; margin: 0 0 12px; }\n  #wdc-calculator .error-summary { background: var(--err-bg); border: 1px solid #f4c7c2; color: var(--err); padding: 12px 14px; border-radius: 8px; font-weight: 600; }\n  #wdc-calculator .headline { border-radius: 10px; padding: 16px; margin-bottom: 14px; border: 1px solid var(--line); background: #f8fafc; }\n  #wdc-calculator .headline .kicker { font-size: .82rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 0 0 4px; }\n  #wdc-calculator .headline .value { font-size: clamp(1.6rem, 4vw, 2.15rem); font-weight: 800; line-height: 1.15; margin: 0; font-variant-numeric: tabular-nums; }\n  #wdc-calculator .headline .sub { margin: 6px 0 0; color: var(--muted); font-size: .92rem; }\n  #wdc-calculator .badge { display: inline-block; font-weight: 800; font-size: .85rem; padding: 3px 10px; border-radius: 999px; border: 1px solid currentColor; }\n  #wdc-calculator .risk-low { color: var(--low); background: var(--low-bg); }\n  #wdc-calculator .risk-moderate { color: var(--mod); background: var(--mod-bg); }\n  #wdc-calculator .risk-high { color: var(--high); background: var(--high-bg); }\n  #wdc-calculator .meter { margin-top: 12px; }\n  #wdc-calculator .meter-bar { position: relative; height: 10px; border-radius: 999px; background: linear-gradient(90deg, #2f9e5b 0 33.3%, #e0a21c 33.3% 66.6%, #d0453a 66.6% 100%); }\n  #wdc-calculator .meter-dot { position: absolute; top: 50%; width: 16px; height: 16px; border-radius: 50%; background: #fff; border: 3px solid var(--ink); transform: translate(-50%, -50%); }\n  #wdc-calculator .meter-labels { display: flex; justify-content: space-between; font-size: .75rem; color: var(--muted); margin-top: 4px; }\n  #wdc-calculator .results h3 { font-size: 1rem; margin: 18px 0 8px; }\n  #wdc-calculator table.kv { width: 100%; border-collapse: collapse; font-size: .9rem; }\n  #wdc-calculator table.kv th, #wdc-calculator table.kv td { text-align: left; padding: 6px 4px; border-bottom: 1px solid var(--line); vertical-align: top; }\n  #wdc-calculator table.kv th { font-weight: 600; color: var(--muted); width: 46%; }\n  #wdc-calculator table.kv td { font-variant-numeric: tabular-nums; }\n  #wdc-calculator ul.tight { margin: 0; padding-left: 20px; font-size: .92rem; }\n  #wdc-calculator ul.tight li { margin-bottom: 5px; }\n  #wdc-calculator .warn-box { background: var(--warn-bg); border: 1px solid #f1d29c; color: var(--warn); border-radius: 8px; padding: 10px 12px; font-size: .9rem; margin-bottom: 12px; }\n  #wdc-calculator .warn-box ul { margin: 4px 0 0; padding-left: 20px; }\n  #wdc-calculator .disclaimer { margin-top: 16px; font-size: .84rem; color: var(--muted); border-top: 1px solid var(--line); padding-top: 12px; }\n  #wdc-calculator .disclaimer p { margin: 0 0 6px; }\n  #wdc-calculator .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }\n  #wdc-calculator [hidden] { display: none !important; }\n  \/* Defensive resets: keep theme styles from leaking into the calculator *\/\n  #wdc-calculator { display: block; width: 100%; font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif; font-size: 16px; line-height: 1.5; color: var(--ink); background: var(--bg); text-align: left; padding: 0; margin: 0; }\n  #wdc-calculator h2, #wdc-calculator h3 { font-family: inherit; color: var(--ink); font-weight: 700; letter-spacing: normal; text-transform: none; padding: 0; border: 0; background: none; }\n  #wdc-calculator p, #wdc-calculator ul, #wdc-calculator li, #wdc-calculator table { font-family: inherit; letter-spacing: normal; }\n  #wdc-calculator ul.tight { list-style: disc; }\n  #wdc-calculator fieldset { min-width: 0; }\n  #wdc-calculator .seg { position: relative; }\n  #wdc-calculator legend { float: none; width: auto; font-family: inherit; color: var(--ink); font-size: inherit; line-height: 1.4; border: 0; text-transform: none; }\n  #wdc-calculator label { text-transform: none; letter-spacing: normal; color: inherit; }\n  #wdc-calculator input[type=\"text\"], #wdc-calculator select { margin: 0; box-shadow: none; line-height: 1.4; height: auto; max-width: none; font-size: 16px; }\n  #wdc-calculator input[type=\"radio\"] { margin: 0; box-shadow: none; }\n  #wdc-calculator button { text-transform: none; letter-spacing: normal; box-shadow: none; line-height: 1.2; text-decoration: none; }\n  #wdc-calculator table.kv { margin: 0; border: 0; table-layout: auto; background: transparent; }\n  #wdc-calculator table.kv th, #wdc-calculator table.kv td { background: transparent; border-left: 0; border-right: 0; border-top: 0; }\n<\/style>\n<div id=\"wdc-calculator\">\n<div class=\"wrap\">\n  <main class=\"layout\">\n    <!-- ================= INPUT FORM ================= -->\n    <section class=\"card card-pad\" aria-labelledby=\"wdc-inputs-title\">\n      <h2 id=\"wdc-inputs-title\" class=\"sr-only\">Inputs<\/h2>\n      <form id=\"wdc-calcForm\" novalidate onsubmit=\"return false;\">\n        <fieldset class=\"mode\">\n          <legend>Calculation mode<\/legend>\n          <div class=\"seg\">\n            <input type=\"radio\" id=\"wdc-modeAngular\" name=\"wdc-mode\" value=\"angular\" checked>\n            <label for=\"wdc-modeAngular\">Estimate Angular Distortion<\/label>\n            <input type=\"radio\" id=\"wdc-modeShrink\" name=\"wdc-mode\" value=\"shrink\">\n            <label for=\"wdc-modeShrink\">Estimate Transverse Shrinkage<\/label>\n          <\/div>\n        <\/fieldset>\n        <fieldset class=\"group\">\n          <legend>Process &amp; material<\/legend>\n          <div class=\"grid\">\n            <div class=\"field\">\n              <label for=\"wdc-process\">Welding process<\/label>\n              <select id=\"wdc-process\" aria-describedby=\"wdc-process-hint\">\n                <option value=\"gmaw\">MIG\/MAG (GMAW)<\/option>\n                <option value=\"smaw\">Stick (SMAW)<\/option>\n                <option value=\"gtaw\">TIG (GTAW)<\/option>\n                <option value=\"fcaw\">Flux-Cored (FCAW)<\/option>\n                <option value=\"saw\">Submerged Arc (SAW)<\/option>\n                <option value=\"custom\">Custom<\/option>\n              <\/select>\n              <p class=\"hint\" id=\"wdc-process-hint\">Sets the default thermal efficiency.<\/p>\n            <\/div>\n            <div class=\"field\">\n              <label for=\"wdc-material\">Material<\/label>\n              <select id=\"wdc-material\">\n                <option value=\"cs\">Carbon steel<\/option>\n                <option value=\"ss\">Stainless steel<\/option>\n                <option value=\"al\">Aluminum<\/option>\n                <option value=\"custom\">Custom<\/option>\n              <\/select>\n            <\/div>\n            <div class=\"field full\" id=\"wdc-customMatGroup\" hidden>\n              <label for=\"wdc-customMatFactor\">Custom material distortion factor <span class=\"opt\">(optional, relative to carbon steel = 1.0)<\/span><\/label>\n              <input type=\"text\" id=\"wdc-customMatFactor\" inputmode=\"decimal\" autocomplete=\"off\" aria-describedby=\"wdc-customMatFactor-hint wdc-customMatFactor-msg\">\n              <p class=\"hint\" id=\"wdc-customMatFactor-hint\">Leave blank if unknown. Without a factor, only a relative risk level is shown.<\/p>\n              <p class=\"msg\" id=\"wdc-customMatFactor-msg\"><\/p>\n            <\/div>\n          <\/div>\n        <\/fieldset>\n        <fieldset class=\"group\">\n          <legend>Joint geometry<\/legend>\n          <div class=\"grid\">\n            <div class=\"field\">\n              <label for=\"wdc-thickness\">Plate thickness<\/label>\n              <div class=\"with-unit\">\n                <input type=\"text\" id=\"wdc-thickness\" inputmode=\"decimal\" autocomplete=\"off\" aria-describedby=\"wdc-thickness-msg\">\n                <select id=\"wdc-thicknessUnit\" aria-label=\"Plate thickness unit\" data-kind=\"len\" data-target=\"thickness\">\n                  <option value=\"mm\">mm<\/option><option value=\"in\">in<\/option>\n                <\/select>\n              <\/div>\n              <p class=\"msg\" id=\"wdc-thickness-msg\"><\/p>\n            <\/div>\n            <div class=\"field\">\n              <label for=\"wdc-length\">Weld length<\/label>\n              <div class=\"with-unit\">\n                <input type=\"text\" id=\"wdc-length\" inputmode=\"decimal\" autocomplete=\"off\" aria-describedby=\"wdc-length-msg\">\n                <select id=\"wdc-lengthUnit\" aria-label=\"Weld length unit\" data-kind=\"len\" data-target=\"length\">\n                  <option value=\"mm\">mm<\/option><option value=\"in\">in<\/option>\n                <\/select>\n              <\/div>\n              <p class=\"msg\" id=\"wdc-length-msg\"><\/p>\n            <\/div>\n            <div class=\"field\">\n              <label for=\"wdc-joint\">Weld type<\/label>\n              <select id=\"wdc-joint\" aria-describedby=\"wdc-joint-hint\">\n                <option value=\"butt\">Butt joint (single pass)<\/option>\n                <option value=\"fillet\">Fillet joint (single pass, one side)<\/option>\n                <option value=\"multipass\">Multi-pass weld (groove)<\/option>\n              <\/select>\n              <p class=\"hint\" id=\"wdc-joint-hint\">Choose multi-pass to enter the number of passes.<\/p>\n            <\/div>\n            <div class=\"field\" id=\"wdc-passesGroup\" hidden>\n              <label for=\"wdc-passes\">Number of weld passes<\/label>\n              <input type=\"text\" id=\"wdc-passes\" inputmode=\"numeric\" autocomplete=\"off\" aria-describedby=\"wdc-passes-msg\">\n              <p class=\"msg\" id=\"wdc-passes-msg\"><\/p>\n            <\/div>\n          <\/div>\n        <\/fieldset>\n        <fieldset class=\"group\">\n          <legend>Heat input<\/legend>\n          <div class=\"radios\" role=\"radiogroup\" aria-label=\"Heat input method\">\n            <label><input type=\"radio\" name=\"wdc-hiMethod\" value=\"calc\" checked> Calculate from current, voltage &amp; speed<\/label>\n            <label><input type=\"radio\" name=\"wdc-hiMethod\" value=\"direct\"> Enter heat input directly<\/label>\n          <\/div>\n          <div id=\"wdc-hiDirectGroup\" hidden>\n            <div class=\"field\">\n              <label for=\"wdc-heatInput\">Heat input per pass<\/label>\n              <div class=\"with-unit\">\n                <input type=\"text\" id=\"wdc-heatInput\" inputmode=\"decimal\" autocomplete=\"off\" aria-describedby=\"wdc-heatInput-hint wdc-heatInput-msg\">\n                <select id=\"wdc-heatInputUnit\" aria-label=\"Heat input unit\" data-kind=\"hi\" data-target=\"heatInput\">\n                  <option value=\"kJ\/mm\">kJ\/mm<\/option><option value=\"kJ\/in\">kJ\/in<\/option>\n                <\/select>\n              <\/div>\n              <p class=\"hint\" id=\"wdc-heatInput-hint\">Net heat input, with arc efficiency already applied.<\/p>\n              <p class=\"msg\" id=\"wdc-heatInput-msg\"><\/p>\n            <\/div>\n          <\/div>\n          <div id=\"wdc-hiCalcGroup\">\n            <div class=\"grid\">\n              <div class=\"field\">\n                <label for=\"wdc-voltage\">Arc voltage (V)<\/label>\n                <input type=\"text\" id=\"wdc-voltage\" inputmode=\"decimal\" autocomplete=\"off\" aria-describedby=\"wdc-voltage-msg\">\n                <p class=\"msg\" id=\"wdc-voltage-msg\"><\/p>\n              <\/div>\n              <div class=\"field\">\n                <label for=\"wdc-current\">Welding current (A)<\/label>\n                <input type=\"text\" id=\"wdc-current\" inputmode=\"decimal\" autocomplete=\"off\" aria-describedby=\"wdc-current-msg\">\n                <p class=\"msg\" id=\"wdc-current-msg\"><\/p>\n              <\/div>\n              <div class=\"field\">\n                <label for=\"wdc-speed\">Travel speed<\/label>\n                <div class=\"with-unit wide-unit\">\n                  <input type=\"text\" id=\"wdc-speed\" inputmode=\"decimal\" autocomplete=\"off\" aria-describedby=\"wdc-speed-msg\">\n                  <select id=\"wdc-speedUnit\" aria-label=\"Travel speed unit\" data-kind=\"speed\" data-target=\"speed\">\n                    <option value=\"mm\/min\">mm\/min<\/option><option value=\"in\/min\">in\/min<\/option>\n                  <\/select>\n                <\/div>\n                <p class=\"msg\" id=\"wdc-speed-msg\"><\/p>\n              <\/div>\n              <div class=\"field\">\n                <label for=\"wdc-efficiency\">Thermal (process) efficiency<\/label>\n                <input type=\"text\" id=\"wdc-efficiency\" inputmode=\"decimal\" autocomplete=\"off\" aria-describedby=\"wdc-efficiency-hint wdc-efficiency-msg\">\n                <p class=\"hint\" id=\"wdc-efficiency-hint\">Decimal between 0 and 1 (for example 0.8 = 80%).<\/p>\n                <p class=\"msg\" id=\"wdc-efficiency-msg\"><\/p>\n              <\/div>\n              <div class=\"full\">\n                <p class=\"formula\" aria-label=\"Heat input formula\">Heat input (kJ\/mm) = V \u00d7 A \u00d7 \u03b7 \u00d7 60 \u00f7 (travel speed [mm\/min] \u00d7 1000)<\/p>\n              <\/div>\n            <\/div>\n          <\/div>\n        <\/fieldset>\n        <fieldset class=\"group\">\n          <legend>Restraint &amp; tacking <span class=\"opt\">(optional)<\/span><\/legend>\n          <div class=\"grid\">\n            <div class=\"field\">\n              <label for=\"wdc-restraint\">Restraint level<\/label>\n              <select id=\"wdc-restraint\" aria-describedby=\"wdc-restraint-hint\">\n                <option value=\"\">Not specified<\/option>\n                <option value=\"low\">Low (free \/ lightly clamped)<\/option>\n                <option value=\"medium\">Medium (clamped \/ tacked)<\/option>\n                <option value=\"high\">High (rigid fixture \/ stiff structure)<\/option>\n              <\/select>\n              <p class=\"hint\" id=\"wdc-restraint-hint\">More restraint can reduce visible movement but raises residual stress.<\/p>\n            <\/div>\n            <div class=\"field\">\n              <label for=\"wdc-tack\">Tack weld spacing<\/label>\n              <div class=\"with-unit\">\n                <input type=\"text\" id=\"wdc-tack\" inputmode=\"decimal\" autocomplete=\"off\" aria-describedby=\"wdc-tack-msg\">\n                <select id=\"wdc-tackUnit\" aria-label=\"Tack weld spacing unit\" data-kind=\"len\" data-target=\"tack\">\n                  <option value=\"mm\">mm<\/option><option value=\"in\">in<\/option>\n                <\/select>\n              <\/div>\n              <p class=\"msg\" id=\"wdc-tack-msg\"><\/p>\n            <\/div>\n            <div class=\"field\" id=\"wdc-resultUnitGroup\" hidden>\n              <label for=\"wdc-resultUnit\">Shrinkage result unit<\/label>\n              <select id=\"wdc-resultUnit\">\n                <option value=\"mm\">mm<\/option><option value=\"in\">in<\/option>\n              <\/select>\n            <\/div>\n          <\/div>\n        <\/fieldset>\n        <div class=\"actions\">\n          <button type=\"submit\" class=\"btn-primary\" id=\"wdc-btnCalc\">Calculate<\/button>\n        <\/div>\n      <\/form>\n    <\/section>\n    <!-- ================= RESULTS ================= -->\n    <section class=\"card card-pad results\" id=\"wdc-resultsSection\" aria-labelledby=\"wdc-results-title\" hidden>\n      <h2 id=\"wdc-results-title\" tabindex=\"-1\">Results<\/h2>\n      <div id=\"wdc-results\" aria-live=\"polite\"><\/div>\n    <\/section>\n  <\/main>\n<\/div>\n<\/div>\n<script>\n(function () {\n  \"use strict\";\n  \/* =====================================================================\n     EDITABLE ESTIMATION COEFFICIENTS\n     All numbers below are simplified planning coefficients, not universal\n     constants. Calibrate them against your own shop measurements.\n     ===================================================================== *\/\n  var CONFIG = {\n    IN_TO_MM: 25.4,               \/\/ 1 inch = 25.4 mm (exact)\n    \/\/ Default thermal (arc) efficiency per process, plus a typical heat-input\n    \/\/ window (kJ\/mm) used only to warn about unusual values.\n    processes: {\n      gmaw:   { label: \"MIG\/MAG (GMAW)\",       eff: 0.8, hiRange: [0.15, 2.5] },\n      smaw:   { label: \"Stick (SMAW)\",         eff: 0.8, hiRange: [0.4, 3.0] },\n      gtaw:   { label: \"TIG (GTAW)\",           eff: 0.6, hiRange: [0.08, 1.5] },\n      fcaw:   { label: \"Flux-Cored (FCAW)\",    eff: 0.8, hiRange: [0.5, 3.0] },\n      saw:    { label: \"Submerged Arc (SAW)\",  eff: 1.0, hiRange: [0.8, 6.0] },\n      custom: { label: \"Custom\",               eff: null, hiRange: [0.05, 8.0] }\n    },\n    \/\/ Relative distortion tendency vs carbon steel (= 1.0).\n    \/\/ Stainless: higher expansion, lower conductivity. Aluminum: high expansion\n    \/\/ (more shrinkage) but high conductivity (flatter through-thickness gradient).\n    materials: {\n      cs:     { label: \"Carbon steel\",    angular: 1.0,  shrink: 1.0 },\n      ss:     { label: \"Stainless steel\", angular: 1.3,  shrink: 1.3 },\n      al:     { label: \"Aluminum\",        angular: 0.85, shrink: 1.8 },\n      custom: { label: \"Custom\",          angular: null, shrink: null }\n    },\n    \/\/ Weld type multipliers. \"multipass\" is treated as a multi-pass groove weld.\n    joints: {\n      butt:      { label: \"Butt joint\",       angular: 1.0, shrink: 1.0 },\n      fillet:    { label: \"Fillet joint\",     angular: 0.8, shrink: 0.35 },\n      multipass: { label: \"Multi-pass weld\",  angular: 1.0, shrink: 1.0 }\n    },\n    \/\/ Restraint multipliers on visible movement (\"\" = not specified).\n    restraint: {\n      \"\":     { label: \"Not specified (free assumed)\", angular: 1.0,  shrink: 1.0,  extraUnc: 0.10 },\n      low:    { label: \"Low\",    angular: 1.0,  shrink: 1.0,  extraUnc: 0 },\n      medium: { label: \"Medium\", angular: 0.7,  shrink: 0.75, extraUnc: 0 },\n      high:   { label: \"High\",   angular: 0.45, shrink: 0.5,  extraUnc: 0.05 }\n    },\n    \/\/ Tack spacing effect, based on spacing \u00f7 plate thickness.\n    tack: [\n      { maxRatio: 20,       factor: 0.95, note: \"close\" },\n      { maxRatio: 50,       factor: 1.0,  note: \"moderate\" },\n      { maxRatio: Infinity, factor: 1.1,  note: \"wide\", extraUnc: 0.05 }\n    ],\n    \/\/ Angular model: angle(deg) = K \u00d7 index\n    \/\/ index = mat \u00d7 joint \u00d7 restraint \u00d7 tack \u00d7 f(P) \u00d7 passes^passExp\n    \/\/ f(P) = (P\/Pc)^a \u00d7 exp(a \u00d7 (1 \u2212 P\/Pc)), P = Q\/t\u00b2 (J\/mm\u00b3); peaks at P = Pc.\n    angular: {\n      K: 1.5, Pc: 20, a: 0.7, passExp: 0.7,\n      validP: [0.5, 60],              \/\/ numeric estimate supported only inside this window\n      thresholds: { low: 0.9, high: 2.4 },   \/\/ degrees: < low = low risk, > high = high risk\n      meterMax: 4.0\n    },\n    \/\/ Shrinkage model: shrinkage(mm) = K \u00d7 index\n    \/\/ index = mat \u00d7 joint \u00d7 restraint \u00d7 tack \u00d7 (Q\/t \u00f7 refQt) \u00d7 passes^passExp\n    shrink: {\n      K: 0.6, refQt: 100, passExp: 0.6,\n      validQt: [5, 500],              \/\/ J\/mm\u00b2 window for numeric estimate\n      thresholds: { low: 0.45, high: 1.2 },  \/\/ mm\n      meterMax: 2.0\n    },\n    \/\/ Range half-width as a fraction of the central value.\n    uncertainty: { base: 0.35, customProcess: 0.05 },\n    \/\/ Conditions for showing a numeric value instead of risk only.\n    validity: { thicknessMm: [1.5, 40], minLengthMm: 25, minLengthPerT: 5 },\n    \/\/ Practical ranges: values outside trigger a warning, never clamping.\n    practical: {\n      thicknessMm: [1, 100], lengthMm: [10, 10000], tackMm: [10, 1000],\n      voltage: [8, 45], current: [20, 1200], speedMmMin: [50, 2000],\n      efficiency: [0.5, 1.0], passes: [2, 60], customMat: [0.3, 3.0]\n    }\n  };\n  \/* ===================== Helpers ===================== *\/\n  var ROOT = document.getElementById(\"wdc-calculator\");\n  if (!ROOT) return;\n  \/\/ All element ids are prefixed with \"wdc-\" to avoid clashes with the host page\n  var $ = function (id) { return document.getElementById(\"wdc-\" + id); };\n  var NUM_RE = \/^[+-]?(\\d+([.,]\\d*)?|[.,]\\d+)([eE][+-]?\\d+)?$\/;\n  \/\/ Unit conversions to internal base units (mm, kJ\/mm, mm\/min).\n  function toBase(kind, unit, v) {\n    if (kind === \"len\" || kind === \"speed\") return unit === \"in\" || unit === \"in\/min\" ? v * CONFIG.IN_TO_MM : v;\n    if (kind === \"hi\") return unit === \"kJ\/in\" ? v \/ CONFIG.IN_TO_MM : v;   \/\/ kJ\/in \u00f7 25.4 = kJ\/mm\n    return v;\n  }\n  function fromBase(kind, unit, v) {\n    if (kind === \"len\" || kind === \"speed\") return unit === \"in\" || unit === \"in\/min\" ? v \/ CONFIG.IN_TO_MM : v;\n    if (kind === \"hi\") return unit === \"kJ\/in\" ? v * CONFIG.IN_TO_MM : v;\n    return v;\n  }\n  function fmt(v, d) { return Number(v).toFixed(d == null ? 2 : d); }\n  function sig(v) { return String(parseFloat(Number(v).toPrecision(4))); }\n  function esc(s) { return String(s).replace(\/\\x26\/g, \"\\x26amp;\").replace(\/\\x3c\/g, \"\\x26lt;\").replace(\/\\x3e\/g, \"\\x26gt;\").replace(\/\\x22\/g, \"\\x26quot;\"); }\n  function radioVal(name) { var r = ROOT.querySelector('input[name=\"wdc-' + name + '\"]:checked'); return r ? r.value : null; }\n  var errors = [];\n  var warnings = [];\n  function setMsg(id, text, type) {\n    var el = $(id + \"-msg\");\n    if (!el) return;\n    el.textContent = text;\n    el.className = \"msg \" + type;\n    if (type === \"error\") $(id).setAttribute(\"aria-invalid\", \"true\");\n  }\n  function clearMsgs() {\n    errors = []; warnings = [];\n    ROOT.querySelectorAll(\".msg\").forEach(function (m) { m.textContent = \"\"; m.className = \"msg\"; });\n    ROOT.querySelectorAll('[aria-invalid=\"true\"]').forEach(function (i) { i.removeAttribute(\"aria-invalid\"); });\n  }\n  function addError(id, text) { errors.push({ id: id, text: text }); setMsg(id, text, \"error\"); }\n  function addWarn(id, text) { warnings.push(text); if (!$(id + \"-msg\").textContent) setMsg(id, text, \"warn\"); }\n  \/\/ Reads a required (or optional) strictly positive number. Returns null on error\/blank.\n  function readPositive(id, label, opts) {\n    opts = opts || {};\n    var raw = $(id).value.trim();\n    if (raw === \"\") {\n      if (opts.optional) return null;\n      addError(id, label + \" is required.\");\n      return null;\n    }\n    if (!NUM_RE.test(raw)) { addError(id, label + \" must be a number.\"); return null; }\n    var v = parseFloat(raw.replace(\",\", \".\"));\n    if (!isFinite(v)) { addError(id, label + \" must be a number.\"); return null; }\n    if (v <= 0) { addError(id, label + \" must be greater than zero.\"); return null; }\n    if (opts.integer ? Math.floor(v) !== v : false) { addError(id, label + \" must be a whole number.\"); return null; }\n    if (opts.max == null ? false : v > opts.max) { addError(id, opts.maxMsg); return null; }\n    return v;\n  }\n  \/\/ Warn (never clamp) when a base-unit value is outside a practical range.\n  function checkRange(id, label, vBase, range, kind, unit) {\n    if (!(vBase < range[0] || vBase > range[1])) return;\n    var lo = kind ? sig(fromBase(kind, unit, range[0])) : range[0];\n    var hi = kind ? sig(fromBase(kind, unit, range[1])) : range[1];\n    addWarn(id, label + \" is outside the typical range (\" + lo + \"\u2013\" + hi + (unit ? \" \" + unit : \"\") + \"). It is used as entered; please double-check.\");\n  }\n  \/* ===================== UI state ===================== *\/\n  function updateVisibility() {\n    var joint = $(\"joint\").value;\n    $(\"passesGroup\").hidden = joint !== \"multipass\";\n    $(\"customMatGroup\").hidden = $(\"material\").value !== \"custom\";\n    var direct = radioVal(\"hiMethod\") === \"direct\";\n    $(\"hiDirectGroup\").hidden = !direct;\n    $(\"hiCalcGroup\").hidden = direct;\n    $(\"resultUnitGroup\").hidden = radioVal(\"mode\") !== \"shrink\";\n  }\n  function applyProcessDefaults() {\n    var p = CONFIG.processes[$(\"process\").value];\n    $(\"efficiency\").value = p.eff == null ? \"\" : p.eff;\n    $(\"process-hint\").textContent = p.eff == null\n      ? \"Custom process: enter your own thermal efficiency.\"\n      : \"Default thermal efficiency for this process: \" + p.eff + \".\";\n  }\n  \/\/ Convert an entered value when its unit selector changes, so the physical value is preserved.\n  function initUnitConversion() {\n    ROOT.querySelectorAll(\"select[data-kind]\").forEach(function (sel) {\n      sel.dataset.prev = sel.value;\n      sel.addEventListener(\"change\", function () {\n        var input = $(sel.dataset.target);\n        var raw = input.value.trim();\n        if (raw !== \"\" ? NUM_RE.test(raw) : false) {\n          var v = parseFloat(raw.replace(\",\", \".\"));\n          var base = toBase(sel.dataset.kind, sel.dataset.prev, v);\n          input.value = sig(fromBase(sel.dataset.kind, sel.value, base));\n        }\n        sel.dataset.prev = sel.value;\n      });\n    });\n  }\n  \/* ===================== Calculation ===================== *\/\n  function calculate() {\n    clearMsgs();\n    var mode = radioVal(\"mode\");\n    var processKey = $(\"process\").value;\n    var matKey = $(\"material\").value;\n    var jointKey = $(\"joint\").value;\n    var restraintKey = $(\"restraint\").value;\n    var hiMethod = radioVal(\"hiMethod\");\n    var P = CONFIG.practical;\n    \/\/ --- Geometry (converted to mm) ---\n    var tUnit = $(\"thicknessUnit\").value, lUnit = $(\"lengthUnit\").value, tackUnit = $(\"tackUnit\").value;\n    var tIn = readPositive(\"thickness\", \"Plate thickness\");\n    var lIn = readPositive(\"length\", \"Weld length\");\n    var t = tIn == null ? null : toBase(\"len\", tUnit, tIn);\n    var L = lIn == null ? null : toBase(\"len\", lUnit, lIn);\n    var passes = 1;\n    if (jointKey === \"multipass\") {\n      var n = readPositive(\"passes\", \"Number of passes\", { integer: true });\n      if (n != null) passes = n;\n    }\n    var customMat = null;\n    if (matKey === \"custom\") customMat = readPositive(\"customMatFactor\", \"Material factor\", { optional: true });\n    \/\/ --- Heat input (kJ\/mm) ---\n    var hi = null, V = null, A = null, spd = null, eff = null, sUnit = $(\"speedUnit\").value;\n    if (hiMethod === \"direct\") {\n      var hiIn = readPositive(\"heatInput\", \"Heat input\");\n      if (hiIn != null) hi = toBase(\"hi\", $(\"heatInputUnit\").value, hiIn);\n    } else {\n      V = readPositive(\"voltage\", \"Voltage\");\n      A = readPositive(\"current\", \"Current\");\n      var spdIn = readPositive(\"speed\", \"Travel speed\");\n      if (spdIn != null) spd = toBase(\"speed\", sUnit, spdIn);     \/\/ in\/min \u00d7 25.4 = mm\/min\n      eff = readPositive(\"efficiency\", \"Efficiency\", { max: 1, maxMsg: \"Efficiency must be between 0 and 1 (enter 0.8 for 80%).\" });\n      if (!(V == null || A == null || spd == null || eff == null)) {\n        \/\/ Heat input (kJ\/mm) = V \u00d7 A \u00d7 \u03b7 \u00d7 60 \u00f7 (speed[mm\/min] \u00d7 1000)\n        \/\/   V \u00d7 A = W = J\/s;  speed\/60 = mm\/s;  \u00f71000 converts J to kJ.\n        hi = (V * A * eff * 60) \/ (spd * 1000);\n      }\n    }\n    var tackIn = readPositive(\"tack\", \"Tack spacing\", { optional: true });\n    var tack = tackIn == null ? null : toBase(\"len\", tackUnit, tackIn);\n    if (errors.length) { renderErrors(); return; }\n    \/\/ --- Practical-range warnings (values are never clamped) ---\n    checkRange(\"thickness\", \"Plate thickness\", t, P.thicknessMm, \"len\", tUnit);\n    checkRange(\"length\", \"Weld length\", L, P.lengthMm, \"len\", lUnit);\n    if (jointKey === \"multipass\") {\n      if (passes < P.passes[0]) addWarn(\"passes\", \"A multi-pass weld with 1 pass is treated like a single-pass groove weld.\");\n      else checkRange(\"passes\", \"Number of passes\", passes, P.passes);\n    }\n    if (customMat != null) checkRange(\"customMatFactor\", \"Material factor\", customMat, P.customMat);\n    if (hiMethod === \"calc\") {\n      checkRange(\"voltage\", \"Voltage\", V, P.voltage, null, \"V\");\n      checkRange(\"current\", \"Current\", A, P.current, null, \"A\");\n      checkRange(\"speed\", \"Travel speed\", spd, P.speedMmMin, \"speed\", sUnit);\n      checkRange(\"efficiency\", \"Efficiency\", eff, P.efficiency);\n    }\n    var proc = CONFIG.processes[processKey];\n    if (hi < proc.hiRange[0] || hi > proc.hiRange[1]) {\n      var hiField = hiMethod === \"direct\" ? \"heatInput\" : \"speed\";\n      var u = hiMethod === \"direct\" ? $(\"heatInputUnit\").value : \"kJ\/mm\";\n      warnings.push(\"Heat input (\" + sig(fromBase(\"hi\", u, hi)) + \" \" + u + \") is unusual for \" + proc.label +\n        \" (typical \" + sig(fromBase(\"hi\", u, proc.hiRange[0])) + \"\u2013\" + sig(fromBase(\"hi\", u, proc.hiRange[1])) + \" \" + u + \").\");\n      if (hiField === \"heatInput\") setMsg(\"heatInput\", warnings[warnings.length - 1], \"warn\");\n    }\n    if (tack != null) {\n      checkRange(\"tack\", \"Tack spacing\", tack, P.tackMm, \"len\", tackUnit);\n      if (tack > L) addWarn(\"tack\", \"Tack spacing is longer than the weld length, so it has little effect.\");\n    }\n    \/\/ --- Model ---\n    var mat = CONFIG.materials[matKey];\n    var joint = CONFIG.joints[jointKey];\n    var rest = CONFIG.restraint[restraintKey];\n    var key = mode === \"angular\" ? \"angular\" : \"shrink\";\n    var M = CONFIG[key];\n    var matF = matKey === \"custom\" ? (customMat == null ? 1.0 : customMat) : mat[key];\n    var jointF = joint[key];\n    var restF = rest[key];\n    var tackBand = null, tackF = 1.0;\n    if (tack != null) {\n      var ratio = tack \/ t;\n      for (var i = 0; i < CONFIG.tack.length; i++) { if (ratio <= CONFIG.tack[i].maxRatio) { tackBand = CONFIG.tack[i]; break; } }\n      tackF = tackBand.factor;\n    }\n    var passF = Math.pow(passes, M.passExp);\n    var Q = hi * 1000;                       \/\/ J\/mm\n    var heatParam, heatF, heatLabel, heatUnit, validWindow;\n    if (mode === \"angular\") {\n      heatParam = Q \/ (t * t);               \/\/ P = Q\/t\u00b2  (J\/mm\u00b3)\n      var x = heatParam \/ M.Pc;\n      heatF = Math.pow(x, M.a) * Math.exp(M.a * (1 - x));\n      heatLabel = \"Q\/t\u00b2\"; heatUnit = \"J\/mm\u00b3\"; validWindow = M.validP;\n    } else {\n      heatParam = Q \/ t;                     \/\/ Q\/t (J\/mm\u00b2)\n      heatF = heatParam \/ M.refQt;\n      heatLabel = \"Q\/t\"; heatUnit = \"J\/mm\u00b2\"; validWindow = M.validQt;\n    }\n    var index = matF * jointF * restF * tackF * heatF * passF;\n    var central = M.K * index;              \/\/ degrees or mm\n    var risk = central < M.thresholds.low ? \"low\" : (central > M.thresholds.high ? \"high\" : \"moderate\");\n    \/\/ --- Is a numeric value supportable? ---\n    var reasons = [];\n    var V_ = CONFIG.validity;\n    if (matKey === \"custom\" ? customMat == null : false) reasons.push(\"Custom material selected without a distortion factor.\");\n    if (t < V_.thicknessMm[0] || t > V_.thicknessMm[1])\n      reasons.push(\"Plate thickness is outside the range the simplified model covers (\" + V_.thicknessMm[0] + \"\u2013\" + V_.thicknessMm[1] + \" mm).\");\n    if (heatParam < validWindow[0] || heatParam > validWindow[1])\n      reasons.push(\"The heat-input-to-thickness ratio (\" + heatLabel + \" = \" + fmt(heatParam) + \" \" + heatUnit + \") is outside the model's calibrated window (\" + validWindow[0] + \"\u2013\" + validWindow[1] + \").\");\n    if (L < Math.max(V_.minLengthMm, V_.minLengthPerT * t))\n      reasons.push(\"The weld is short relative to the thickness, so end effects dominate.\");\n    var numeric = reasons.length === 0;\n    \/\/ Uncertainty band (not clamped; just widened for less-specified inputs)\n    var unc = CONFIG.uncertainty.base + rest.extraUnc + (processKey === \"custom\" ? CONFIG.uncertainty.customProcess : 0) +\n              (tackBand ? (tackBand.extraUnc || 0) : 0);\n    var lo = central * (1 - unc), hiV = central * (1 + unc);\n    render({\n      mode: mode, processKey: processKey, matKey: matKey, jointKey: jointKey, restraintKey: restraintKey,\n      t: t, L: L, tUnit: tUnit, lUnit: lUnit, tackUnit: tackUnit, passes: passes, hi: hi, hiMethod: hiMethod,\n      V: V, A: A, spd: spd, sUnit: sUnit, eff: eff, tack: tack, tackBand: tackBand, customMat: customMat,\n      matF: matF, jointF: jointF, restF: restF, tackF: tackF, heatF: heatF, passF: passF,\n      heatParam: heatParam, heatLabel: heatLabel, heatUnit: heatUnit,\n      index: index, central: central, lo: lo, hiV: hiV, unc: unc, risk: risk, numeric: numeric, reasons: reasons, M: M\n    });\n  }\n  \/* ===================== Rendering ===================== *\/\n  function renderErrors() {\n    var first = errors[0];\n    $(\"resultsSection\").hidden = true;\n    $(\"results\").innerHTML = \"\";\n    if (first) $(first.id).focus();\n  }\n  function render(r) {\n    var isAng = r.mode === \"angular\";\n    var proc = CONFIG.processes[r.processKey];\n    var resUnit = $(\"resultUnit\").value;\n    var riskText = { low: \"Low\", moderate: \"Moderate\", high: \"High\" }[r.risk];\n    function shrinkStr(mm) { return resUnit === \"in\" ? fmt(mm \/ CONFIG.IN_TO_MM) + \" in\" : fmt(mm) + \" mm\"; }\n    function shrinkAlt(mm) { return resUnit === \"in\" ? fmt(mm) + \" mm\" : fmt(mm \/ CONFIG.IN_TO_MM) + \" in\"; }\n    var headValue, headSub;\n    if (r.numeric) {\n      if (isAng) {\n        headValue = fmt(r.lo) + \"\u00b0 \u2013 \" + fmt(r.hiV) + \"\u00b0\";\n        headSub = \"Central estimate \" + fmt(r.central) + \"\u00b0 (range \u00b1\" + Math.round(r.unc * 100) + \"%)\";\n      } else {\n        headValue = shrinkStr(r.lo) + \" \u2013 \" + shrinkStr(r.hiV);\n        headSub = \"Central estimate \" + shrinkStr(r.central) + \" (\" + shrinkAlt(r.lo) + \" \u2013 \" + shrinkAlt(r.hiV) + \"), range \u00b1\" + Math.round(r.unc * 100) + \"%\";\n      }\n    } else {\n      headValue = riskText + \" relative risk\";\n      headSub = \"A numeric value is not shown because the inputs fall outside what the simplified model can reasonably support.\";\n    }\n    var pct = Math.max(0, Math.min(100, (r.central \/ r.M.meterMax) * 100));\n    \/\/ Map thresholds onto equal thirds of the meter for readability\n    var th = r.M.thresholds, pos;\n    if (r.central <= th.low) pos = (r.central \/ th.low) * 33.3;\n    else if (r.central <= th.high) pos = 33.3 + ((r.central - th.low) \/ (th.high - th.low)) * 33.3;\n    else pos = 66.6 + Math.min(1, (r.central - th.high) \/ (r.M.meterMax - th.high)) * 33.4;\n    pos = Math.max(1, Math.min(99, pos || pct));\n    var html = \"\";\n    if (warnings.length) {\n      html += '\\x3cdiv class=\"warn-box\">\\x3cstrong>Check these inputs\\x3c\/strong>\\x3cul>' +\n        warnings.map(function (w) { return \"\\x3cli>\" + esc(w) + \"\\x3c\/li>\"; }).join(\"\") + \"\\x3c\/ul>\\x3c\/div>\";\n    }\n    html += '\\x3cdiv class=\"headline\">' +\n      '\\x3cp class=\"kicker\">' + (isAng ? \"Estimated angular distortion\" : \"Estimated transverse shrinkage\") + \"\\x3c\/p>\" +\n      '\\x3cp class=\"value\">' + esc(headValue) + \"\\x3c\/p>\" +\n      '\\x3cp class=\"sub\">' + esc(headSub) + \"\\x3c\/p>\" +\n      '\\x3cp style=\"margin:10px 0 0\">Relative risk: \\x3cspan class=\"badge risk-' + r.risk + '\">' + riskText + \"\\x3c\/span>\\x3c\/p>\" +\n      '\\x3cdiv class=\"meter\" aria-hidden=\"true\">\\x3cdiv class=\"meter-bar\">\\x3cspan class=\"meter-dot\" style=\"left:' + pos.toFixed(1) + '%\">\\x3c\/span>\\x3c\/div>' +\n      '\\x3cdiv class=\"meter-labels\">\\x3cspan>Low\\x3c\/span>\\x3cspan>Moderate\\x3c\/span>\\x3cspan>High\\x3c\/span>\\x3c\/div>\\x3c\/div>' +\n      \"\\x3c\/div>\";\n    if (!r.numeric) {\n      html += '\\x3ch3>Why only a risk level?\\x3c\/h3>\\x3cul class=\"tight\">' +\n        r.reasons.map(function (x) { return \"\\x3cli>\" + esc(x) + \"\\x3c\/li>\"; }).join(\"\") + \"\\x3c\/ul>\";\n    }\n    \/\/ Inputs and key values\n    var hiUnitShow = r.hiMethod === \"direct\" ? $(\"heatInputUnit\").value : \"kJ\/mm\";\n    var rows = [\n      [\"Mode\", isAng ? \"Angular distortion\" : \"Transverse shrinkage\"],\n      [\"Welding process\", proc.label],\n      [\"Material\", CONFIG.materials[r.matKey].label + (r.matKey === \"custom\" ? (r.customMat == null ? \" (no factor given)\" : \" (factor \" + r.customMat + \")\") : \"\")],\n      [\"Plate thickness\", sig(fromBase(\"len\", r.tUnit, r.t)) + \" \" + r.tUnit + (r.tUnit === \"in\" ? \" (\" + fmt(r.t) + \" mm)\" : \"\")],\n      [\"Weld length\", sig(fromBase(\"len\", r.lUnit, r.L)) + \" \" + r.lUnit],\n      [\"Weld type\", CONFIG.joints[r.jointKey].label],\n      [\"Passes\", String(r.passes)],\n      [\"Heat input per pass\", fmt(r.hi, 3) + \" kJ\/mm (\" + fmt(r.hi * CONFIG.IN_TO_MM) + \" kJ\/in)\" +\n        (r.hiMethod === \"calc\" ? \" \u2014 calculated\" : \" \u2014 entered\")]\n    ];\n    if (r.hiMethod === \"calc\") {\n      rows.push([\"Arc parameters\", r.V + \" V \u00d7 \" + r.A + \" A, \" + sig(fromBase(\"speed\", r.sUnit, r.spd)) + \" \" + r.sUnit + \", \u03b7 = \" + r.eff]);\n    }\n    rows.push([\"Total heat into joint\", fmt(r.hi * r.L * r.passes, 1) + \" kJ (heat input \u00d7 length \u00d7 passes)\"]);\n    rows.push([r.heatLabel + \" (heat \u00f7 thickness\" + (isAng ? \"\u00b2\" : \"\") + \")\", fmt(r.heatParam) + \" \" + r.heatUnit]);\n    rows.push([\"Restraint\", CONFIG.restraint[r.restraintKey].label]);\n    rows.push([\"Tack spacing\", r.tack == null ? \"Not specified\" : sig(fromBase(\"len\", r.tackUnit, r.tack)) + \" \" + r.tackUnit + \" (\" + r.tackBand.note + \", \" + fmt(r.tack \/ r.t, 1) + \"\u00d7 thickness)\"]);\n    rows.push([\"Distortion index\", fmt(r.index, 3) + \" (dimensionless, relative)\"]);\n    html += '\\x3ch3>Inputs and key values\\x3c\/h3>\\x3ctable class=\"kv\">\\x3ctbody>' +\n      rows.map(function (row) { return \"\\x3ctr>\\x3cth scope=\\\"row\\\">\" + esc(row[0]) + \"\\x3c\/th>\\x3ctd>\" + esc(row[1]) + \"\\x3c\/td>\\x3c\/tr>\"; }).join(\"\") +\n      \"\\x3c\/tbody>\\x3c\/table>\";\n    \/\/ Main factors\n    html += \"\\x3ch3>Main factors affecting this result\\x3c\/h3>\\x3cul class=\\\"tight\\\">\" + factorList(r).map(function (x) { return \"\\x3cli>\" + x + \"\\x3c\/li>\"; }).join(\"\") + \"\\x3c\/ul>\";\n    \/\/ Suggestions\n    html += \"\\x3ch3>Distortion-control suggestions\\x3c\/h3>\\x3cul class=\\\"tight\\\">\" + suggestions(r).map(function (x) { return \"\\x3cli>\" + esc(x) + \"\\x3c\/li>\"; }).join(\"\") + \"\\x3c\/ul>\";\n    html += '\\x3cdiv class=\"disclaimer\">' +\n      \"\\x3cp>\\x3cstrong>This is an approximate planning estimate.\\x3c\/strong> Weld sequence, joint preparation, fixture design, material properties, heat distribution and shop conditions can significantly change actual distortion.\\x3c\/p>\" +\n      \"\\x3cp>Restraint can reduce visible movement during welding, but it can increase residual stress, and parts may still move when they are unclamped, machined or stress-relieved.\\x3c\/p>\" +\n      \"\\x3cp>The result does not replace engineering analysis, a qualified Welding Procedure Specification (WPS), or a physical test.\\x3c\/p>\" +\n      \"\\x3c\/div>\";\n    $(\"results\").innerHTML = html;\n    $(\"resultsSection\").hidden = false;\n    $(\"results-title\").focus({ preventScroll: true });\n    $(\"resultsSection\").scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n  }\n  function factorList(r) {\n    var isAng = r.mode === \"angular\";\n    var out = [];\n    var mult = function (v) { return \"\\x3cstrong>\u00d7\" + fmt(v) + \"\\x3c\/strong>\"; };\n    if (isAng) {\n      var x = r.heatParam \/ r.M.Pc;\n      var where = x < 0.6 ? \"below the peak range: the heat stays near the weld face, and each pass adds only a small rotation\"\n        : x > 1.6 ? \"above the peak range: the plate heats more evenly through its thickness, so angular movement drops (thin sheet may buckle instead)\"\n        : \"near the peak range, where through-thickness temperature differences are largest\";\n      out.push(\"Heat vs thickness (Q\/t\u00b2 = \" + fmt(r.heatParam) + \" J\/mm\u00b3) \" + mult(r.heatF) + \": \" + where + \".\");\n    } else {\n      out.push(\"Heat vs thickness (Q\/t = \" + fmt(r.heatParam) + \" J\/mm\u00b2) \" + mult(r.heatF) + \": more heat per unit of thickness widens the hot zone that contracts on cooling.\");\n    }\n    if (r.passes > 1) out.push(\"Passes (\" + r.passes + \") \" + mult(r.passF) + \": distortion builds up with each pass, though at a decreasing rate as the joint stiffens.\");\n    out.push(\"Material \" + mult(r.matF) + \": \" + (r.matKey === \"ss\" ? \"stainless steel expands more and conducts heat less than carbon steel.\"\n      : r.matKey === \"al\" ? \"aluminum expands about twice as much as steel but spreads heat quickly.\"\n      : r.matKey === \"custom\" ? (r.customMat == null ? \"no factor given; carbon steel (1.0) assumed for the risk level only.\" : \"user-defined factor.\")\n      : \"reference material (1.0).\"));\n    out.push(\"Weld type \" + mult(r.jointF) + \": \" + (r.jointKey === \"fillet\"\n      ? (isAng ? \"a one-sided fillet pulls the flange toward the weld.\" : \"fillet welds produce limited shrinkage across the joint.\")\n      : r.jointKey === \"multipass\" ? \"groove weld; passes are counted separately.\" : \"single-pass butt reference.\"));\n    out.push(\"Restraint \" + mult(r.restF) + \": \" + (r.restraintKey === \"\" ? \"not specified, so a free joint is assumed and the range is wider.\"\n      : r.restraintKey === \"high\" ? \"rigid restraint limits movement but locks in residual stress.\" : \"reduces free movement during welding.\"));\n    if (r.tack != null) out.push(\"Tack spacing \" + mult(r.tackF) + \": \" + r.tackBand.note + \" spacing relative to thickness.\");\n    return out;\n  }\n  function suggestions(r) {\n    var s = [\n      \"Balance welding about the neutral axis: weld both sides alternately, or use a double-V or double-bevel joint where practical.\",\n      \"Keep heat input as low as the WPS allows; avoid oversized welds and excess reinforcement.\",\n      \"Plan the weld sequence (back-step, skip or symmetric welding) to spread the heat out.\",\n      \"Use suitable clamping or strongbacks, and consider pre-setting parts opposite the expected movement.\"\n    ];\n    if (r.tack == null) s.push(\"Specify tack spacing. Evenly spaced tacks help hold fit-up and joint gap.\");\n    else if (r.tackBand.note === \"wide\") s.push(\"Tacks are widely spaced for this thickness; closer tacks can help hold alignment and gap.\");\n    else s.push(\"Keep tack spacing consistent and make sure tacks are sound so they don't crack under shrinkage forces.\");\n    if (r.restraintKey === \"high\") s.push(\"With high restraint, check for residual stress and cracking risk; consider preheat or stress relief where the procedure calls for it.\");\n    if (r.passes > 3) s.push(\"For many passes, alternate sides between passes on double-sided joints and check angular movement as you go, so you can adjust the sequence.\");\n    if (r.matKey === \"ss\" || r.matKey === \"al\") s.push(\"For \" + (r.matKey === \"ss\" ? \"stainless steel\" : \"aluminum\") + \", use copper or aluminum backing bars or chill bars to draw heat away, and allow for larger shrinkage.\");\n    if (r.t < 3) s.push(\"Thin material is prone to buckling, which this estimate does not cover; use stitch welding, chill bars and tight fixturing.\");\n    if (r.mode === \"shrink\") s.push(\"Add a shrinkage allowance to the cut sizes or fit-up gap for critical dimensions, and confirm it on a trial piece.\");\n    return s;\n  }\n  \/* ===================== Wiring ===================== *\/\n  $(\"calcForm\").addEventListener(\"submit\", function (e) { e.preventDefault(); calculate(); });\n  $(\"process\").addEventListener(\"change\", applyProcessDefaults);\n  [\"joint\", \"material\", \"resultUnit\"].forEach(function (id) { $(id).addEventListener(\"change\", updateVisibility); });\n  ROOT.querySelectorAll('input[name=\"wdc-hiMethod\"], input[name=\"wdc-mode\"]').forEach(function (r) { r.addEventListener(\"change\", updateVisibility); });\n  \/\/ Clear a field's error as soon as the user edits it\n  ROOT.querySelectorAll('input[type=\"text\"]').forEach(function (inp) {\n    inp.addEventListener(\"input\", function () {\n      if (inp.getAttribute(\"aria-invalid\") === \"true\") {\n        inp.removeAttribute(\"aria-invalid\");\n        var m = document.getElementById(inp.id + \"-msg\"); if (m) { m.textContent = \"\"; m.className = \"msg\"; }\n      }\n    });\n  });\n  initUnitConversion();\n  applyProcessDefaults();\n  updateVisibility();\n})();\n<\/script>\n<!-- Welding Distortion Calculator: END -->\n","protected":false},"excerpt":{"rendered":"<p>The Welding Distortion Calculator provides a preliminary estimate of angular distortion or transverse shrinkage based on welding process, material, plate thickness, weld type, and heat&#8230;<\/p>","protected":false},"author":2,"featured_media":25111,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"pmpro_default_level":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_hide_featured_image":false,"_jetpack_featured_image_duplicate_dismissed":0,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[8808],"tags":[8851,4198,8859,8793],"class_list":["post-25110","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-manufacturing-and-processes","tag-heat-input","tag-metal-fabrication","tag-weld-distortion","tag-welding-calculator","pmpro-has-access"],"acf":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/www.mechstream.com\/wp-content\/uploads\/2026\/11\/Welding-Distortion-Calculator.png","_links":{"self":[{"href":"https:\/\/www.mechstream.com\/es\/wp-json\/wp\/v2\/posts\/25110","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mechstream.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mechstream.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mechstream.com\/es\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mechstream.com\/es\/wp-json\/wp\/v2\/comments?post=25110"}],"version-history":[{"count":1,"href":"https:\/\/www.mechstream.com\/es\/wp-json\/wp\/v2\/posts\/25110\/revisions"}],"predecessor-version":[{"id":25113,"href":"https:\/\/www.mechstream.com\/es\/wp-json\/wp\/v2\/posts\/25110\/revisions\/25113"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mechstream.com\/es\/wp-json\/wp\/v2\/media\/25111"}],"wp:attachment":[{"href":"https:\/\/www.mechstream.com\/es\/wp-json\/wp\/v2\/media?parent=25110"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mechstream.com\/es\/wp-json\/wp\/v2\/categories?post=25110"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mechstream.com\/es\/wp-json\/wp\/v2\/tags?post=25110"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}