The control valve flow coefficient (Cv) calculation looks simple. The implementation details — multi-point averaging, Reynolds number correction, characteristic curve fitting — are where the engineering lives.
The Core Cv Formula
import numpy as np
def calculate_cv(flow_gpm, delta_p_psi, specific_gravity=1.0):
"""
Calculate control valve flow coefficient Cv
per ISA 75.01 / ISA 75.02
flow_gpm: volumetric flow rate in US gallons per minute
delta_p_psi: differential pressure across valve in psi
specific_gravity: fluid SG relative to water at 60°F (default 1.0 for water)
"""
if delta_p_psi <= 0:
raise ValueError("Differential pressure must be positive")
cv = flow_gpm * np.sqrt(specific_gravity / delta_p_psi)
return cv
def cv_to_kv(cv):
"""Convert Cv (US) to Kv (SI metric) per IEC 60534"""
return cv * 0.865
def kv_to_cv(kv):
"""Convert Kv (SI) to Cv (US)"""
return kv / 0.865
Multi-Point Cv Measurement (ISA 75.02)
ISA 75.02 requires averaging multiple readings at each test point:
def measure_cv_at_position(flow_readings, dp_readings, sg=1.0, n_avg=10):
"""
ISA 75.02 compliant Cv measurement at one valve position.
Takes n_avg readings and averages for stability.
Returns: mean Cv, standard deviation, coefficient of variation
"""
assert len(flow_readings) == len(dp_readings) == n_avg
cv_values = [calculate_cv(q, dp, sg)
for q, dp in zip(flow_readings, dp_readings)]
mean_cv = np.mean(cv_values)
std_cv = np.std(cv_values)
cov = std_cv / mean_cv * 100 # coefficient of variation %
# ISA 75.02: readings should be within +-2% of mean
outliers = [cv for cv in cv_values if abs(cv - mean_cv)/mean_cv > 0.02]
if outliers:
print(f"Warning: {len(outliers)} readings outside +-2% tolerance")
return {'mean_cv': mean_cv, 'std_cv': std_cv, 'cov_pct': cov}
Characteristic Curve Generation
def generate_characteristic_curve(travel_positions, cv_measured, cv_max):
"""
Generate normalised valve characteristic curve.
travel_positions: list of % open (0-100)
cv_measured: Cv at each position
cv_max: Cv at 100% open
Returns: characteristic type and R^2 fit quality
"""
from scipy.optimize import curve_fit
from scipy.stats import pearsonr
travel = np.array(travel_positions) / 100 # normalise 0-1
cv_norm = np.array(cv_measured) / cv_max # normalise 0-1
# Linear characteristic: Cv/Cvmax = x
linear_residuals = cv_norm - travel
r2_linear = 1 - np.var(linear_residuals) / np.var(cv_norm)
# Equal percentage: Cv/Cvmax = R^(x-1) where R is rangeability
def eq_pct(x, R):
return R ** (x - 1)
try:
popt, _ = curve_fit(eq_pct, travel[1:], cv_norm[1:], p0=[50])
R_fitted = popt[0]
eq_pct_fitted = eq_pct(travel, R_fitted)
r2_eqpct = 1 - np.var(cv_norm - eq_pct_fitted) / np.var(cv_norm)
except:
r2_eqpct = 0
R_fitted = None
if r2_linear > r2_eqpct and r2_linear > 0.95:
char_type = "Linear"
fit_quality = r2_linear
elif r2_eqpct > 0.95:
char_type = f"Equal Percentage (R={R_fitted:.1f})"
fit_quality = r2_eqpct
else:
char_type = "Non-standard / Quick-opening"
fit_quality = max(r2_linear, r2_eqpct)
return {
'characteristic': char_type,
'r2': fit_quality,
'rangeability': R_fitted,
'travel': travel_positions,
'cv_normalised': (cv_norm * 100).tolist()
}
Seat Leakage Test — ANSI/FCI 70-2
def evaluate_seat_leakage(leakage_flow_gpm, rated_cv, test_dp_psi, leakage_class):
"""
Evaluate seat leakage per ANSI/FCI 70-2
leakage_class: 'II', 'III', 'IV', 'V', or 'VI'
Returns: pass/fail and allowable leakage limit
"""
limits = {
'II': rated_cv * 0.005,
'III': rated_cv * 0.001,
'IV': rated_cv * 0.0001,
}
if leakage_class in ['II', 'III', 'IV']:
max_leakage = limits[leakage_class] * np.sqrt(test_dp_psi / 1.0)
result = leakage_flow_gpm <= max_leakage
return {
'pass': result,
'measured_gpm': leakage_flow_gpm,
'limit_gpm': max_leakage,
'class': leakage_class
}
elif leakage_class == 'VI':
max_bubbles_per_min = 0.18 # mL/min for 2" port
measured_ml_per_min = leakage_flow_gpm * 3785.41
result = measured_ml_per_min <= max_bubbles_per_min
return {'pass': result, 'class': 'VI', 'measured_mL_min': measured_ml_per_min}
The Neometrix CV and Control Valve Test Rig implements this measurement chain — from raw sensor data through ISA 75.02 averaging, characteristic curve generation, and ANSI/FCI 70-2 leakage classification — with automated report output for each tested valve.
→ https://neometrixgroup.com/products/cv-and-control-valve-test-rig
Top comments (0)