.
Equations: IEC 60534-2-1 style, SI units (bar, m3/h, kg/h, K). */
(function () {
'use strict';
function init() {
var root = document.getElementById('cvs-app');
if (!root || root.getAttribute('data-ready')) return;
root.setAttribute('data-ready', '1');
var $ = function (id) { return root.querySelector('#cvs_' + id); };
var N1 = 0.865, N8 = 94.8, KV = 1.156099, RG = 8314.462618;
var CASES = [['Minimum', 5, 10, 8], ['Normal', 25, 10, 7], ['Maximum', 40, 10, 6]];
var ids = ['project','plant','tag','service','pid','datasheet','engineer','phase','flow','pin','pout','temp','pipe','density','visc','pv','pc','mw','k','z','vtype','characteristic','ratedCv','FL','xT','FP','rangeability'];
var last = null, lastCases = [], marks = { main: null, cases: [] };
/* ---------- helpers ---------- */
function n(id) { return parseFloat($(id).value); }
function fmt(x, d) { return isFinite(x) ? Number(x).toLocaleString('en-US', { maximumFractionDigits: d == null ? 3 : d }) : '—'; }
function esc(s) { return String(s).replace(/[&<>"']/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; }); }
function txt(id, s) { $(id).textContent = s; }
function lvlOf(w) { return w.some(function (x) { return x[0] === 'bad'; }) ? 'bad' : (w.length ? 'warn' : 'ok'); }
function badge(l) { return '' + (l === 'ok' ? 'OK' : l === 'bad' ? 'Review' : 'Caution') + ' '; }
/* ---------- operating case rows ---------- */
$('caseRows').innerHTML = CASES.map(function (c, i) {
ids.push('c' + i + 'q', 'c' + i + 'a', 'c' + i + 'b');
var inp = function (k, v) { return '
'; };
var out = function (k) { return '— '; };
return '' + c[0] + ' ' + inp('q', c[1]) + inp('a', c[2]) + inp('b', c[3]) + out('cv') + out('r') + out('t') + out('s') + ' ';
}).join('');
var defaults = {};
ids.forEach(function (id) { defaults[id] = $(id).value; });
/* ---------- tabs and phase ---------- */
function tab(id) {
root.querySelectorAll('.cvs-tab').forEach(function (b) { b.classList.toggle('cvs-active', b.getAttribute('data-tab') === id); });
root.querySelectorAll('.cvs-pane').forEach(function (p) { p.classList.toggle('cvs-active', p.id === 'cvs_pane_' + id); });
}
root.querySelectorAll('.cvs-tab').forEach(function (b) { b.addEventListener('click', function () { tab(b.getAttribute('data-tab')); }); });
function phaseUI() {
var ph = $('phase').value;
root.querySelectorAll('[data-ph]').forEach(function (el) { el.classList.toggle('cvs-hide', el.getAttribute('data-ph') !== ph); });
var u = ph === 'liquid' ? 'm³/h' : 'kg/h';
txt('flowLbl', 'Required flow, ' + u); txt('fu', u);
}
/* ---------- validation ---------- */
function check(q, p1, p2) {
var e = [], ph = $('phase').value;
if (!(q > 0)) e.push('Flow must be greater than zero.');
if (!(p1 > 0 && p2 > 0)) e.push('Absolute pressures must be greater than zero.');
else if (p1 <= p2) e.push('Upstream pressure must exceed downstream pressure.');
if (!(n('ratedCv') > 0)) e.push('Rated Cv must be greater than zero.');
if (!(n('FP') > 0 && n('FP') <= 1.5)) e.push('FP must be between 0 and 1.5.');
if (!(n('rangeability') >= 2)) e.push('Rangeability must be at least 2.');
if (!(n('temp') > -273.15)) e.push('Temperature must be above absolute zero.');
if (ph === 'liquid') {
if (!(n('FL') > 0 && n('FL') <= 1)) e.push('FL must be between 0 and 1.');
if (!(n('density') > 0)) e.push('Density must be greater than zero.');
if (!(n('pv') >= 0)) e.push('Vapor pressure must be zero or more.');
if (!(n('pc') > 0)) e.push('Critical pressure must be greater than zero.');
if (p1 <= n('pv')) e.push('Upstream pressure is at or below vapor pressure: fluid is not a liquid at the inlet.');
} else {
if (!(n('xT') > 0 && n('xT') <= 1)) e.push('xT must be between 0 and 1.');
if (!(n('mw') > 0)) e.push('Molecular weight must be greater than zero.');
if (!(n('k') > 1)) e.push('k must be greater than 1.');
if (!(n('z') > 0)) e.push('Z must be greater than zero.');
}
return e;
}
/* ---------- sizing ---------- */
function size(q, p1, p2) {
var ph = $('phase').value, FP = n('FP'), d = n('pipe'), A = d > 0 ? Math.PI * d * d / 4e6 : 0;
var r = { ph: ph, q: q, p1: p1, p2: p2, dp: p1 - p2, FP: FP };
if (ph === 'liquid') {
var sg = n('density') / 999.016, pv = n('pv'), pc = n('pc'), FL = n('FL');
var FF = 0.96 - 0.28 * Math.sqrt(pv / pc);
var dpMax = FL * FL * (p1 - FF * pv), dpE = Math.min(r.dp, dpMax);
r.sg = sg; r.FF = FF; r.dpMax = dpMax; r.choked = r.dp >= dpMax;
r.cv = q / (N1 * FP) * Math.sqrt(sg / dpE); /* Cv = Q / (N1 Fp) * sqrt(SG / dP) */
r.flash = p2 <= pv;
r.kc = r.dp / (p1 - pv);
r.cav = r.flash ? 'Flashing likely' : r.choked ? 'Choked / cavitating' : (r.kc >= 0.7 * FL * FL ? 'Incipient cavitation possible' : 'Not indicated');
r.regime = r.choked ? 'Choked flow' : 'Non-choked';
if (A > 0) r.v = q / 3600 / A;
} else {
var M = n('mw'), k = n('k'), Z = n('z'), T = n('temp') + 273.15, xT = n('xT');
var xc = (k / 1.4) * xT, x = r.dp / p1, xE = Math.min(x, xc), Y = 1 - xE / (3 * xc);
r.x = x; r.xc = xc; r.Y = Y; r.choked = x >= xc;
r.cv = q / (N8 * FP * p1 * Y) * Math.sqrt(T * Z / (xE * M)); /* Cv = W/(N8 Fp P1 Y) * sqrt(T Z / (x M)) */
r.rho1 = p1 * 1e5 * M / (RG * Z * T);
r.cav = 'Not applicable'; r.regime = r.choked ? 'Choked flow' : 'Non-choked';
if (A > 0) { var rho2 = p2 * 1e5 * M / (RG * Z * T); r.v = q / 3600 / rho2 / A; r.mach = r.v / Math.sqrt(k * Z * RG * T / M); }
}
r.kv = r.cv * KV;
r.rated = n('ratedCv'); r.ratio = r.rated / r.cv; r.t = travel(r.cv / r.rated);
return r;
}
/* ---------- characteristic (t = travel fraction 0..1, f = capacity fraction) ---------- */
function travel(f) {
var R = Math.max(2, n('rangeability') || 50), c = $('characteristic').value;
if (!(f > 0)) return 0;
var t = c === 'linear' ? f : c === 'quick' ? f * f : 1 + Math.log(Math.max(f, 1 / R)) / Math.log(R);
return Math.max(0, Math.min(1, t)) * 100;
}
function cap(t) {
var R = Math.max(2, n('rangeability') || 50), c = $('characteristic').value;
return c === 'linear' ? t : c === 'quick' ? Math.sqrt(t) : Math.pow(R, t - 1);
}
/* ---------- assessment ---------- */
function assess(r) {
var w = [];
if (r.ratio < 1) w.push(['bad', 'Rated Cv is below the required Cv.']);
else if (r.ratio > 3) w.push(['warn', 'Rated Cv is more than 3× required: check for oversizing.']);
if (r.ph === 'liquid') {
if (r.flash) w.push(['bad', 'P2 is at or below vapor pressure: flashing likely.']);
else if (r.choked) w.push(['bad', 'ΔP is at or above the allowable ΔP: choked flow / cavitation.']);
else if (r.cav.indexOf('Incipient') === 0) w.push(['warn', 'Incipient cavitation possible (screening, Kc ≈ 0.7·FL²).']);
if (r.v > 5) w.push(['warn', 'Downstream liquid velocity above 5 m/s.']);
if (n('visc') > 20) w.push(['warn', 'Viscosity above 20 cP: Reynolds correction (FR) is not applied.']);
} else {
if (r.choked) w.push(['warn', 'Choked gas flow: flow no longer rises with ΔP. Check noise and vibration.']);
if (r.mach > 0.5) w.push(['bad', 'Downstream Mach number above 0.5.']);
else if (r.mach > 0.3) w.push(['warn', 'Downstream Mach number above 0.3.']);
}
if (r.ratio >= 1 && r.t < 10) w.push(['warn', 'Travel below 10%: poor control resolution.']);
if (r.ratio >= 1 && r.t > 90) w.push(['warn', 'Travel above 90%: little control margin.']);
return w;
}
/* ---------- plot ---------- */
function draw() {
var c = $('curve'), x = c.getContext('2d'), w = c.width, h = c.height, L = 62, R = 24, T = 22, B = 48, pw = w - L - R, ph = h - T - B, i;
var px = function (t) { return L + pw * t / 100; }, py = function (f) { return h - B - ph * f; };
x.clearRect(0, 0, w, h); x.fillStyle = '#0e171e'; x.fillRect(0, 0, w, h);
x.font = '14px system-ui,sans-serif'; x.lineWidth = 1;
for (i = 0; i <= 5; i++) {
x.strokeStyle = '#344655'; x.fillStyle = '#a5b5c1';
x.beginPath(); x.moveTo(px(i * 20), T); x.lineTo(px(i * 20), h - B); x.stroke();
x.beginPath(); x.moveTo(L, py(i / 5)); x.lineTo(w - R, py(i / 5)); x.stroke();
x.fillText(String(i * 20), px(i * 20) - 10, h - B + 22); x.fillText(String(i * 20), 26, py(i / 5) + 5);
}
x.fillStyle = '#edf3f7'; x.fillText('Valve travel (%)', w / 2 - 45, h - 8);
x.save(); x.translate(16, h / 2 + 60); x.rotate(-Math.PI / 2); x.fillText('Relative capacity (%)', 0, 0); x.restore();
x.strokeStyle = '#53d6a0'; x.lineWidth = 3; x.beginPath();
for (i = 0; i <= 100; i++) { if (i === 0) x.moveTo(px(i), py(cap(i / 100))); else x.lineTo(px(i), py(cap(i / 100))); }
x.stroke();
var pts = [];
if (marks.main) pts.push([marks.main, '#72b7ff', 'Design']);
marks.cases.forEach(function (m, j) { if (m) pts.push([m, ['#ffd166', '#ffffff', '#ff7373'][j], ['Min', 'Nor', 'Max'][j]]); });
pts.forEach(function (p) {
var X = px(p[0].t), Y = py(Math.min(1, p[0].f));
x.fillStyle = p[1]; x.beginPath(); x.arc(X, Y, 6, 0, 6.2832); x.fill();
x.fillText(p[2], Math.min(X + 9, w - 60), Math.max(Y - 9, T + 12));
});
}
/* ---------- design point ---------- */
function calcMain() {
var q = n('flow'), p1 = n('pin'), p2 = n('pout'), e = check(q, p1, p2);
if (e.length) { $('validation').innerHTML = 'Input errors ' + esc(e.join(' ')); return false; }
var r = size(q, p1, p2), w = assess(r); r.warns = w; last = r;
$('validation').innerHTML = badge(lvlOf(w)) + ' Calculation completed. Review the checks before use.';
txt('reqCv', fmt(r.cv, 3)); txt('reqKv', fmt(r.kv, 3)); txt('ratio', fmt(r.ratio, 2) + '×'); txt('trv', fmt(r.t, 1) + '%');
txt('dp', fmt(r.dp, 3) + ' bar'); txt('regime', r.regime); txt('cav', r.cav);
txt('vel', r.v ? (r.ph === 'liquid' ? fmt(r.v, 2) + ' m/s' : fmt(r.v, 1) + ' m/s (Ma ' + fmt(r.mach, 2) + ')') : '—');
var notes = w.concat([['warn', 'Valve factors, full IEC/ISA method, noise, actuator and mechanical checks must be verified separately.']]);
$('warns').innerHTML = notes.map(function (x) { return '' + badge(x[0]) + '
' + esc(x[1]) + '
'; }).join('');
var rows = [['Phase', r.ph], ['Flow', fmt(q, 4) + (r.ph === 'liquid' ? ' m³/h' : ' kg/h')], ['P1 / P2', fmt(p1, 4) + ' / ' + fmt(p2, 4) + ' bara'], ['ΔP', fmt(r.dp, 4) + ' bar'], ['FP', fmt(r.FP, 4)], ['Required Cv', fmt(r.cv, 5)], ['Required Kv', fmt(r.kv, 5)], ['Rated Cv', fmt(r.rated, 4)], ['Rated / required', fmt(r.ratio, 4)], ['Travel', fmt(r.t, 2) + '%']];
if (r.ph === 'liquid') rows.push(['Specific gravity', fmt(r.sg, 5)], ['FF', fmt(r.FF, 5)], ['Allowable ΔP (choked)', fmt(r.dpMax, 4) + ' bar'], ['ΔP / (P1 − Pv)', fmt(r.kc, 4)]);
else rows.push(['x = ΔP / P1', fmt(r.x, 5)], ['Choking limit Fk·xT', fmt(r.xc, 5)], ['Expansion factor Y', fmt(r.Y, 5)], ['Inlet density', fmt(r.rho1, 4) + ' kg/m³']);
$('inter').innerHTML = rows.map(function (a) { return '' + esc(a[0]) + ' ' + esc(a[1]) + ' '; }).join('');
marks.main = { t: r.t, f: r.cv / r.rated };
draw(); renderReport(); return true;
}
/* ---------- operating cases ---------- */
function calcCases() {
lastCases = []; marks.cases = [];
CASES.forEach(function (c, i) {
var q = n('c' + i + 'q'), p1 = n('c' + i + 'a'), p2 = n('c' + i + 'b'), e = check(q, p1, p2);
var cell = function (k) { return $('c' + i + k); };
if (e.length) { ['cv', 'r', 't'].forEach(function (k) { cell(k).textContent = '—'; }); cell('s').innerHTML = badge('bad') + ' ' + esc(e[0]); lastCases.push(null); marks.cases.push(null); return; }
var r = size(q, p1, p2), w = assess(r), l = lvlOf(w); r.name = c[0]; r.warns = w; lastCases.push(r);
cell('cv').textContent = fmt(r.cv, 3); cell('r').textContent = fmt(r.ratio, 2) + '×'; cell('t').textContent = fmt(r.t, 1);
cell('s').innerHTML = badge(l) + (w.length ? ' ' + esc(w[0][1]) : '');
marks.cases.push({ t: r.t, f: r.cv / r.rated });
});
draw(); renderReport();
}
/* ---------- report / export ---------- */
function renderReport() {
if (!last) return;
var rows = [['Project', $('project').value], ['Plant / unit', $('plant').value], ['Valve tag', $('tag').value], ['Service', $('service').value], ['Phase', last.ph], ['Flow', fmt(last.q, 4) + (last.ph === 'liquid' ? ' m³/h' : ' kg/h')], ['P1 / P2', fmt(last.p1, 4) + ' / ' + fmt(last.p2, 4) + ' bara'], ['Required Cv', fmt(last.cv, 4)], ['Rated Cv', fmt(last.rated, 4)], ['Rated / required', fmt(last.ratio, 3)], ['Travel', fmt(last.t, 1) + '%'], ['Flow regime', last.regime], ['Cavitation / flashing', last.cav]];
var cs = lastCases.filter(Boolean).map(function (r) { return '' + esc(r.name) + ' ' + fmt(r.q, 3) + ' ' + fmt(r.p1, 3) + '/' + fmt(r.p2, 3) + ' ' + fmt(r.cv, 3) + ' ' + fmt(r.t, 1) + '% '; }).join('');
$('reportText').innerHTML = 'Control valve sizing report ' + rows.map(function (a) { return '' + esc(a[0]) + ' ' + esc(a[1]) + ' '; }).join('') + '
' +
(cs ? 'Operating cases Case Flow P1/P2 bara Req. Cv Travel ' + cs + '
' : '') +
'Preliminary calculation. Not equivalent to CONVAL or a validated IEC/ISA package. Verify all inputs, factors and results independently.
';
}
function collect() {
var o = { application: 'Control Valve Sizing Simulator', version: '1.0', exportedAt: new Date().toISOString(), inputs: {} };
ids.forEach(function (id) { o.inputs[id] = $(id).value; });
o.lastCalculation = last; o.operatingCases = lastCases; return o;
}
function apply(o) {
var s = (o && o.inputs) || o || {};
ids.forEach(function (id) { if (s[id] !== undefined && s[id] !== null && typeof s[id] !== 'object') $(id).value = String(s[id]); });
phaseUI();
}
function download(name, content, type) {
var url = URL.createObjectURL(new Blob([content], { type: type })), a = document.createElement('a');
a.href = url; a.download = name; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url);
}
function csvCell(v) { v = String(v == null ? '' : v); if (/^[=+\-@]/.test(v)) v = "'" + v; return '"' + v.replace(/"/g, '""') + '"'; }
function base() { return ($('tag').value || 'control-valve').replace(/[^\w\-]+/g, '_'); }
function resetAll() {
ids.forEach(function (id) { $(id).value = defaults[id]; });
last = null; lastCases = []; marks = { main: null, cases: [] };
['reqCv', 'reqKv', 'ratio', 'trv', 'dp', 'regime', 'cav', 'vel'].forEach(function (id) { txt(id, '—'); });
CASES.forEach(function (c, i) { ['cv', 'r', 't', 's'].forEach(function (k) { $('c' + i + k).textContent = '—'; }); });
$('validation').textContent = 'Enter data and select Calculate sizing.';
$('warns').innerHTML = 'Run a calculation to populate checks.
';
$('inter').innerHTML = 'Not yet calculated ';
$('reportText').innerHTML = 'Report preview Run a calculation to generate the report.
';
phaseUI(); draw();
}
/* ---------- events ---------- */
$('toValve').addEventListener('click', function () { tab('valve'); });
$('toResults').addEventListener('click', function () { tab('results'); });
$('calc').addEventListener('click', function () { if (calcMain()) tab('results'); });
$('calcCases').addEventListener('click', calcCases);
$('json').addEventListener('click', function () { download(base() + '-project.json', JSON.stringify(collect(), null, 2), 'application/json'); });
$('csv').addEventListener('click', function () {
if (!last) { alert('Run sizing first.'); return; }
var L = [['Parameter', 'Value'], ['Project', $('project').value], ['Tag', $('tag').value], ['Phase', last.ph], ['Flow', last.q], ['P1 bara', last.p1], ['P2 bara', last.p2], ['dP bar', last.dp], ['Required Cv', last.cv], ['Required Kv', last.kv], ['Rated Cv', last.rated], ['Rated/required', last.ratio], ['Travel %', last.t], ['Regime', last.regime], ['Cavitation/flashing', last.cav]];
lastCases.forEach(function (r) { if (r) L.push(['Case ' + r.name + ' flow / P1 / P2 / Cv / travel%', [r.q, r.p1, r.p2, r.cv, r.t].join(' / ')]); });
L.push(['Warning', 'Preliminary model; verify independently']);
download(base() + '-results.csv', L.map(function (row) { return row.map(csvCell).join(','); }).join('\r\n'), 'text/csv');
});
$('print').addEventListener('click', function () { if (!last) calcMain(); tab('report'); window.print(); });
$('save').addEventListener('click', function () { try { localStorage.setItem('cvs-valve-save', JSON.stringify(collect())); alert('Saved in this browser.'); } catch (e) { alert('Browser storage unavailable. Use JSON export.'); } });
$('load').addEventListener('click', function () {
try { var s = localStorage.getItem('cvs-valve-save'); if (!s) { alert('No browser save found.'); return; } apply(JSON.parse(s)); calcMain(); }
catch (e) { alert('Could not load the saved project.'); }
});
$('reset').addEventListener('click', function () { if (confirm('Reset all inputs to the example values?')) resetAll(); });
$('file').addEventListener('change', function (ev) {
var f = ev.target.files[0]; if (!f) return;
var rd = new FileReader();
rd.onload = function () { try { apply(JSON.parse(rd.result)); calcMain(); alert('Project imported. Review the inputs and warnings.'); } catch (e) { alert('Invalid project JSON.'); } };
rd.readAsText(f);
});
root.querySelectorAll('input,select').forEach(function (el) {
if (el.type === 'file') return;
el.addEventListener('change', function () { phaseUI(); if (last) calcMain(); if (lastCases.length) calcCases(); });
});
phaseUI(); draw();
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init();
})();
Skip to content
RTD types used in industry are listed above, the main difference between them is the resistance output at the ice point. As you see in the name the number indicates the ohms measured at 0 °C.
Featured posts
Gated SR Latch S (Set) R (Reset) E (Enable) Q: OFF NOT Q: ON Welcome to the SR Latch Gated