{"id":24933,"date":"2026-10-10T00:33:38","date_gmt":"2026-10-10T00:33:38","guid":{"rendered":"https:\/\/www.mechstream.com\/?p=24933"},"modified":"2026-10-10T00:33:41","modified_gmt":"2026-10-10T00:33:41","slug":"thermal-distortion-risk-assessment-calculator","status":"publish","type":"post","link":"https:\/\/www.mechstream.com\/fr\/thermal-distortion-risk-assessment-calculator\/","title":{"rendered":"Thermal Distortion Risk Assessment Calculator"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The Thermal Distortion Risk Assessment Calculator helps engineers, manufacturers, and heat treatment professionals evaluate potential thermal deformation risks caused by temperature changes in metal components. By considering material properties, thermal expansion coefficients, temperature differences, component dimensions, and mechanical constraints, this tool estimates thermal expansion, thermal strain, and thermal stress. It supports heat treatment planning, welding process optimization, and dimensional stability assessment, helping users identify potential distortion risks and improve manufacturing quality. Actual deformation may vary depending on material behavior, temperature distribution, and operating conditions.<\/p>\n\n\n\n<div id=\"tdr-calculator\" class=\"tdr-root\" data-tdr-widget=\"thermal-distortion-risk\">\n<style>\n#tdr-calculator,#tdr-calculator *,#tdr-calculator *::before,#tdr-calculator *::after{box-sizing:border-box}\n#tdr-calculator{\n  --tdr-bg:#f5f5f7;--tdr-card:#ffffff;--tdr-text:#1d1d1f;--tdr-muted:#6e6e73;--tdr-soft:#86868b;\n  --tdr-line:#d2d2d7;--tdr-line-soft:#e8e8ed;--tdr-accent:#0071e3;--tdr-accent-dark:#0058b0;--tdr-accent-soft:#e8f2fd;\n  --tdr-err:#c4291c;--tdr-err-soft:#fdecea;--tdr-warn:#8a5a00;--tdr-warn-soft:#fff6e0;\n  --tdr-low:#0a7c42;--tdr-low-dot:#0ca30c;--tdr-low-soft:#e7f5ec;\n  --tdr-mod:#a84f00;--tdr-mod-dot:#f08a24;--tdr-mod-soft:#fff1e3;\n  --tdr-high:#b3261e;--tdr-high-dot:#d03b3b;--tdr-high-soft:#fdecea;\n  --tdr-nd:#48484a;--tdr-nd-dot:#8e8e93;--tdr-nd-soft:#f0f0f3;\n  display:block;width:100%;max-width:1040px;margin:24px auto;padding:32px;background:var(--tdr-bg);color:var(--tdr-text);\n  border-radius:22px;font-family:-apple-system,BlinkMacSystemFont,\"SF Pro Text\",\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;\n  font-size:16px;line-height:1.5;font-weight:400;letter-spacing:normal;text-transform:none;text-indent:0;word-spacing:normal;-webkit-font-smoothing:antialiased;text-align:left;\n  position:relative\n}\n#tdr-calculator .tdr-wrap{container-type:inline-size;container-name:tdr;width:100%}\n#tdr-calculator h2,#tdr-calculator h3,#tdr-calculator h4,#tdr-calculator p,#tdr-calculator ul,#tdr-calculator ol,#tdr-calculator li,#tdr-calculator dl,#tdr-calculator dt,#tdr-calculator dd,#tdr-calculator figure,#tdr-calculator table,#tdr-calculator details,#tdr-calculator summary,#tdr-calculator fieldset,#tdr-calculator legend,#tdr-calculator label{\n  margin:0;padding:0;border:0;background:none;color:inherit;font-family:inherit;font-size:inherit;font-weight:inherit;font-style:normal;line-height:inherit;text-indent:0;text-transform:none;letter-spacing:normal;text-shadow:none;box-shadow:none;float:none;width:auto;max-width:none\n}\n#tdr-calculator ul,#tdr-calculator ol{list-style:none}\n#tdr-calculator a{color:var(--tdr-accent);text-decoration:underline}\n#tdr-calculator .tdr-card{background:var(--tdr-card);border:1px solid var(--tdr-line-soft);border-radius:18px;padding:22px;box-shadow:0 1px 2px rgba(0,0,0,.04),0 4px 14px rgba(0,0,0,.03);min-width:0}\n#tdr-calculator .tdr-card+.tdr-card{margin-top:16px}\n#tdr-calculator .tdr-card-title{margin:0 0 4px;color:var(--tdr-text);font-size:17.3px;font-weight:600;line-height:1.25;display:flex;align-items:center;gap:10px}\n#tdr-calculator .tdr-step{display:inline-flex;align-items:center;justify-content:center;flex:0 0 24px;width:24px;height:24px;border-radius:50%;background:var(--tdr-accent-soft);color:var(--tdr-accent);font-size:12.8px;font-weight:700}\n#tdr-calculator .tdr-card-note{color:var(--tdr-muted);font-size:14.1px;margin:0 0 16px}\n#tdr-calculator .tdr-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:16px 18px}\n#tdr-calculator .tdr-field{min-width:0}\n#tdr-calculator .tdr-span{grid-column:1\/-1}\n#tdr-calculator .tdr-label{display:block;font-size:14.1px;font-weight:600;color:var(--tdr-text);margin:0 0 6px;line-height:1.3}\n#tdr-calculator .tdr-opt{color:var(--tdr-soft);font-weight:400;font-size:12.8px}\n#tdr-calculator .tdr-label .tdr-badge,#tdr-calculator .tdr-label .tdr-opt{margin-left:4px}\n#tdr-calculator sub,#tdr-calculator sup{font-size:.72em;line-height:0;position:relative;vertical-align:baseline}\n#tdr-calculator sub{bottom:-.25em}\n#tdr-calculator sup{top:-.5em}\n#tdr-calculator .tdr-hint{display:block;color:var(--tdr-soft);font-size:12.6px;margin-top:5px;line-height:1.4}\n#tdr-calculator .tdr-hint:empty{display:none}\n#tdr-calculator .tdr-error{display:block;color:var(--tdr-err);font-size:13px;margin-top:4px;line-height:1.4}\n#tdr-calculator .tdr-error:empty{display:none}\n#tdr-calculator .tdr-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:stretch}\n#tdr-calculator .tdr-unit{display:inline-flex;align-items:center;justify-content:center;min-width:64px;min-height:46px;padding:0 12px;border-radius:12px;background:#f2f2f5;color:var(--tdr-muted);font-size:14.1px;font-weight:600;white-space:nowrap}\n#tdr-calculator input[type=number],#tdr-calculator select{\n  -webkit-appearance:none;appearance:none;display:block;width:100%;min-height:46px;height:auto;margin:0;padding:10px 14px;font:inherit;font-size:16px;line-height:1.3;\n  color:var(--tdr-text);background-color:#fff;border:1px solid var(--tdr-line);border-radius:12px;box-shadow:none;outline:none;max-width:none;\n  transition:border-color .15s ease,box-shadow .15s ease\n}\n#tdr-calculator input[type=number]::-webkit-inner-spin-button,#tdr-calculator input[type=number]::-webkit-outer-spin-button{opacity:.6}\n#tdr-calculator select{padding-right:36px;background-image:linear-gradient(45deg,transparent 50%,#6e6e73 50%),linear-gradient(135deg,#6e6e73 50%,transparent 50%);background-position:calc(100% - 19px) 20px,calc(100% - 14px) 20px;background-size:5px 5px,5px 5px;background-repeat:no-repeat;cursor:pointer}\n#tdr-calculator .tdr-row select{width:auto;min-width:118px}\n#tdr-calculator input[type=number]:focus,#tdr-calculator select:focus{border-color:var(--tdr-accent);box-shadow:0 0 0 4px rgba(0,113,227,.18)}\n#tdr-calculator input[type=number]::placeholder{color:#aeaeb2;opacity:1}\n#tdr-calculator .tdr-invalid{border-color:var(--tdr-err)!important;box-shadow:0 0 0 3px rgba(196,41,28,.12)!important}\n#tdr-calculator .tdr-seg{display:grid;grid-auto-columns:minmax(0,1fr);grid-auto-flow:column;min-width:0;width:100%;gap:4px;padding:4px;background:#ececf0;border-radius:13px;border:0;margin:0;position:relative}\n#tdr-calculator .tdr-seg legend{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}\n#tdr-calculator .tdr-seg input{position:absolute;opacity:0;width:1px;height:1px;margin:0;pointer-events:none}\n#tdr-calculator .tdr-seg label{display:flex;align-items:center;justify-content:center;min-height:40px;padding:6px 6px;border-radius:10px;font-size:14.1px;font-weight:600;color:var(--tdr-muted);cursor:pointer;text-align:center;line-height:1.2;transition:background .15s,color .15s,box-shadow .15s;margin:0}\n#tdr-calculator .tdr-seg input:checked+label{background:#fff;color:var(--tdr-text);box-shadow:0 1px 3px rgba(0,0,0,.12)}\n#tdr-calculator .tdr-seg input:focus-visible+label{outline:3px solid rgba(0,113,227,.4);outline-offset:1px}\n#tdr-calculator .tdr-seg-label{display:block;font-size:14.1px;font-weight:600;margin:0 0 6px}\n#tdr-calculator .tdr-info{padding:13px 15px;border-radius:12px;background:#f9f9fb;border:1px solid var(--tdr-line-soft);font-size:13.4px;color:var(--tdr-muted);line-height:1.5}\n#tdr-calculator .tdr-info strong{color:var(--tdr-text);font-weight:600}\n#tdr-calculator .tdr-info p+p{margin-top:5px}\n#tdr-calculator .tdr-info-warn{background:var(--tdr-warn-soft);border-color:#f3dfa8;color:#5c4300}\n#tdr-calculator .tdr-info-warn strong{color:#4a3600}\n#tdr-calculator .tdr-badge{display:inline-block;padding:1px 8px;border-radius:999px;font-size:10.9px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;line-height:1.6;vertical-align:middle}\n#tdr-calculator .tdr-badge-edit{background:var(--tdr-accent-soft);color:var(--tdr-accent-dark)}\n#tdr-calculator .tdr-badge[hidden],#tdr-calculator [hidden]{display:none!important}\n#tdr-calculator .tdr-check{display:flex;gap:10px;align-items:flex-start;margin-top:10px;font-size:13.6px;color:#4a3600;cursor:pointer;line-height:1.45}\n#tdr-calculator .tdr-check input{flex:0 0 18px;width:18px;height:18px;margin:2px 0 0;accent-color:var(--tdr-accent);cursor:pointer}\n#tdr-calculator .tdr-inline-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}\n#tdr-calculator .tdr-actions{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:18px}\n#tdr-calculator .tdr-btn{-webkit-appearance:none;appearance:none;display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:46px;min-width:140px;margin:0;padding:10px 22px;border-radius:999px;border:1px solid transparent;font:inherit;font-size:15.4px;font-weight:600;line-height:1.2;cursor:pointer;text-decoration:none;text-transform:none;letter-spacing:normal;box-shadow:none;width:auto;height:auto;transition:background .15s,border-color .15s,color .15s}\n#tdr-calculator .tdr-btn-primary{background:var(--tdr-accent);color:#fff}\n#tdr-calculator .tdr-btn-primary:hover{background:var(--tdr-accent-dark);color:#fff}\n#tdr-calculator .tdr-btn-ghost{background:#fff;color:var(--tdr-accent);border-color:var(--tdr-line)}\n#tdr-calculator .tdr-btn-ghost:hover{border-color:var(--tdr-accent);background:#fff;color:var(--tdr-accent-dark)}\n#tdr-calculator .tdr-btn-small{min-height:36px;min-width:0;padding:6px 15px;font-size:13.6px}\n#tdr-calculator .tdr-btn:focus-visible{outline:3px solid rgba(0,113,227,.4);outline-offset:2px}\n#tdr-calculator .tdr-form-msg{text-align:center;color:var(--tdr-err);font-size:13.8px;margin-top:10px;min-height:1.3em}\n#tdr-calculator .tdr-crit{margin-top:16px}\n#tdr-calculator .tdr-crit summary{cursor:pointer;list-style:none;display:flex;align-items:center;justify-content:space-between;gap:10px;font-size:16.3px;font-weight:600;color:var(--tdr-text)}\n#tdr-calculator .tdr-crit summary::-webkit-details-marker{display:none}\n#tdr-calculator .tdr-crit summary::after{content:\"+\";font-size:20.8px;font-weight:400;color:var(--tdr-accent);line-height:1}\n#tdr-calculator .tdr-crit[open] summary::after{content:\"\\2212\"}\n#tdr-calculator .tdr-crit summary:focus-visible{outline:3px solid rgba(0,113,227,.4);outline-offset:4px;border-radius:6px}\n#tdr-calculator .tdr-crit-body{margin-top:12px}\n#tdr-calculator .tdr-crit-row{padding:14px 0;border-top:1px solid var(--tdr-line-soft)}\n#tdr-calculator .tdr-crit-row:first-of-type{border-top:0;padding-top:4px}\n#tdr-calculator .tdr-crit-name{font-weight:600;font-size:14.4px;margin:0 0 4px}\n#tdr-calculator .tdr-crit-basis{color:var(--tdr-muted);font-size:12.8px;margin:0 0 10px;line-height:1.45}\n#tdr-calculator .tdr-crit-inputs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}\n#tdr-calculator .tdr-crit-inputs .tdr-label{font-weight:500;font-size:12.8px;color:var(--tdr-muted)}\n#tdr-calculator .tdr-results-head{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px;margin-bottom:14px}\n#tdr-calculator .tdr-stale{margin:0 0 12px;padding:10px 13px;border-radius:10px;background:var(--tdr-warn-soft);color:#5c4300;font-size:13.3px;border:1px solid #f3dfa8}\n#tdr-calculator .tdr-is-stale .tdr-dim{opacity:.45;transition:opacity .2s}\n#tdr-calculator .tdr-risk{display:flex;gap:14px;align-items:flex-start;padding:16px 18px;border-radius:14px;border:1px solid transparent}\n#tdr-calculator .tdr-risk-icon{flex:0 0 34px;width:34px;height:34px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:800;font-size:16.8px;line-height:1}\n#tdr-calculator .tdr-risk-kicker{font-size:11.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;opacity:.85}\n#tdr-calculator .tdr-risk-level{font-size:21.1px;font-weight:700;line-height:1.2;letter-spacing:-.01em}\n#tdr-calculator .tdr-risk-prov{font-size:13.4px;font-weight:600;margin-top:3px}\n#tdr-calculator .tdr-risk-reasons{margin-top:6px;font-size:13.4px;line-height:1.45}\n#tdr-calculator .tdr-risk-reasons li{position:relative;padding-left:14px;margin:3px 0}\n#tdr-calculator .tdr-risk-reasons li::before{content:\"\";position:absolute;left:2px;top:.6em;width:5px;height:5px;border-radius:50%;background:currentColor;opacity:.6}\n#tdr-calculator .tdr-risk-low{background:var(--tdr-low-soft);border-color:#c5e6d1;color:var(--tdr-low)}\n#tdr-calculator .tdr-risk-low .tdr-risk-icon{background:var(--tdr-low-dot)}\n#tdr-calculator .tdr-risk-moderate{background:var(--tdr-mod-soft);border-color:#f7d3ad;color:var(--tdr-mod)}\n#tdr-calculator .tdr-risk-moderate .tdr-risk-icon{background:var(--tdr-mod-dot)}\n#tdr-calculator .tdr-risk-high{background:var(--tdr-high-soft);border-color:#f5c6c1;color:var(--tdr-high)}\n#tdr-calculator .tdr-risk-high .tdr-risk-icon{background:var(--tdr-high-dot)}\n#tdr-calculator .tdr-risk-nd{background:var(--tdr-nd-soft);border-color:#dcdce1;color:var(--tdr-nd)}\n#tdr-calculator .tdr-risk-nd .tdr-risk-icon{background:var(--tdr-nd-dot)}\n#tdr-calculator .tdr-kpis{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-top:14px}\n#tdr-calculator .tdr-kpi{background:#f9f9fb;border:1px solid var(--tdr-line-soft);border-radius:13px;padding:12px 14px;min-width:0}\n#tdr-calculator .tdr-kpi-label{font-size:12.8px;font-weight:600;color:var(--tdr-muted);line-height:1.3}\n#tdr-calculator .tdr-kpi-value{font-size:clamp(17.9px,2.2vw,22.7px);font-weight:700;letter-spacing:-.01em;margin-top:3px;line-height:1.25;overflow-wrap:anywhere}\n#tdr-calculator .tdr-kpi-value.tdr-na{color:var(--tdr-soft);font-size:16.3px}\n#tdr-calculator .tdr-kpi-sub{font-size:12.5px;color:var(--tdr-muted);margin-top:2px;line-height:1.35;overflow-wrap:anywhere}\n#tdr-calculator th{background:#f9f9fb;color:var(--tdr-muted);font-size:12.5px;font-weight:600;white-space:nowrap}\n#tdr-calculator .tdr-toast{position:fixed;left:50%;bottom:24px;transform:translateX(-50%) translateY(20px);background:#1d1d1f;color:#fff;padding:10px 18px;border-radius:999px;font-size:14.1px;opacity:0;pointer-events:none;transition:opacity .2s,transform .2s;z-index:99999;white-space:nowrap}\n#tdr-calculator .tdr-toast.tdr-show{opacity:1;transform:translateX(-50%) translateY(0)}\n#tdr-calculator .tdr-sr{position:absolute!important;width:1px!important;height:1px!important;overflow:hidden!important;clip:rect(0 0 0 0)!important;white-space:nowrap!important;margin:-1px!important;padding:0!important;border:0!important}\n@container tdr (min-width:480px){\n  #tdr-calculator .tdr-grid{grid-template-columns:repeat(2,minmax(0,1fr))}\n}\n@container tdr (min-width:760px){\n  #tdr-calculator .tdr-kpis{grid-template-columns:repeat(4,minmax(0,1fr))}\n}\n@media (max-width:700px){\n  #tdr-calculator{padding:18px 14px;border-radius:16px;margin:12px auto}\n  #tdr-calculator .tdr-card{padding:18px 15px;border-radius:16px}\n  #tdr-calculator .tdr-actions .tdr-btn{flex:1 1 auto}\n  #tdr-calculator .tdr-seg label{font-size:13.1px;padding:6px 3px}\n}\n@media (max-width:380px){\n  #tdr-calculator .tdr-kpis{grid-template-columns:minmax(0,1fr)}\n  #tdr-calculator .tdr-row select{min-width:96px}\n}\n@media (prefers-reduced-motion:reduce){#tdr-calculator *{transition:none!important}}\n@media print{#tdr-calculator .tdr-actions,#tdr-calculator .tdr-toast{display:none!important}}\n<\/style>\n<div class=\"tdr-wrap\">\n<form class=\"tdr-form\" id=\"tdr-form\" novalidate onsubmit=\"return false;\">\n<section class=\"tdr-card\" aria-labelledby=\"tdr-h-mat\">\n<h3 class=\"tdr-card-title\" id=\"tdr-h-mat\"><span class=\"tdr-step\" aria-hidden=\"true\">1<\/span>Material &amp; properties<\/h3>\n<p class=\"tdr-card-note\">Defaults are representative room-temperature reference values. Replace any value with data for your alloy, temper and temperature range.<\/p>\n<div class=\"tdr-grid\">\n<div class=\"tdr-field tdr-span\">\n<label class=\"tdr-label\" for=\"tdr-material\">Material<\/label>\n<select id=\"tdr-material\" name=\"material\">\n<option value=\"carbon\" selected>Carbon Steel<\/option>\n<option value=\"ss304\">Stainless Steel 304<\/option>\n<option value=\"ss316\">Stainless Steel 316<\/option>\n<option value=\"al6061\">Aluminum Alloy 6061<\/option>\n<option value=\"copper\">Copper<\/option>\n<option value=\"brass\">Brass<\/option>\n<option value=\"ti64\">Titanium Alloy Ti-6Al-4V<\/option>\n<option value=\"castiron\">Cast Iron<\/option>\n<option value=\"custom\">Custom Material<\/option>\n<\/select>\n<\/div>\n<div class=\"tdr-info tdr-span\" id=\"tdr-mat-info\" aria-live=\"polite\"><\/div>\n<div class=\"tdr-field\">\n<label class=\"tdr-label\" for=\"tdr-alpha\">Expansion coefficient &#945; <span class=\"tdr-badge tdr-badge-edit\" id=\"tdr-alpha-edit\" hidden>Edited<\/span><\/label>\n<div class=\"tdr-row\">\n<input id=\"tdr-alpha\" name=\"alpha\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-alpha-hint tdr-alpha-err\">\n<select id=\"tdr-alpha-unit\" name=\"alphaUnit\" aria-label=\"Expansion coefficient unit\">\n<option value=\"K\" selected>&#215;10&#8315;&#8310;\/&#176;C<\/option>\n<option value=\"F\">&#215;10&#8315;&#8310;\/&#176;F<\/option>\n<\/select>\n<\/div>\n<span class=\"tdr-hint\" id=\"tdr-alpha-hint\">Mean coefficient between the initial and final temperature. 1\/&#176;C equals 1\/K.<\/span>\n<span class=\"tdr-error\" id=\"tdr-alpha-err\" role=\"alert\"><\/span>\n<\/div>\n<div class=\"tdr-field\">\n<label class=\"tdr-label\" for=\"tdr-E\">Young&#8217;s modulus E <span class=\"tdr-badge tdr-badge-edit\" id=\"tdr-E-edit\" hidden>Edited<\/span><\/label>\n<div class=\"tdr-row\">\n<input id=\"tdr-E\" name=\"E\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-E-err\">\n<span class=\"tdr-unit\" aria-hidden=\"true\">GPa<\/span>\n<\/div>\n<span class=\"tdr-error\" id=\"tdr-E-err\" role=\"alert\"><\/span>\n<\/div>\n<div class=\"tdr-field\">\n<label class=\"tdr-label\" for=\"tdr-nu\">Poisson&#8217;s ratio &#957; <span class=\"tdr-badge tdr-badge-edit\" id=\"tdr-nu-edit\" hidden>Edited<\/span><\/label>\n<input id=\"tdr-nu\" name=\"nu\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-nu-hint tdr-nu-err\">\n<span class=\"tdr-hint\" id=\"tdr-nu-hint\">Used for biaxial plate restraint, lateral strain and the thermal stress resistance index.<\/span>\n<span class=\"tdr-error\" id=\"tdr-nu-err\" role=\"alert\"><\/span>\n<\/div>\n<div class=\"tdr-field\">\n<label class=\"tdr-label\" for=\"tdr-sy\">Yield strength &#963;<sub>y<\/sub> <span class=\"tdr-badge tdr-badge-edit\" id=\"tdr-sy-edit\" hidden>Edited<\/span><\/label>\n<div class=\"tdr-row\">\n<input id=\"tdr-sy\" name=\"sy\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-sy-hint tdr-sy-err\">\n<span class=\"tdr-unit\" aria-hidden=\"true\">MPa<\/span>\n<\/div>\n<span class=\"tdr-hint\" id=\"tdr-sy-hint\">Use the yield strength at the highest temperature in the cycle (conservative).<\/span>\n<span class=\"tdr-error\" id=\"tdr-sy-err\" role=\"alert\"><\/span>\n<\/div>\n<div class=\"tdr-field\">\n<label class=\"tdr-label\" for=\"tdr-k\">Thermal conductivity k <span class=\"tdr-opt\">(optional)<\/span> <span class=\"tdr-badge tdr-badge-edit\" id=\"tdr-k-edit\" hidden>Edited<\/span><\/label>\n<div class=\"tdr-row\">\n<input id=\"tdr-k\" name=\"k\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-k-err\">\n<span class=\"tdr-unit\" aria-hidden=\"true\">W\/m&#183;K<\/span>\n<\/div>\n<span class=\"tdr-error\" id=\"tdr-k-err\" role=\"alert\"><\/span>\n<\/div>\n<div class=\"tdr-field\">\n<label class=\"tdr-label\" for=\"tdr-scope\">Elastic screening limit <span class=\"tdr-badge tdr-badge-edit\" id=\"tdr-scope-edit\" hidden>Edited<\/span><\/label>\n<div class=\"tdr-row\">\n<input id=\"tdr-scope\" name=\"scope\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-scope-hint tdr-scope-err\">\n<span class=\"tdr-unit tdr-tunit-label\" aria-hidden=\"true\">&#176;C<\/span>\n<\/div>\n<span class=\"tdr-hint\" id=\"tdr-scope-hint\">Highest temperature at which time-independent elastic screening is meaningful.<\/span>\n<span class=\"tdr-error\" id=\"tdr-scope-err\" role=\"alert\"><\/span>\n<\/div>\n<\/div>\n<div class=\"tdr-inline-actions\"><button type=\"button\" class=\"tdr-btn tdr-btn-ghost tdr-btn-small\" id=\"tdr-restore-mat\">Restore material defaults<\/button><\/div>\n<\/section>\n<section class=\"tdr-card\" aria-labelledby=\"tdr-h-geo\">\n<h3 class=\"tdr-card-title\" id=\"tdr-h-geo\"><span class=\"tdr-step\" aria-hidden=\"true\">2<\/span>Geometry<\/h3>\n<p class=\"tdr-card-note\">Overall dimensions of the component or of the section being assessed.<\/p>\n<span class=\"tdr-seg-label\" aria-hidden=\"true\">Component geometry<\/span>\n<fieldset class=\"tdr-seg\">\n<legend>Component geometry<\/legend>\n<input type=\"radio\" id=\"tdr-g-plate\" name=\"tdr-geom\" value=\"plate\" checked><label for=\"tdr-g-plate\">Plate<\/label>\n<input type=\"radio\" id=\"tdr-g-bar\" name=\"tdr-geom\" value=\"bar\"><label for=\"tdr-g-bar\">Bar<\/label>\n<input type=\"radio\" id=\"tdr-g-block\" name=\"tdr-geom\" value=\"block\"><label for=\"tdr-g-block\">Block<\/label>\n<input type=\"radio\" id=\"tdr-g-custom\" name=\"tdr-geom\" value=\"custom\"><label for=\"tdr-g-custom\">Custom<\/label>\n<\/fieldset>\n<span class=\"tdr-hint\" id=\"tdr-geom-hint\"><\/span>\n<div class=\"tdr-grid\" style=\"margin-top:16px\">\n<div class=\"tdr-field\">\n<label class=\"tdr-label\" for=\"tdr-L\">Length L<\/label>\n<div class=\"tdr-row\"><input id=\"tdr-L\" name=\"L\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-L-err\"><span class=\"tdr-unit\" aria-hidden=\"true\">mm<\/span><\/div>\n<span class=\"tdr-error\" id=\"tdr-L-err\" role=\"alert\"><\/span>\n<\/div>\n<div class=\"tdr-field\">\n<label class=\"tdr-label\" for=\"tdr-W\">Width W<\/label>\n<div class=\"tdr-row\"><input id=\"tdr-W\" name=\"W\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-W-err\"><span class=\"tdr-unit\" aria-hidden=\"true\">mm<\/span><\/div>\n<span class=\"tdr-error\" id=\"tdr-W-err\" role=\"alert\"><\/span>\n<\/div>\n<div class=\"tdr-field\">\n<label class=\"tdr-label\" for=\"tdr-t\">Thickness t<\/label>\n<div class=\"tdr-row\"><input id=\"tdr-t\" name=\"t\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-t-err\"><span class=\"tdr-unit\" aria-hidden=\"true\">mm<\/span><\/div>\n<span class=\"tdr-error\" id=\"tdr-t-err\" role=\"alert\"><\/span>\n<\/div>\n<\/div>\n<span class=\"tdr-hint\" id=\"tdr-geom-warn\"><\/span>\n<\/section>\n<section class=\"tdr-card\" aria-labelledby=\"tdr-h-temp\">\n<h3 class=\"tdr-card-title\" id=\"tdr-h-temp\"><span class=\"tdr-step\" aria-hidden=\"true\">3<\/span>Temperature<\/h3>\n<p class=\"tdr-card-note\">Enter bulk (mean) temperatures. The model assumes the part is stress-free at the initial temperature.<\/p>\n<span class=\"tdr-seg-label\" aria-hidden=\"true\">Temperature unit<\/span>\n<fieldset class=\"tdr-seg\">\n<legend>Temperature unit<\/legend>\n<input type=\"radio\" id=\"tdr-u-c\" name=\"tdr-tunit\" value=\"C\" checked><label for=\"tdr-u-c\">&#176;C<\/label>\n<input type=\"radio\" id=\"tdr-u-f\" name=\"tdr-tunit\" value=\"F\"><label for=\"tdr-u-f\">&#176;F<\/label>\n<\/fieldset>\n<div class=\"tdr-grid\" style=\"margin-top:16px\">\n<div class=\"tdr-field\">\n<label class=\"tdr-label\" for=\"tdr-Ti\">Initial temperature<\/label>\n<div class=\"tdr-row\"><input id=\"tdr-Ti\" name=\"Ti\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-Ti-err\"><span class=\"tdr-unit tdr-tunit-label\" aria-hidden=\"true\">&#176;C<\/span><\/div>\n<span class=\"tdr-error\" id=\"tdr-Ti-err\" role=\"alert\"><\/span>\n<\/div>\n<div class=\"tdr-field\">\n<label class=\"tdr-label\" for=\"tdr-Tf\">Final temperature<\/label>\n<div class=\"tdr-row\"><input id=\"tdr-Tf\" name=\"Tf\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-Tf-err\"><span class=\"tdr-unit tdr-tunit-label\" aria-hidden=\"true\">&#176;C<\/span><\/div>\n<span class=\"tdr-error\" id=\"tdr-Tf-err\" role=\"alert\"><\/span>\n<\/div>\n<\/div>\n<span class=\"tdr-seg-label\" aria-hidden=\"true\" style=\"margin-top:16px\">Temperature distribution<\/span>\n<fieldset class=\"tdr-seg\">\n<legend>Temperature distribution<\/legend>\n<input type=\"radio\" id=\"tdr-d-uni\" name=\"tdr-dist\" value=\"uniform\" checked><label for=\"tdr-d-uni\">Uniform<\/label>\n<input type=\"radio\" id=\"tdr-d-non\" name=\"tdr-dist\" value=\"nonuniform\"><label for=\"tdr-d-non\">Nonuniform<\/label>\n<\/fieldset>\n<div class=\"tdr-field\" id=\"tdr-grad-field\" style=\"margin-top:16px\" hidden>\n<label class=\"tdr-label\" for=\"tdr-dTg\">Temperature difference across thickness &#916;T<sub>gradient<\/sub><\/label>\n<div class=\"tdr-row\"><input id=\"tdr-dTg\" name=\"dTg\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-dTg-hint tdr-dTg-err\"><span class=\"tdr-unit tdr-tdiff-label\" aria-hidden=\"true\">&#176;C<\/span><\/div>\n<span class=\"tdr-hint\" id=\"tdr-dTg-hint\">Largest difference between the hottest and coldest points through the thickness at the critical moment (for example surface vs. core during quenching).<\/span>\n<span class=\"tdr-error\" id=\"tdr-dTg-err\" role=\"alert\"><\/span>\n<\/div>\n<div class=\"tdr-info tdr-info-warn\" id=\"tdr-range-box\" style=\"margin-top:16px\" hidden>\n<p id=\"tdr-range-text\"><\/p>\n<label class=\"tdr-check\" for=\"tdr-confirm\"><input type=\"checkbox\" id=\"tdr-confirm\" name=\"confirm\"><span id=\"tdr-confirm-text\">I have entered &#945;, E and yield strength values that apply to this temperature range.<\/span><\/label>\n<\/div>\n<\/section>\n<section class=\"tdr-card\" aria-labelledby=\"tdr-h-bc\">\n<h3 class=\"tdr-card-title\" id=\"tdr-h-bc\"><span class=\"tdr-step\" aria-hidden=\"true\">4<\/span>Mechanical boundary conditions<\/h3>\n<p class=\"tdr-card-note\">How strongly the surroundings prevent the part from expanding or contracting.<\/p>\n<span class=\"tdr-seg-label\" aria-hidden=\"true\">Restraint<\/span>\n<fieldset class=\"tdr-seg\">\n<legend>Restraint condition<\/legend>\n<input type=\"radio\" id=\"tdr-r-free\" name=\"tdr-restraint\" value=\"free\"><label for=\"tdr-r-free\">Free<\/label>\n<input type=\"radio\" id=\"tdr-r-full\" name=\"tdr-restraint\" value=\"full\" checked><label for=\"tdr-r-full\">Fully restrained<\/label>\n<input type=\"radio\" id=\"tdr-r-part\" name=\"tdr-restraint\" value=\"partial\"><label for=\"tdr-r-part\">Partially restrained<\/label>\n<\/fieldset>\n<span class=\"tdr-hint\" id=\"tdr-restraint-hint\"><\/span>\n<div class=\"tdr-field\" id=\"tdr-R-field\" style=\"margin-top:16px\" hidden>\n<label class=\"tdr-label\" for=\"tdr-R\">Effective restraint factor R<\/label>\n<input id=\"tdr-R\" name=\"R\" type=\"number\" inputmode=\"decimal\" step=\"any\" min=\"0\" max=\"1\" aria-describedby=\"tdr-R-hint tdr-R-err\">\n<span class=\"tdr-hint\" id=\"tdr-R-hint\">0 = free, 1 = fully restrained. R is a simplified modeling assumption: it scales the fully restrained stress (&#963; = &#8722;R&#183;E&#183;&#945;&#183;&#916;T). Real restraint depends on the stiffness of the surrounding structure, friction, gaps and bolt slip.<\/span>\n<span class=\"tdr-error\" id=\"tdr-R-err\" role=\"alert\"><\/span>\n<\/div>\n<div id=\"tdr-dir-field\" style=\"margin-top:16px\">\n<span class=\"tdr-seg-label\" aria-hidden=\"true\">Restrained directions (plates)<\/span>\n<fieldset class=\"tdr-seg\">\n<legend>Restrained directions<\/legend>\n<input type=\"radio\" id=\"tdr-dir-uni\" name=\"tdr-dir\" value=\"uni\" checked><label for=\"tdr-dir-uni\">Length only<\/label>\n<input type=\"radio\" id=\"tdr-dir-bi\" name=\"tdr-dir\" value=\"bi\"><label for=\"tdr-dir-bi\">Length + width<\/label>\n<\/fieldset>\n<span class=\"tdr-hint\" id=\"tdr-dir-hint\"><\/span>\n<\/div>\n<div class=\"tdr-field\" id=\"tdr-allow-field\" style=\"margin-top:16px\">\n<label class=\"tdr-label\" for=\"tdr-allow\">Available expansion allowance along L <span class=\"tdr-opt\">(optional)<\/span><\/label>\n<div class=\"tdr-row\"><input id=\"tdr-allow\" name=\"allow\" type=\"number\" inputmode=\"decimal\" step=\"any\" placeholder=\"e.g. 0.5\" aria-describedby=\"tdr-allow-hint tdr-allow-err\"><span class=\"tdr-unit\" aria-hidden=\"true\">mm<\/span><\/div>\n<span class=\"tdr-hint\" id=\"tdr-allow-hint\">Clearance or gap that can absorb the length change before contact (fit-up, slot, joint). Leave blank if not relevant.<\/span>\n<span class=\"tdr-error\" id=\"tdr-allow-err\" role=\"alert\"><\/span>\n<\/div>\n<\/section>\n<details class=\"tdr-card tdr-crit\" id=\"tdr-crit\">\n<summary><span><span class=\"tdr-step\" aria-hidden=\"true\" style=\"margin-right:10px\">5<\/span>Screening criteria (configurable)<\/span><\/summary>\n<div class=\"tdr-crit-body\">\n<p class=\"tdr-card-note\">Risk levels are assigned only from the thresholds below. Each default has a stated mechanical basis; replace them with your company standard, tolerance or code limits. Clearing both thresholds of an applicable criterion turns the classification into &#8220;Risk Not Determined&#8221;.<\/p>\n<div class=\"tdr-crit-row\">\n<p class=\"tdr-crit-name\">Restraint stress utilization U = |&#963;| \/ &#963;<sub>y<\/sub><\/p>\n<p class=\"tdr-crit-basis\">Default High &#8805; 1.00: elastic stress reaches yield, so permanent deformation is predicted. Default Moderate &#8805; 0.67: stress exceeds the 2\/3&#183;&#963;<sub>y<\/sub> yield-based design margin used for basic allowable stresses in pressure-equipment codes.<\/p>\n<div class=\"tdr-crit-inputs\">\n<div><label class=\"tdr-label\" for=\"tdr-u-mod\">Moderate if &#8805;<\/label><input id=\"tdr-u-mod\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-u-err\"><\/div>\n<div><label class=\"tdr-label\" for=\"tdr-u-high\">High if &#8805;<\/label><input id=\"tdr-u-high\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-u-err\"><\/div>\n<\/div>\n<span class=\"tdr-error\" id=\"tdr-u-err\" role=\"alert\"><\/span>\n<\/div>\n<div class=\"tdr-crit-row\">\n<p class=\"tdr-crit-name\">Gradient strain ratio &#916;&#949; \/ &#949;<sub>y<\/sub><\/p>\n<p class=\"tdr-crit-basis\">&#949;<sub>y<\/sub> = &#963;<sub>y<\/sub>\/E is the elastic yield strain. If the differential strain were fully restrained, the elastic stress would be (&#916;&#949;\/&#949;<sub>y<\/sub>)&#183;&#963;<sub>y<\/sub>, so the same yield-based limits are used by default. A free plate with a linear gradient bows elastically instead, so this is an upper-bound tendency indicator.<\/p>\n<div class=\"tdr-crit-inputs\">\n<div><label class=\"tdr-label\" for=\"tdr-g-mod\">Moderate if &#8805;<\/label><input id=\"tdr-g-mod\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-g-err\"><\/div>\n<div><label class=\"tdr-label\" for=\"tdr-g-high\">High if &#8805;<\/label><input id=\"tdr-g-high\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-g-err\"><\/div>\n<\/div>\n<span class=\"tdr-error\" id=\"tdr-g-err\" role=\"alert\"><\/span>\n<\/div>\n<div class=\"tdr-crit-row\">\n<p class=\"tdr-crit-name\">Length change vs. allowance |&#916;L| \/ allowance<\/p>\n<p class=\"tdr-crit-basis\">Applies only when an expansion allowance is entered. Default High &#8805; 1.00: the length change uses up the available clearance, so contact and new restraint develop. No default Moderate level exists because the right margin depends on your tolerance; add one if your standard defines it.<\/p>\n<div class=\"tdr-crit-inputs\">\n<div><label class=\"tdr-label\" for=\"tdr-c-mod\">Moderate if &#8805;<\/label><input id=\"tdr-c-mod\" type=\"number\" inputmode=\"decimal\" step=\"any\" placeholder=\"not set\" aria-describedby=\"tdr-c-err\"><\/div>\n<div><label class=\"tdr-label\" for=\"tdr-c-high\">High if &#8805;<\/label><input id=\"tdr-c-high\" type=\"number\" inputmode=\"decimal\" step=\"any\" aria-describedby=\"tdr-c-err\"><\/div>\n<\/div>\n<span class=\"tdr-error\" id=\"tdr-c-err\" role=\"alert\"><\/span>\n<\/div>\n<div class=\"tdr-inline-actions\"><button type=\"button\" class=\"tdr-btn tdr-btn-ghost tdr-btn-small\" id=\"tdr-restore-crit\">Restore default criteria<\/button><\/div>\n<\/div>\n<\/details>\n<div class=\"tdr-actions\">\n<button type=\"button\" class=\"tdr-btn tdr-btn-primary\" id=\"tdr-calc\">Calculate<\/button>\n<\/div>\n<p class=\"tdr-form-msg\" id=\"tdr-form-msg\" role=\"alert\" aria-live=\"polite\"><\/p>\n<section class=\"tdr-card tdr-results\" id=\"tdr-results\" aria-labelledby=\"tdr-h-res\" tabindex=\"-1\" hidden>\n<div class=\"tdr-results-head\">\n<h3 class=\"tdr-card-title\" id=\"tdr-h-res\"><span class=\"tdr-step\" aria-hidden=\"true\">6<\/span>Results<\/h3>\n<\/div>\n<div id=\"tdr-out\">\n<p class=\"tdr-stale\" id=\"tdr-stale\" role=\"status\" hidden>Some inputs are invalid. The results below are from the last valid calculation.<\/p>\n<div class=\"tdr-dim\">\n<div class=\"tdr-risk\" id=\"tdr-risk\" role=\"status\" aria-live=\"polite\">\n<div class=\"tdr-risk-icon\" id=\"tdr-risk-icon\" aria-hidden=\"true\"><\/div>\n<div>\n<div class=\"tdr-risk-kicker\">Preliminary distortion risk<\/div>\n<div class=\"tdr-risk-level\" id=\"tdr-risk-level\"><\/div>\n<div class=\"tdr-risk-prov\" id=\"tdr-risk-prov\" hidden><\/div>\n<ul class=\"tdr-risk-reasons\" id=\"tdr-risk-reasons\"><\/ul>\n<\/div>\n<\/div>\n<div class=\"tdr-kpis\">\n<div class=\"tdr-kpi\"><div class=\"tdr-kpi-label\">Temperature change &#916;T<\/div><div class=\"tdr-kpi-value\" id=\"tdr-k-temp\"><\/div><div class=\"tdr-kpi-sub\" id=\"tdr-k-temp-sub\"><\/div><\/div>\n<div class=\"tdr-kpi\"><div class=\"tdr-kpi-label\" id=\"tdr-k-len-label\">Linear expansion &#916;L<\/div><div class=\"tdr-kpi-value\" id=\"tdr-k-len\"><\/div><div class=\"tdr-kpi-sub\" id=\"tdr-k-len-sub\"><\/div><\/div>\n<div class=\"tdr-kpi\"><div class=\"tdr-kpi-label\">Width change &#916;W<\/div><div class=\"tdr-kpi-value\" id=\"tdr-k-wid\"><\/div><div class=\"tdr-kpi-sub\" id=\"tdr-k-wid-sub\"><\/div><\/div>\n<div class=\"tdr-kpi\"><div class=\"tdr-kpi-label\">Thickness change &#916;t<\/div><div class=\"tdr-kpi-value\" id=\"tdr-k-thk\"><\/div><div class=\"tdr-kpi-sub\" id=\"tdr-k-thk-sub\"><\/div><\/div>\n<div class=\"tdr-kpi\"><div class=\"tdr-kpi-label\">Thermal strain &#949;<\/div><div class=\"tdr-kpi-value\" id=\"tdr-k-eps\"><\/div><div class=\"tdr-kpi-sub\" id=\"tdr-k-eps-sub\"><\/div><\/div>\n<div class=\"tdr-kpi\"><div class=\"tdr-kpi-label\">Estimated thermal stress &#963;<\/div><div class=\"tdr-kpi-value\" id=\"tdr-k-sig\"><\/div><div class=\"tdr-kpi-sub\" id=\"tdr-k-sig-sub\"><\/div><\/div>\n<div class=\"tdr-kpi\"><div class=\"tdr-kpi-label\">Stress utilization U<\/div><div class=\"tdr-kpi-value\" id=\"tdr-k-U\"><\/div><div class=\"tdr-kpi-sub\" id=\"tdr-k-U-sub\"><\/div><\/div>\n<div class=\"tdr-kpi\"><div class=\"tdr-kpi-label\">Differential strain &#916;&#949;<\/div><div class=\"tdr-kpi-value\" id=\"tdr-k-de\"><\/div><div class=\"tdr-kpi-sub\" id=\"tdr-k-de-sub\"><\/div><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/section>\n<\/form>\n<\/div>\n<div class=\"tdr-toast\" id=\"tdr-toast\" role=\"status\" aria-live=\"polite\"><\/div>\n<script>\n(function () {\n  \"use strict\";\n  \/* =====================================================================\n     REFERENCE DATA\n     Representative room-temperature values (see the reference table in\n     the page). Replace them with verified supplier data where available.\n     ===================================================================== *\/\n  var MATERIALS = {\"carbon\":{\"name\":\"Carbon Steel\",\"grade\":\"Low-carbon structural steel (ASTM A36 \/ AISI 1020 class)\",\"alpha\":11.7,\"alphaBasis\":\"mean, 20\\u2013100 \\u00b0C\",\"E\":200,\"nu\":0.29,\"sy\":250,\"syBasis\":\"ASTM A36 specified minimum\",\"k\":51.9,\"scope\":370,\"scopeBasis\":\"Above about 370 \\u00b0C (700 \\u00b0F), allowable stresses for carbon steel in ASME pressure-equipment codes become creep (time-dependent) governed; the A1 phase transformation begins near 727 \\u00b0C.\",\"solidus\":1420,\"lowTempBrittle\":true,\"a1\":727,\"relief\":\"For carbon steel, stress relief is typically carried out below the lower critical temperature, commonly around 550\\u2013650 \\u00b0C, followed by slow cooling.\"},\"ss304\":{\"name\":\"Stainless Steel 304\",\"grade\":\"Austenitic stainless steel, annealed (ASTM A240)\",\"alpha\":17.3,\"alphaBasis\":\"mean, 0\\u2013100 \\u00b0C\",\"E\":193,\"nu\":0.29,\"sy\":205,\"syBasis\":\"ASTM A240 specified minimum, annealed\",\"k\":16.2,\"scope\":540,\"scopeBasis\":\"Above about 540 \\u00b0C (1000 \\u00b0F), creep becomes significant for austenitic stainless steels; exposure at 425\\u2013815 \\u00b0C can also sensitize non-L grades.\",\"solidus\":1400,\"lowTempBrittle\":false,\"a1\":null,\"relief\":\"For austenitic stainless steel, holding at 425\\u2013815 \\u00b0C can cause sensitization in non-L grades; low-temperature stress relief (below about 425 \\u00b0C) or full solution annealing are the usual options.\"},\"ss316\":{\"name\":\"Stainless Steel 316\",\"grade\":\"Austenitic Mo-bearing stainless steel, annealed (ASTM A240)\",\"alpha\":16.0,\"alphaBasis\":\"mean, 0\\u2013100 \\u00b0C\",\"E\":193,\"nu\":0.3,\"sy\":205,\"syBasis\":\"ASTM A240 specified minimum, annealed\",\"k\":16.3,\"scope\":540,\"scopeBasis\":\"Above about 540 \\u00b0C (1000 \\u00b0F), creep becomes significant for austenitic stainless steels; exposure at 425\\u2013815 \\u00b0C can also sensitize non-L grades.\",\"solidus\":1375,\"lowTempBrittle\":false,\"a1\":null,\"relief\":\"For austenitic stainless steel, holding at 425\\u2013815 \\u00b0C can cause sensitization in non-L grades; low-temperature stress relief (below about 425 \\u00b0C) or full solution annealing are the usual options.\"},\"al6061\":{\"name\":\"Aluminum Alloy 6061\",\"grade\":\"6061-T6 wrought aluminum\",\"alpha\":23.6,\"alphaBasis\":\"mean, 20\\u2013100 \\u00b0C\",\"E\":68.9,\"nu\":0.33,\"sy\":240,\"syBasis\":\"6061-T6 specified minimum (typical about 276 MPa)\",\"k\":167,\"scope\":150,\"scopeBasis\":\"Above about 150 \\u00b0C, prolonged exposure over-ages the T6 temper and lowers strength; creep also becomes relevant.\",\"solidus\":582,\"lowTempBrittle\":false,\"a1\":null,\"relief\":\"For 6061-T6, thermal stress relief would over-age the T6 temper; mechanically stress-relieved tempers (T651, stretched) or supplier-specified stabilizing treatments are preferred.\"},\"copper\":{\"name\":\"Copper\",\"grade\":\"C11000 electrolytic tough-pitch copper, annealed\",\"alpha\":17.0,\"alphaBasis\":\"mean, 20\\u2013100 \\u00b0C\",\"E\":115,\"nu\":0.34,\"sy\":69,\"syBasis\":\"typical, annealed (cold-worked tempers are much stronger)\",\"k\":388,\"scope\":200,\"scopeBasis\":\"Above about 200 \\u00b0C, creep and stress relaxation become significant for copper, and cold-worked tempers soften.\",\"solidus\":1065,\"lowTempBrittle\":false,\"a1\":null,\"relief\":\"For copper, a low-temperature stress-relief treatment reduces residual stress; annealed copper has a low yield strength, so restrained thermal cycles readily cause permanent set.\"},\"brass\":{\"name\":\"Brass\",\"grade\":\"C26000 cartridge brass (70\/30), annealed\",\"alpha\":19.9,\"alphaBasis\":\"mean, 20\\u2013300 \\u00b0C\",\"E\":110,\"nu\":0.35,\"sy\":105,\"syBasis\":\"typical, annealed (cold-worked tempers are much stronger)\",\"k\":120,\"scope\":200,\"scopeBasis\":\"Above about 200 \\u00b0C, creep and stress relaxation increase; stress-relief temperatures for brass begin around 250 \\u00b0C.\",\"solidus\":915,\"lowTempBrittle\":false,\"a1\":null,\"relief\":\"For brass, a low-temperature stress-relief anneal (commonly about 250\\u2013300 \\u00b0C) reduces residual stress and susceptibility to stress-corrosion (season) cracking.\"},\"ti64\":{\"name\":\"Titanium Alloy Ti-6Al-4V\",\"grade\":\"Ti-6Al-4V (Grade 5), annealed\",\"alpha\":8.6,\"alphaBasis\":\"mean, 20\\u2013100 \\u00b0C\",\"E\":113.8,\"nu\":0.34,\"sy\":828,\"syBasis\":\"ASTM B265 Grade 5 specified minimum\",\"k\":6.7,\"scope\":400,\"scopeBasis\":\"Above about 400 \\u00b0C, the commonly cited long-term service limit, creep and oxidation become relevant for Ti-6Al-4V.\",\"solidus\":1604,\"lowTempBrittle\":false,\"a1\":null,\"relief\":\"For Ti-6Al-4V, stress relief is commonly carried out around 480\\u2013650 \\u00b0C in vacuum or inert gas to limit oxidation and alpha-case formation.\"},\"castiron\":{\"name\":\"Cast Iron\",\"grade\":\"Ductile (nodular) iron, ASTM A536 grade 65-45-12\",\"alpha\":11.2,\"alphaBasis\":\"approx. mean, 20\\u2013100 \\u00b0C\",\"E\":169,\"nu\":0.29,\"sy\":310,\"syBasis\":\"ASTM A536 65-45-12 specified minimum\",\"k\":36,\"scope\":345,\"scopeBasis\":\"Above about 345 \\u00b0C (650 \\u00b0F), the typical code limit for ductile-iron pressure parts, creep and graphitic growth become relevant.\",\"solidus\":1150,\"lowTempBrittle\":true,\"a1\":727,\"relief\":\"For cast iron, stress relief is commonly carried out around 500\\u2013600 \\u00b0C followed by slow furnace cooling.\"}};\n  var PROP_RANGE = [-20, 100];\n  var ABS_ZERO = -273.15;\n  var CUSTOM_TMAX = 3000;\n  var DEFAULT_CRIT = { u: { mod: 0.67, high: 1 }, g: { mod: 0.67, high: 1 }, c: { mod: null, high: 1 } };\n  var DEFAULTS = { material: \"carbon\", alphaUnit: \"K\", geom: \"plate\", L: 500, W: 100, t: 10, tunit: \"C\", Ti: 20, Tf: 100, dist: \"uniform\", dTg: 30, restraint: \"full\", R: 0.5, dir: \"uni\", allow: \"\" };\n  var GEOM_HINT = {\n    plate: \"Plate: L and W are the in-plane dimensions and t is the plate thickness.\",\n    bar: \"Bar: L is the bar length; W and t describe the cross-section (use the diameter for both on a round bar).\",\n    block: \"Block: three comparable dimensions; the through-thickness gradient acts across t.\",\n    custom: \"Custom: enter the overall envelope dimensions; results are reported per dimension.\"\n  };\n  var LEVEL_LABEL = { low: \"Low Risk\", moderate: \"Moderate Risk\", high: \"High Risk\", nd: \"Risk Not Determined\" };\n  var LEVEL_SHORT = { low: \"Low\", moderate: \"Moderate\", high: \"High\" };\n  var LEVEL_ICON = { low: \"\\u2713\", moderate: \"!\", high: \"!\", nd: \"?\" };\n  var RANK = { low: 1, moderate: 2, high: 3 };\n  \/* =====================================================================\n     FORMATTING\n     ===================================================================== *\/\n  function cToF(c) { return c * 9 \/ 5 + 32; }\n  function fToC(f) { return (f - 32) * 5 \/ 9; }\n  function isNum(v) { return typeof v === \"number\" ? isFinite(v) : false; }\n  function fix(v, d) { var r = Number(v.toFixed(d)); return r === 0 ? 0 : r; }\n  function fmt(v, d) {\n    if (!isNum(v)) { return \"\\u2014\"; }\n    var r = fix(v, d);\n    return (r < 0 ? \"\\u2212\" : \"\") + Math.abs(r).toLocaleString(\"en-US\", { minimumFractionDigits: d, maximumFractionDigits: d });\n  }\n  function fmtS(v, d) {\n    if (!isNum(v)) { return \"\\u2014\"; }\n    return (fix(v, d) > 0 ? \"+\" : \"\") + fmt(v, d);\n  }\n  function fmtTrim(v, d) {\n    if (!isNum(v)) { return \"\\u2014\"; }\n    var r = fix(v, d);\n    return (r < 0 ? \"\\u2212\" : \"\") + Math.abs(r).toLocaleString(\"en-US\", { minimumFractionDigits: 0, maximumFractionDigits: d });\n  }\n  function sigDec(v, sig) {\n    if (!isNum(v) ? true : v === 0) { return 0; }\n    return Math.max(0, Math.min(8, sig - 1 - Math.floor(Math.log(Math.abs(v)) \/ Math.LN10)));\n  }\n  function tSym(u) { return u === \"F\" ? \"\\u00b0F\" : \"\\u00b0C\"; }\n  function fmtTemp(c, u) { return fmtTrim(u === \"F\" ? cToF(c) : c, 1) + \" \" + tSym(u); }\n  function fmtDT(dC, u, signed) {\n    var v = u === \"F\" ? dC * 1.8 : dC;\n    var s = fmtTrim(v, 1);\n    if (signed ? fix(v, 1) > 0 : false) { s = \"+\" + s; }\n    return s + \" \" + tSym(u);\n  }\n  function mmDec(v) { var a = Math.abs(v); return a >= 10 ? 2 : (a >= 1 ? 3 : 4); }\n  function fmtMM(v, signed) { var d = mmDec(v); return (signed ? fmtS(v, d) : fmt(v, d)) + \" mm\"; }\n  function fmtUm(v) { return fmtS(v * 1000, Math.abs(v * 1000) >= 100 ? 1 : 2) + \" \\u00b5m\"; }\n  function fmtMicro(eps) { return fmtS(eps * 1e6, Math.abs(eps * 1e6) >= 100 ? 0 : 1) + \" \\u00b5\\u03b5\"; }\n  function fmtRatio(v) { return fmt(v, Math.abs(v) >= 10 ? 1 : 2); }\n  \/* =====================================================================\n     ENGINE (pure functions, no DOM)\n     inp: alpha [1\/K], E [MPa], nu, sy [MPa], k [W\/m.K] or null,\n          L, W, t [mm], TiC, TfC, dTgC [K], allow [mm] or null, scopeC [degC] or null\n     ===================================================================== *\/\n  function levelOf(v, th) {\n    if (th.high !== null ? v >= th.high : false) { return \"high\"; }\n    if (th.mod !== null ? v >= th.mod : false) { return \"moderate\"; }\n    return \"low\";\n  }\n  function thPair(th) { return (th.mod === null ? \"Moderate not set\" : \"Moderate \\u2265 \" + fmtRatio(th.mod)) + \", \" + (th.high === null ? \"High not set\" : \"High \\u2265 \" + fmtRatio(th.high)); }\n  function compute(inp) {\n    var m = MATERIALS[inp.mat] || null;\n    var u = inp.unit;\n    var a = inp.alpha, E = inp.E, nu = inp.nu, sy = inp.sy;\n    var r = { inp: inp, mat: m };\n    r.dT = inp.TfC - inp.TiC;\n    if (r.dT === 0) { r.dT = 0; }\n    r.eps = a * r.dT;\n    r.dL = r.eps * inp.L; r.dW = r.eps * inp.W; r.dt = r.eps * inp.t;\n    r.dVpct = (Math.pow(1 + r.eps, 3) - 1) * 100;\n    var Reff = inp.restraint === \"free\" ? 0 : (inp.restraint === \"full\" ? 1 : inp.R);\n    var bi = Reff > 0 ? (inp.dir === \"bi\" ? inp.geom === \"plate\" : false) : false;\n    var kb = bi ? 1 \/ (1 - nu) : 1;\n    r.Reff = Reff; r.bi = bi; r.kb = kb;\n    r.sigma = Reff > 0 ? -Reff * E * a * r.dT * kb : null;\n    if (r.sigma === 0) { r.sigma = 0; }\n    r.sigmaFull = -E * a * r.dT * (bi ? 1 \/ (1 - nu) : 1);\n    r.U = r.sigma === null ? null : Math.abs(r.sigma) \/ sy;\n    r.netL = (1 - Reff) * r.dL;\n    r.netW = bi ? (1 - Reff) * r.dW : r.dW * (1 + nu * Reff);\n    r.nett = bi ? r.dt * (1 + 2 * nu * Reff \/ (1 - nu)) : r.dt * (1 + nu * Reff);\n    r.epsY = sy \/ E;\n    r.Ealpha = E * a;\n    r.dTy = sy \/ (E * a);\n    r.dTyR = Reff > 0 ? sy \/ (Reff * E * a * kb) : null;\n    r.dEps = inp.dist === \"nonuniform\" ? a * inp.dTgC : null;\n    r.G = r.dEps === null ? null : r.dEps \/ r.epsY;\n    var allowApplies = inp.allow !== null ? inp.restraint !== \"full\" : false;\n    r.C = allowApplies ? Math.abs(r.netL) \/ inp.allow : null;\n    r.Rp = inp.k !== null ? inp.k * sy * (1 - nu) \/ (E * a) : null;\n    r.slendL = inp.L \/ inp.t; r.slendW = inp.W \/ inp.t;\n    r.tMax = Math.max(inp.TiC, inp.TfC); r.tMin = Math.min(inp.TiC, inp.TfC);\n    \/* ---------- screening criteria ---------- *\/\n    var crit = [\n      { id: \"u\", name: \"Restraint stress utilization\", sym: \"U = |\\u03c3| \/ \\u03c3y\", value: r.U, applicable: Reff > 0, th: inp.crit.u,\n        na: \"Not applicable: free expansion, so no restraint stress\" },\n      { id: \"g\", name: \"Gradient strain ratio\", sym: \"\\u0394\\u03b5 \/ \\u03b5y\", value: r.G, applicable: inp.dist === \"nonuniform\", th: inp.crit.g,\n        na: \"Not applicable: uniform temperature\" },\n      { id: \"c\", name: \"Length change vs. allowance\", sym: \"|\\u0394L| \/ allowance\", value: r.C, applicable: allowApplies, th: inp.crit.c,\n        na: inp.restraint === \"full\" ? \"Not applicable: fully restrained, no length change in the model\" : \"Not evaluated: no allowance entered (optional)\" }\n    ];\n    var worst = null, missing = [], evaluated = [];\n    crit.forEach(function (c) {\n      c.configured = c.th.mod !== null || c.th.high !== null;\n      c.level = null;\n      if (!c.applicable) { c.status = \"na\"; return; }\n      if (!c.configured) { c.status = \"unconfigured\"; missing.push(c); return; }\n      c.status = \"ok\";\n      c.level = levelOf(c.value, c.th);\n      evaluated.push(c);\n      if (worst === null ? true : RANK[c.level] > RANK[worst]) { worst = c.level; }\n    });\n    r.crit = crit;\n    r.byId = {}; crit.forEach(function (c) { r.byId[c.id] = c; });\n    \/* ---------- material temperature gate ---------- *\/\n    r.propsInRange = m ? (r.tMin >= PROP_RANGE[0] ? r.tMax <= PROP_RANGE[1] : false) : true;\n    r.propsOK = r.propsInRange ? true : !!inp.confirmed;\n    r.scopeState = inp.scopeC === null ? \"unchecked\" : (r.tMax > inp.scopeC ? \"exceeded\" : \"ok\");\n    \/* ---------- classification ---------- *\/\n    var critLines = evaluated.map(function (c) {\n      return c.name + \": \" + c.sym + \" = \" + fmtRatio(c.value) + \" \\u2192 \" + LEVEL_SHORT[c.level] + \" (\" + thPair(c.th) + \")\";\n    });\n    var missLines = missing.map(function (c) { return c.name + \" applies but has no thresholds configured (step 5).\"; });\n    var indic = evaluated.length ? worst : (missing.length ? null : \"low\");\n    var rangeTxt = fmtTemp(r.tMin, u) + \" to \" + fmtTemp(r.tMax, u);\n    var risk = { level: null, provisional: null, reasons: [] };\n    if (!r.propsOK) {\n      risk.level = \"nd\"; risk.provisional = indic;\n      risk.reasons.push(\"The default properties are room-temperature values (treated as valid from \" + fmtTemp(PROP_RANGE[0], u) + \" to \" + fmtTemp(PROP_RANGE[1], u) + \"), but this cycle spans \" + rangeTxt + \". Enter \\u03b1, E and \\u03c3y for this range and tick the confirmation in step 3.\");\n      risk.reasons = risk.reasons.concat(critLines, missLines);\n    } else if (worst === \"high\") {\n      risk.level = \"high\";\n      risk.reasons = critLines.slice();\n      if (r.scopeState === \"exceeded\") { risk.reasons.push(\"The maximum temperature is also above the elastic screening limit (\" + fmtTemp(inp.scopeC, u) + \"); time-dependent effects would add to the distortion.\"); }\n      risk.reasons = risk.reasons.concat(missLines);\n    } else if (r.scopeState === \"exceeded\") {\n      risk.level = \"nd\"; risk.provisional = indic;\n      risk.reasons.push(\"The maximum temperature (\" + fmtTemp(r.tMax, u) + \") is above the elastic screening limit for this material (\" + fmtTemp(inp.scopeC, u) + \"). Creep, phase changes or over-aging are not captured, so no risk level is assigned.\");\n      risk.reasons = risk.reasons.concat(critLines, missLines);\n    } else if (r.scopeState === \"unchecked\") {\n      risk.level = \"nd\"; risk.provisional = indic;\n      risk.reasons.push(\"No elastic screening temperature limit is set. Enter one in step 1 so the temperature range can be checked.\");\n      risk.reasons = risk.reasons.concat(critLines, missLines);\n    } else if (missing.length) {\n      risk.level = \"nd\"; risk.provisional = indic;\n      risk.reasons = missLines.concat(critLines);\n    } else if (!evaluated.length) {\n      risk.level = \"low\";\n      risk.reasons.push(\"No restraint and a uniform temperature: the model predicts only reversible expansion, with no restraint stress and no differential strain.\");\n      if (r.C === null ? (inp.restraint !== \"full\") : false) { risk.reasons.push(\"Optional allowance check not used (no allowance entered).\"); }\n    } else {\n      risk.level = worst;\n      risk.reasons = critLines.slice();\n    }\n    r.risk = risk;\n    return r;\n  }\n  function finiteResult(r) {\n    var keys = [\"dT\", \"eps\", \"dL\", \"dW\", \"dt\", \"dVpct\", \"netL\", \"netW\", \"nett\", \"epsY\", \"Ealpha\", \"dTy\", \"slendL\", \"slendW\"];\n    for (var i = 0; i < keys.length; i++) { if (!isNum(r[keys[i]])) { return false; } }\n    var opt = [\"sigma\", \"U\", \"dTyR\", \"dEps\", \"G\", \"C\", \"Rp\"];\n    for (var j = 0; j < opt.length; j++) { if (r[opt[j]] !== null ? !isNum(r[opt[j]]) : false) { return false; } }\n    return true;\n  }\n  \/* =====================================================================\n     UI\n     ===================================================================== *\/\n  function init() {\n    var root = document.getElementById(\"tdr-calculator\");\n    if (!root) { return; }\n    if (root.getAttribute(\"data-tdr-init\") === \"1\") { return; }\n    root.setAttribute(\"data-tdr-init\", \"1\");\n    root.tdrEngine = { compute: compute, MATERIALS: MATERIALS, DEFAULT_CRIT: DEFAULT_CRIT, PROP_RANGE: PROP_RANGE, cToF: cToF, fToC: fToC };\n    function $(id) { return root.querySelector(\"#\" + id); }\n    function mk(tag, cls, text) { var n = document.createElement(tag); if (cls) { n.className = cls; } if (text !== undefined) { n.textContent = text; } return n; }\n    function clear(n) { while (n.firstChild) { n.removeChild(n.firstChild); } }\n    function radioVal(name) { var r = root.querySelector('input[name=\"' + name + '\"]:checked'); return r ? r.value : null; }\n    function setRadio(name, v) { var r = root.querySelector('input[name=\"' + name + '\"][value=\"' + v + '\"]'); if (r) { r.checked = true; } }\n    var el = {\n      form: $(\"tdr-form\"), material: $(\"tdr-material\"), info: $(\"tdr-mat-info\"),\n      alpha: $(\"tdr-alpha\"), alphaUnit: $(\"tdr-alpha-unit\"), E: $(\"tdr-E\"), nu: $(\"tdr-nu\"), sy: $(\"tdr-sy\"), k: $(\"tdr-k\"), scope: $(\"tdr-scope\"),\n      scopeHint: $(\"tdr-scope-hint\"), nuHint: $(\"tdr-nu-hint\"), geomHint: $(\"tdr-geom-hint\"), geomWarn: $(\"tdr-geom-warn\"),\n      L: $(\"tdr-L\"), W: $(\"tdr-W\"), t: $(\"tdr-t\"), Ti: $(\"tdr-Ti\"), Tf: $(\"tdr-Tf\"), dTg: $(\"tdr-dTg\"), gradField: $(\"tdr-grad-field\"),\n      rangeBox: $(\"tdr-range-box\"), rangeText: $(\"tdr-range-text\"), confirm: $(\"tdr-confirm\"), confirmText: $(\"tdr-confirm-text\"),\n      R: $(\"tdr-R\"), RField: $(\"tdr-R-field\"), restraintHint: $(\"tdr-restraint-hint\"), dirField: $(\"tdr-dir-field\"), dirHint: $(\"tdr-dir-hint\"),\n      allow: $(\"tdr-allow\"), allowField: $(\"tdr-allow-field\"),\n      uMod: $(\"tdr-u-mod\"), uHigh: $(\"tdr-u-high\"), gMod: $(\"tdr-g-mod\"), gHigh: $(\"tdr-g-high\"), cMod: $(\"tdr-c-mod\"), cHigh: $(\"tdr-c-high\"),\n      calc: $(\"tdr-calc\"), formMsg: $(\"tdr-form-msg\"), restoreMat: $(\"tdr-restore-mat\"), restoreCrit: $(\"tdr-restore-crit\"),\n      results: $(\"tdr-results\"), stale: $(\"tdr-stale\"),\n      risk: $(\"tdr-risk\"), riskIcon: $(\"tdr-risk-icon\"), riskLevel: $(\"tdr-risk-level\"), riskProv: $(\"tdr-risk-prov\"), riskReasons: $(\"tdr-risk-reasons\"),\n      toast: $(\"tdr-toast\")\n    };\n    var state = { live: false, last: null, timer: null, toastT: null };\n    function unit() { return radioVal(\"tdr-tunit\") || \"C\"; }\n    function matKey() { return el.material.value; }\n    function preset() { return MATERIALS[matKey()] || null; }\n    \/* ---------- reading numbers ---------- *\/\n    function readNum(input) {\n      var s = String(input.value).trim();\n      if (s === \"\") { return (input.validity ? input.validity.badInput : false) ? NaN : null; }\n      var v = Number(s);\n      return isFinite(v) ? v : NaN;\n    }\n    function toC(v) { return unit() === \"F\" ? fToC(v) : v; }\n    function toDispT(c) { return unit() === \"F\" ? cToF(c) : c; }\n    function setVal(input, v, d) { input.value = v === null ? \"\" : String(fix(v, d === undefined ? 6 : d)); }\n    \/* ---------- field errors ---------- *\/\n    function errEl(input) { return $(input.id + \"-err\"); }\n    function setErr(input, msg, errNode) {\n      var e = errNode || errEl(input);\n      if (e) { e.textContent = msg || \"\"; }\n      if (msg) { input.classList.add(\"tdr-invalid\"); input.setAttribute(\"aria-invalid\", \"true\"); }\n      else { input.classList.remove(\"tdr-invalid\"); input.removeAttribute(\"aria-invalid\"); }\n      return !msg;\n    }\n    function numCheck(input, opts) {\n      var v = readNum(input);\n      if (v === null) { return opts.optional ? setErr(input, \"\") : setErr(input, opts.req); }\n      if (isNaN(v)) { return setErr(input, \"Enter a valid number.\"); }\n      var msg = opts.check(v);\n      return setErr(input, msg || \"\");\n    }\n    function vAlpha() {\n      return numCheck(el.alpha, { req: \"Enter the expansion coefficient.\", check: function (v) {\n        var aK = el.alphaUnit.value === \"F\" ? v * 1.8 : v;\n        if (aK <= 0) { return \"The expansion coefficient must be greater than zero.\"; }\n        if (aK > 250) { return \"Values above 250 \\u00d710\\u207b\\u2076\/\\u00b0C are outside the range of solid engineering materials. Check the unit.\"; }\n        return \"\";\n      } });\n    }\n    function vE() {\n      return numCheck(el.E, { req: \"Enter Young\\u2019s modulus.\", check: function (v) {\n        if (v <= 0) { return \"Young\\u2019s modulus must be greater than zero.\"; }\n        if (v > 1200) { return \"E above 1,200 GPa is not physical. Enter E in GPa (steel \\u2248 200 GPa).\"; }\n        return \"\";\n      } });\n    }\n    function vNu() {\n      return numCheck(el.nu, { req: \"Enter Poisson\\u2019s ratio.\", check: function (v) {\n        if (preset()) { if (v < 0.2 ? true : v > 0.45) { return \"For \" + preset().name + \", Poisson\\u2019s ratio should be between 0.20 and 0.45 (metals are typically 0.26\\u20130.36).\"; } }\n        else if (v <= 0 ? true : v >= 0.5) { return \"Poisson\\u2019s ratio must be greater than 0 and less than 0.5 for this model.\"; }\n        return \"\";\n      } });\n    }\n    function vSy() {\n      return numCheck(el.sy, { req: \"Enter the yield strength.\", check: function (v) {\n        if (v <= 0) { return \"Yield strength must be greater than zero.\"; }\n        if (v > 5000) { return \"Yield strength above 5,000 MPa is not realistic. Enter it in MPa.\"; }\n        var E = readNum(el.E);\n        if (isNum(E) ? (E > 0 ? v \/ (E * 1000) > 0.05 : false) : false) { return \"Yield strength is more than 5% of E, which is not physically consistent. Check the units (\\u03c3y in MPa, E in GPa).\"; }\n        return \"\";\n      } });\n    }\n    function vK() {\n      return numCheck(el.k, { optional: true, check: function (v) {\n        if (v <= 0) { return \"Thermal conductivity must be greater than zero, or leave it blank.\"; }\n        if (v > 2500) { return \"Thermal conductivity above 2,500 W\/m\\u00b7K is not realistic.\"; }\n        return \"\";\n      } });\n    }\n    function vScope() {\n      return numCheck(el.scope, { optional: true, check: function (v) {\n        var c = toC(v), p = preset();\n        if (c <= ABS_ZERO) { return \"The limit must be above absolute zero (\" + fmtTemp(ABS_ZERO, unit()) + \").\"; }\n        if (p ? c >= p.solidus : false) { return \"The limit must be below the solidus of \" + p.name + \" (\\u2248 \" + fmtTemp(p.solidus, unit()) + \").\"; }\n        return \"\";\n      } });\n    }\n    function vDim(input, name) {\n      return numCheck(input, { req: \"Enter the \" + name + \".\", check: function (v) {\n        if (v <= 0) { return \"The \" + name + \" must be greater than zero.\"; }\n        if (v > 100000) { return \"The \" + name + \" must not exceed 100,000 mm (100 m).\"; }\n        return \"\";\n      } });\n    }\n    function vTemp(input, name) {\n      return numCheck(input, { req: \"Enter the \" + name + \".\", check: function (v) {\n        var c = toC(v), p = preset();\n        if (c <= ABS_ZERO) { return \"The temperature must be above absolute zero (\" + fmtTemp(ABS_ZERO, unit()) + \").\"; }\n        if (p ? c >= p.solidus : false) { return fmtTemp(c, unit()) + \" is at or above the solidus (melting) temperature of \" + p.name + \" (\\u2248 \" + fmtTemp(p.solidus, unit()) + \"). The solid-state model does not apply.\"; }\n        if (p ? false : c > CUSTOM_TMAX) { return \"Temperatures above \" + fmtTemp(CUSTOM_TMAX, unit()) + \" are outside this tool\\u2019s scope.\"; }\n        return \"\";\n      } });\n    }\n    function vDTg() {\n      if (radioVal(\"tdr-dist\") !== \"nonuniform\") { return setErr(el.dTg, \"\"); }\n      return numCheck(el.dTg, { req: \"Enter the temperature difference across the thickness.\", check: function (v) {\n        if (v < 0) { return \"Enter the magnitude of the difference (zero or positive).\"; }\n        var dC = unit() === \"F\" ? v \/ 1.8 : v;\n        if (dC > 2000) { return \"A difference above \" + fmtDT(2000, unit()) + \" is outside this tool\\u2019s scope.\"; }\n        return \"\";\n      } });\n    }\n    function vR() {\n      if (radioVal(\"tdr-restraint\") !== \"partial\") { return setErr(el.R, \"\"); }\n      return numCheck(el.R, { req: \"Enter the effective restraint factor R (0 to 1).\", check: function (v) {\n        if (v < 0 ? true : v > 1) { return \"R must be between 0 (free) and 1 (fully restrained).\"; }\n        return \"\";\n      } });\n    }\n    function vAllow() {\n      if (radioVal(\"tdr-restraint\") === \"full\") { return setErr(el.allow, \"\"); }\n      return numCheck(el.allow, { optional: true, check: function (v) {\n        if (v <= 0) { return \"The allowance must be greater than zero, or leave it blank.\"; }\n        if (v > 100000) { return \"The allowance must not exceed 100,000 mm.\"; }\n        return \"\";\n      } });\n    }\n    function markInvalid(input, bad) {\n      if (bad) { input.classList.add(\"tdr-invalid\"); input.setAttribute(\"aria-invalid\", \"true\"); }\n      else { input.classList.remove(\"tdr-invalid\"); input.removeAttribute(\"aria-invalid\"); }\n    }\n    function vCritPair(modIn, highIn, errId) {\n      var errNode = $(errId), mv = readNum(modIn), hv = readNum(highIn);\n      var bad = function (v) { return v === null ? false : (isNaN(v) ? true : (v <= 0 ? true : v > 100)); };\n      var mBad = bad(mv), hBad = bad(hv), order = false;\n      if (!mBad ? !hBad : false) { order = mv !== null ? (hv !== null ? mv >= hv : false) : false; }\n      var msg = (mBad ? true : hBad) ? \"Thresholds must be numbers greater than 0 and at most 100, or blank.\" : (order ? \"The Moderate threshold must be lower than the High threshold.\" : \"\");\n      markInvalid(modIn, mBad ? true : order);\n      markInvalid(highIn, hBad ? true : order);\n      errNode.textContent = msg;\n      return !msg;\n    }\n    var VALIDATORS = {\n      \"tdr-alpha\": vAlpha, \"tdr-alpha-unit\": vAlpha, \"tdr-E\": function () { vE(); return vSy(); }, \"tdr-nu\": vNu, \"tdr-sy\": vSy, \"tdr-k\": vK, \"tdr-scope\": vScope,\n      \"tdr-L\": function () { return vDim(el.L, \"length\"); }, \"tdr-W\": function () { return vDim(el.W, \"width\"); }, \"tdr-t\": function () { return vDim(el.t, \"thickness\"); },\n      \"tdr-Ti\": function () { return vTemp(el.Ti, \"initial temperature\"); }, \"tdr-Tf\": function () { return vTemp(el.Tf, \"final temperature\"); },\n      \"tdr-dTg\": vDTg, \"tdr-R\": vR, \"tdr-allow\": vAllow,\n      \"tdr-u-mod\": function () { return vCritPair(el.uMod, el.uHigh, \"tdr-u-err\"); }, \"tdr-u-high\": function () { return vCritPair(el.uMod, el.uHigh, \"tdr-u-err\"); },\n      \"tdr-g-mod\": function () { return vCritPair(el.gMod, el.gHigh, \"tdr-g-err\"); }, \"tdr-g-high\": function () { return vCritPair(el.gMod, el.gHigh, \"tdr-g-err\"); },\n      \"tdr-c-mod\": function () { return vCritPair(el.cMod, el.cHigh, \"tdr-c-err\"); }, \"tdr-c-high\": function () { return vCritPair(el.cMod, el.cHigh, \"tdr-c-err\"); }\n    };\n    function validateAll() {\n      var results = [vAlpha(), vE(), vNu(), vSy(), vK(), vScope(), vDim(el.L, \"length\"), vDim(el.W, \"width\"), vDim(el.t, \"thickness\"),\n        vTemp(el.Ti, \"initial temperature\"), vTemp(el.Tf, \"final temperature\"), vDTg(), vR(), vAllow(),\n        vCritPair(el.uMod, el.uHigh, \"tdr-u-err\"), vCritPair(el.gMod, el.gHigh, \"tdr-g-err\"), vCritPair(el.cMod, el.cHigh, \"tdr-c-err\")];\n      return results.every(Boolean);\n    }\n    function readInput() {\n      var aRaw = readNum(el.alpha);\n      var dist = radioVal(\"tdr-dist\"), restraint = radioVal(\"tdr-restraint\");\n      var dTgRaw = readNum(el.dTg), scopeRaw = readNum(el.scope), allowRaw = readNum(el.allow);\n      return {\n        mat: matKey(), unit: unit(),\n        alpha: (el.alphaUnit.value === \"F\" ? aRaw * 1.8 : aRaw) * 1e-6,\n        E: readNum(el.E) * 1000, nu: readNum(el.nu), sy: readNum(el.sy), k: readNum(el.k),\n        scopeC: scopeRaw === null ? null : toC(scopeRaw),\n        geom: radioVal(\"tdr-geom\"), L: readNum(el.L), W: readNum(el.W), t: readNum(el.t),\n        TiC: toC(readNum(el.Ti)), TfC: toC(readNum(el.Tf)),\n        dist: dist, dTgC: dist === \"nonuniform\" ? (unit() === \"F\" ? dTgRaw \/ 1.8 : dTgRaw) : 0,\n        restraint: restraint, R: restraint === \"partial\" ? readNum(el.R) : (restraint === \"full\" ? 1 : 0),\n        dir: radioVal(\"tdr-dir\"), allow: restraint === \"full\" ? null : allowRaw,\n        confirmed: el.confirm.checked,\n        crit: {\n          u: { mod: readNum(el.uMod), high: readNum(el.uHigh) },\n          g: { mod: readNum(el.gMod), high: readNum(el.gHigh) },\n          c: { mod: readNum(el.cMod), high: readNum(el.cHigh) }\n        }\n      };\n    }\n    \/* ---------- material UI ---------- *\/\n    function infoLine(parts) { var p = mk(\"p\"); parts.forEach(function (x) { p.appendChild(typeof x === \"string\" ? document.createTextNode(x) : x); }); return p; }\n    function renderMatInfo() {\n      var p = preset(), u = unit();\n      clear(el.info);\n      if (!p) {\n        el.info.appendChild(infoLine([mk(\"strong\", \"\", \"Custom material. \"), \"Enter \\u03b1, E, \\u03bd and \\u03c3y (and optionally k) for your material and temperature range. No property-range check is applied. Enter an elastic screening temperature limit to allow a risk classification.\"]));\n        return;\n      }\n      el.info.appendChild(infoLine([mk(\"strong\", \"\", p.grade)]));\n      el.info.appendChild(infoLine([\"\\u03b1 \" + fmtTrim(p.alpha, 2) + \" \\u00d710\\u207b\\u2076\/\\u00b0C (\" + p.alphaBasis + \") \\u00b7 E \" + fmtTrim(p.E, 1) + \" GPa \\u00b7 \\u03bd \" + fmtTrim(p.nu, 3) + \" \\u00b7 \\u03c3y \" + fmtTrim(p.sy, 0) + \" MPa (\" + p.syBasis + \") \\u00b7 k \" + fmtTrim(p.k, 1) + \" W\/m\\u00b7K \\u00b7 solidus \\u2248 \" + fmtTemp(p.solidus, u)]));\n      el.info.appendChild(infoLine([\"Representative room-temperature values, treated as valid from \" + fmtTemp(PROP_RANGE[0], u) + \" to \" + fmtTemp(PROP_RANGE[1], u) + \". Verify against supplier data before critical use.\"]));\n      if (matKey() === \"castiron\") { el.info.appendChild(infoLine([\"Gray cast iron has no defined yield point and a stress-dependent modulus; for gray iron use Custom with an allowable stress in place of \\u03c3y.\"])); }\n    }\n    function fillMaterial() {\n      var p = preset();\n      if (p) {\n        setVal(el.alpha, el.alphaUnit.value === \"F\" ? p.alpha \/ 1.8 : p.alpha, el.alphaUnit.value === \"F\" ? 4 : 3);\n        setVal(el.E, p.E); setVal(el.nu, p.nu); setVal(el.sy, p.sy); setVal(el.k, p.k);\n        setVal(el.scope, toDispT(p.scope), 1);\n      } else {\n        [el.alpha, el.E, el.nu, el.sy, el.k, el.scope].forEach(function (i) { i.value = \"\"; });\n      }\n      el.alpha.placeholder = p ? \"\" : \"e.g. 12.0\"; el.E.placeholder = p ? \"\" : \"e.g. 200\"; el.nu.placeholder = p ? \"\" : \"e.g. 0.30\";\n      el.sy.placeholder = p ? \"\" : \"e.g. 250\"; el.k.placeholder = p ? \"\" : \"optional\"; el.scope.placeholder = p ? \"\" : \"e.g. 350\";\n      el.confirm.checked = false;\n      [el.alpha, el.E, el.nu, el.sy, el.k, el.scope].forEach(function (i) { setErr(i, \"\"); });\n      updateMaterialUI();\n    }\n    function near(a, b) { return Math.abs(a - b) <= Math.max(1e-9, Math.abs(b) * 1e-4); }\n    function updateEditBadges() {\n      var p = preset();\n      function badge(id, edited) { var b = $(id); if (b) { b.hidden = !edited; } }\n      if (!p) { [\"tdr-alpha-edit\", \"tdr-E-edit\", \"tdr-nu-edit\", \"tdr-sy-edit\", \"tdr-k-edit\", \"tdr-scope-edit\"].forEach(function (id) { badge(id, false); }); return; }\n      var a = readNum(el.alpha), e = readNum(el.E), n = readNum(el.nu), s = readNum(el.sy), k = readNum(el.k), sc = readNum(el.scope);\n      var aK = isNum(a) ? (el.alphaUnit.value === \"F\" ? a * 1.8 : a) : a;\n      badge(\"tdr-alpha-edit\", isNum(aK) ? !near(aK, p.alpha) : true);\n      badge(\"tdr-E-edit\", isNum(e) ? !near(e, p.E) : true);\n      badge(\"tdr-nu-edit\", isNum(n) ? !near(n, p.nu) : true);\n      badge(\"tdr-sy-edit\", isNum(s) ? !near(s, p.sy) : true);\n      badge(\"tdr-k-edit\", isNum(k) ? !near(k, p.k) : true);\n      badge(\"tdr-scope-edit\", isNum(sc) ? Math.abs(toC(sc) - p.scope) > 0.05 : true);\n    }\n    function updateMaterialUI() {\n      var p = preset();\n      renderMatInfo();\n      el.scopeHint.textContent = p ? p.scopeBasis + \" Editable.\" : \"Optional, but required for a risk classification: the temperature above which creep, phase changes or aging matter for your material.\";\n      el.restoreMat.hidden = !p;\n      updateEditBadges();\n      updateRangeBox();\n    }\n    \/* ---------- dependent UI ---------- *\/\n    function updateUnitLabels() {\n      var u = unit();\n      var a = root.querySelectorAll(\".tdr-tunit-label\"), b = root.querySelectorAll(\".tdr-tdiff-label\"), i;\n      for (i = 0; i < a.length; i++) { a[i].textContent = tSym(u); }\n      for (i = 0; i < b.length; i++) { b[i].textContent = tSym(u); }\n    }\n    function updateGeomUI() {\n      var g = radioVal(\"tdr-geom\");\n      el.geomHint.textContent = GEOM_HINT[g] || \"\";\n      var L = readNum(el.L), W = readNum(el.W), t = readNum(el.t), msg = \"\";\n      if (isNum(L) ? (isNum(W) ? isNum(t) : false) : false) {\n        if (g === \"plate\" ? t > Math.min(L, W) : false) { msg = \"Note: the thickness is larger than an in-plane dimension; Block may fit better.\"; }\n        if (g === \"bar\" ? L < Math.max(W, t) : false) { msg = \"Note: the bar length is shorter than its cross-section.\"; }\n      }\n      el.geomWarn.textContent = msg;\n    }\n    function updateRestraintUI() {\n      var rs = radioVal(\"tdr-restraint\"), g = radioVal(\"tdr-geom\");\n      el.RField.hidden = rs !== \"partial\";\n      el.allowField.hidden = rs === \"full\";\n      var showDir = g === \"plate\" ? rs !== \"free\" : false;\n      el.dirField.hidden = !showDir;\n      if (!showDir ? radioVal(\"tdr-dir\") === \"bi\" : false) { setRadio(\"tdr-dir\", \"uni\"); }\n      el.restraintHint.textContent = rs === \"free\" ? \"Ideal free expansion: the part can expand or contract without resistance, so no restraint stress develops.\" :\n        (rs === \"full\" ? \"The surroundings fully prevent the thermal strain in the restrained direction (simplified uniaxial model by default).\" : \"The surroundings prevent a fraction R of the free thermal strain.\");\n      el.dirHint.textContent = radioVal(\"tdr-dir\") === \"bi\" ? \"Equal biaxial restraint of the plate edges: \\u03c3 = \\u2212R\\u00b7E\\u00b7\\u03b1\\u00b7\\u0394T \/ (1 \\u2212 \\u03bd), larger than the uniaxial value.\" : \"Uniaxial restraint along the length L: \\u03c3 = \\u2212R\\u00b7E\\u00b7\\u03b1\\u00b7\\u0394T.\";\n      if (rs !== \"partial\") { setErr(el.R, \"\"); }\n      if (rs === \"full\") { setErr(el.allow, \"\"); }\n    }\n    function updateDistUI() {\n      var non = radioVal(\"tdr-dist\") === \"nonuniform\";\n      el.gradField.hidden = !non;\n      if (!non) { setErr(el.dTg, \"\"); }\n    }\n    function updateRangeBox() {\n      var p = preset(), Ti = readNum(el.Ti), Tf = readNum(el.Tf), u = unit();\n      if (!p ? true : (!isNum(Ti) ? true : !isNum(Tf))) { el.rangeBox.hidden = true; return; }\n      var lo = Math.min(toC(Ti), toC(Tf)), hi = Math.max(toC(Ti), toC(Tf));\n      var out = lo < PROP_RANGE[0] ? true : hi > PROP_RANGE[1];\n      el.rangeBox.hidden = !out;\n      if (out) {\n        el.rangeText.textContent = \"This cycle (\" + fmtTemp(lo, u) + \" to \" + fmtTemp(hi, u) + \") is outside the \" + fmtTemp(PROP_RANGE[0], u) + \" to \" + fmtTemp(PROP_RANGE[1], u) + \" range of the room-temperature defaults. \\u03b1 usually rises and E and \\u03c3y fall with temperature. Enter the mean \\u03b1 for the range and E and \\u03c3y at the highest temperature, then confirm. Until then, risk is reported as \\u201cNot Determined\\u201d.\";\n        el.confirmText.textContent = \"I confirm that \\u03b1, E and \\u03c3y entered above apply to \" + fmtTemp(lo, u) + \" to \" + fmtTemp(hi, u) + \".\";\n      }\n    }\n    function updateAllUI() { updateUnitLabels(); updateGeomUI(); updateRestraintUI(); updateDistUI(); updateEditBadges(); updateRangeBox(); }\n    \/* ---------- unit switching converts entered values ---------- *\/\n    var lastUnit = unit(), lastAlphaUnit = el.alphaUnit.value;\n    function onTempUnit() {\n      var u = unit();\n      if (u === lastUnit) { return; }\n      [el.Ti, el.Tf, el.scope].forEach(function (i) {\n        var v = readNum(i);\n        if (isNum(v)) { setVal(i, u === \"F\" ? cToF(v) : fToC(v), 2); }\n      });\n      var d = readNum(el.dTg);\n      if (isNum(d)) { setVal(el.dTg, u === \"F\" ? d * 1.8 : d \/ 1.8, 2); }\n      lastUnit = u;\n      updateUnitLabels(); renderMatInfo(); updateRangeBox(); updateEditBadges();\n    }\n    function onAlphaUnit() {\n      var u = el.alphaUnit.value, v = readNum(el.alpha);\n      if (u === lastAlphaUnit) { return; }\n      if (isNum(v)) { setVal(el.alpha, u === \"F\" ? v \/ 1.8 : v * 1.8, 4); }\n      lastAlphaUnit = u;\n      vAlpha(); updateEditBadges();\n    }\n    \/* =====================================================================\n       RENDER RESULTS\n       ===================================================================== *\/\n    function kpi(id, value, sub, na) {\n      var v = $(\"tdr-k-\" + id), s = $(\"tdr-k-\" + id + \"-sub\");\n      v.textContent = value; v.className = \"tdr-kpi-value\" + (na ? \" tdr-na\" : \"\");\n      s.textContent = sub || \"\";\n    }\n    function render(r, scroll) {\n      var inp = r.inp, u = inp.unit, risk = r.risk;\n      el.results.hidden = false;\n      root.classList.remove(\"tdr-is-stale\"); el.stale.hidden = true;\n      \/* risk banner *\/\n      el.risk.className = \"tdr-risk tdr-risk-\" + risk.level;\n      el.riskIcon.textContent = LEVEL_ICON[risk.level];\n      el.riskLevel.textContent = LEVEL_LABEL[risk.level];\n      if (risk.level === \"nd\" ? risk.provisional !== null : false) {\n        el.riskProv.hidden = false;\n        el.riskProv.textContent = \"Provisional indicator: \" + LEVEL_SHORT[risk.provisional] + \" (not a classification until the items below are resolved)\";\n      } else { el.riskProv.hidden = true; }\n      clear(el.riskReasons);\n      risk.reasons.forEach(function (t) { el.riskReasons.appendChild(mk(\"li\", \"\", t)); });\n      \/* KPIs *\/\n      kpi(\"temp\", fmtDT(r.dT, u, true), u === \"F\" ? \"= \" + fmtDT(r.dT, \"C\", true) + \" (difference, \\u00d75\/9)\" : (r.dT > 0 ? \"Heating\" : (r.dT < 0 ? \"Cooling\" : \"No change\")) + \" \\u00b7 \" + fmtDT(r.dT, \"F\", true));\n      $(\"tdr-k-len-label\").textContent = r.dT < 0 ? \"Linear contraction \\u0394L\" : \"Linear expansion \\u0394L\";\n      kpi(\"len\", fmtMM(r.dL, true), fmtUm(r.dL) + \" \\u00b7 free, L\\u2080 = \" + fmtTrim(inp.L, 3) + \" mm\");\n      kpi(\"wid\", fmtMM(r.dW, true), fmtUm(r.dW) + \" \\u00b7 free, W\\u2080 = \" + fmtTrim(inp.W, 3) + \" mm\");\n      kpi(\"thk\", fmtMM(r.dt, true), fmtUm(r.dt) + \" \\u00b7 free, t\\u2080 = \" + fmtTrim(inp.t, 3) + \" mm\");\n      kpi(\"eps\", fmtMicro(r.eps), fmtS(r.eps * 100, sigDec(r.eps * 100, 3)) + \" % \\u00b7 \\u03b5 = \\u03b1\\u00b7\\u0394T\");\n      if (r.sigma === null) {\n        kpi(\"sig\", \"Not Applicable\", \"Free expansion: no restraint-induced stress\", true);\n        kpi(\"U\", \"Not Applicable\", \"No restraint stress to compare with \\u03c3y\", true);\n      } else {\n        kpi(\"sig\", fmtS(r.sigma, 1) + \" MPa\", (r.sigma < 0 ? \"Compressive\" : (r.sigma > 0 ? \"Tensile\" : \"Zero\")) + \" \\u00b7 \" + fmtS(r.sigma \/ 6.894757, 2) + \" ksi \\u00b7 \" + (r.bi ? \"equal biaxial\" : \"uniaxial\") + \", R = \" + fmtTrim(r.Reff, 3));\n        var uc = r.byId.u;\n        kpi(\"U\", fmtRatio(r.U), fmt(r.U * 100, 0) + \"% of \\u03c3y\" + (uc.level ? \" \\u00b7 \" + LEVEL_SHORT[uc.level] : (uc.status === \"unconfigured\" ? \" \\u00b7 thresholds not set\" : \"\")));\n      }\n      if (r.dEps === null) { kpi(\"de\", \"Not Applicable\", \"Uniform temperature distribution\", true); }\n      else {\n        var gc = r.byId.g;\n        kpi(\"de\", fmt(r.dEps * 1e6, Math.abs(r.dEps * 1e6) >= 100 ? 0 : 1) + \" \\u00b5\\u03b5\", \"\\u0394\\u03b5\/\\u03b5y = \" + fmtRatio(r.G) + (gc.level ? \" \\u00b7 \" + LEVEL_SHORT[gc.level] : (gc.status === \"unconfigured\" ? \" \\u00b7 thresholds not set\" : \"\")) + \" \\u00b7 indicator only\");\n      }\n      if (scroll) { focusResults(); }\n    }\n    function focusResults() {\n      try { el.results.focus({ preventScroll: true }); } catch (e) { \/* ignore *\/ }\n      var rect = el.results.getBoundingClientRect();\n      var vh = window.innerHeight || document.documentElement.clientHeight;\n      if (rect.top < 0 ? true : rect.top > vh * 0.55) {\n        if (el.results.scrollIntoView) { el.results.scrollIntoView({ behavior: \"smooth\", block: \"start\" }); }\n      }\n    }\n    function toast(msg) {\n      el.toast.textContent = msg; el.toast.classList.add(\"tdr-show\");\n      clearTimeout(state.toastT); state.toastT = setTimeout(function () { el.toast.classList.remove(\"tdr-show\"); }, 2200);\n    }\n    \/* =====================================================================\n       CALCULATE \/ LIVE UPDATE\n       ===================================================================== *\/\n    function run(fromButton) {\n      el.formMsg.textContent = \"\";\n      var ok = validateAll();\n      if (!ok) {\n        if (fromButton) {\n          el.formMsg.textContent = \"Please correct the highlighted fields.\";\n          var first = root.querySelector(\".tdr-invalid\");\n          if (first) {\n            var det = first.closest ? first.closest(\"details\") : null;\n            if (det) { det.open = true; }\n            first.focus();\n          }\n        }\n        if (state.last) { root.classList.add(\"tdr-is-stale\"); el.stale.hidden = false; }\n        return;\n      }\n      var r = compute(readInput());\n      if (!finiteResult(r)) {\n        el.formMsg.textContent = \"The inputs produce a non-finite result. Check the values and units.\";\n        if (state.last) { root.classList.add(\"tdr-is-stale\"); el.stale.hidden = false; }\n        return;\n      }\n      state.last = r; state.live = true;\n      render(r, fromButton);\n    }\n    function schedule() { clearTimeout(state.timer); state.timer = setTimeout(function () { run(false); }, 160); }\n    function setCrit(c) {\n      setVal(el.uMod, c.u.mod); setVal(el.uHigh, c.u.high); setVal(el.gMod, c.g.mod); setVal(el.gHigh, c.g.high); setVal(el.cMod, c.c.mod); setVal(el.cHigh, c.c.high);\n      [el.uMod, el.uHigh, el.gMod, el.gHigh, el.cMod, el.cHigh].forEach(function (i) { setErr(i, \"\", $(i.id.slice(0, 5) + \"-err\")); });\n    }\n    function applyDefaults() {\n      el.material.value = DEFAULTS.material; el.alphaUnit.value = DEFAULTS.alphaUnit; lastAlphaUnit = DEFAULTS.alphaUnit;\n      setRadio(\"tdr-tunit\", DEFAULTS.tunit); lastUnit = DEFAULTS.tunit;\n      setRadio(\"tdr-geom\", DEFAULTS.geom); setRadio(\"tdr-dist\", DEFAULTS.dist); setRadio(\"tdr-restraint\", DEFAULTS.restraint); setRadio(\"tdr-dir\", DEFAULTS.dir);\n      setVal(el.L, DEFAULTS.L); setVal(el.W, DEFAULTS.W); setVal(el.t, DEFAULTS.t);\n      setVal(el.Ti, DEFAULTS.Ti); setVal(el.Tf, DEFAULTS.Tf); setVal(el.dTg, DEFAULTS.dTg); setVal(el.R, DEFAULTS.R); el.allow.value = DEFAULTS.allow;\n      setCrit(DEFAULT_CRIT);\n      fillMaterial();\n      var all = root.querySelectorAll(\".tdr-invalid\"), i;\n      for (i = 0; i < all.length; i++) { all[i].classList.remove(\"tdr-invalid\"); all[i].removeAttribute(\"aria-invalid\"); }\n      var errs = root.querySelectorAll(\".tdr-error\");\n      for (i = 0; i < errs.length; i++) { errs[i].textContent = \"\"; }\n      updateAllUI();\n    }\n    \/* ---------- events (bound once) ---------- *\/\n    function onFieldEvent(e) {\n      var t = e.target;\n      if (!t ? true : !t.id) { return; }\n      if (t.name === \"tdr-tunit\") { onTempUnit(); }\n      if (t.id === \"tdr-alpha-unit\") { onAlphaUnit(); }\n      if (t.id === \"tdr-material\") {\n        fillMaterial();\n        [\"tdr-Ti\", \"tdr-Tf\"].forEach(function (id) { if (String($(id).value).trim() !== \"\") { VALIDATORS[id](); } });\n      }\n      if (t.name === \"tdr-geom\") { updateGeomUI(); updateRestraintUI(); }\n      if (t.name === \"tdr-restraint\") { updateRestraintUI(); }\n      if (t.name === \"tdr-dir\") { updateRestraintUI(); }\n      if (t.name === \"tdr-dist\") { updateDistUI(); }\n      if (t.name === \"tdr-tunit\") { [\"tdr-Ti\", \"tdr-Tf\", \"tdr-scope\", \"tdr-dTg\"].forEach(function (id) { if (String($(id).value).trim() !== \"\") { VALIDATORS[id](); } }); }\n      if (VALIDATORS[t.id]) { VALIDATORS[t.id](); }\n      if (t.id === \"tdr-L\" ? true : (t.id === \"tdr-W\" ? true : t.id === \"tdr-t\")) { updateGeomUI(); }\n      if (t.id === \"tdr-Ti\" ? true : t.id === \"tdr-Tf\") { updateRangeBox(); }\n      if (t.id === \"tdr-nu\") { if (radioVal(\"tdr-dir\") === \"bi\") { vNu(); } }\n      updateEditBadges();\n      if (state.live) { schedule(); }\n    }\n    function isChoice(t) { return !t ? false : (t.tagName === \"SELECT\" ? true : (t.type === \"radio\" ? true : t.type === \"checkbox\")); }\n    el.form.addEventListener(\"input\", function (e) { if (!isChoice(e.target)) { onFieldEvent(e); } });\n    el.form.addEventListener(\"change\", function (e) { if (isChoice(e.target)) { onFieldEvent(e); } });\n    el.form.addEventListener(\"keydown\", function (e) {\n      if (e.key === \"Enter\" ? (e.target ? e.target.tagName === \"INPUT\" : false) : false) { e.preventDefault(); run(true); }\n    });\n    el.calc.addEventListener(\"click\", function () { run(true); });\n    el.restoreMat.addEventListener(\"click\", function () { fillMaterial(); if (state.live) { schedule(); } toast(\"Material defaults restored\"); });\n    el.restoreCrit.addEventListener(\"click\", function () { setCrit(DEFAULT_CRIT); if (state.live) { schedule(); } toast(\"Default criteria restored\"); });\n    applyDefaults();\n  }\n  if (document.getElementById(\"tdr-calculator\")) { init(); }\n  else if (document.readyState === \"loading\") { document.addEventListener(\"DOMContentLoaded\", init); }\n  else { init(); }\n})();\n<\/script>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>The Thermal Distortion Risk Assessment Calculator helps engineers, manufacturers, and heat treatment professionals evaluate potential thermal deformation risks caused by temperature changes in metal components&#8230;.<\/p>","protected":false},"author":2,"featured_media":24934,"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_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[8647],"tags":[4921,8733,8583,8717],"class_list":["post-24933","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-materials-science-and-properties","tag-heat-treatment","tag-thermal-distortion","tag-thermal-expansion","tag-thermal-stress","pmpro-has-access"],"acf":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/www.mechstream.com\/wp-content\/uploads\/2026\/10\/Thermal-Distortion-Risk-Assessment-Calculator.png","_links":{"self":[{"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/posts\/24933","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/comments?post=24933"}],"version-history":[{"count":0,"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/posts\/24933\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/media\/24934"}],"wp:attachment":[{"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/media?parent=24933"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/categories?post=24933"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/tags?post=24933"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}