Files
L007/GB32064Calculator - 副本趋于良好导热系数干湿满意比热有时候低.cs
2026-01-08 21:12:15 +08:00

659 lines
26 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using MathNet.Numerics.Interpolation;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConductivityApp.GBStandard
{
/// <summary>
/// GB/T 32064-2015 标准计算器(严格遵循国标)
/// 瞬态平面热源测试法计算导热系数和热扩散系数
/// </summary>
public class GB32064Calculator
{
#region
public struct BridgeConfig
{
public double Rs { get; set; } // 串联电阻 (Ω)
public double RL { get; set; } // 引线电阻 (Ω)
}
public struct ProbeConfig
{
public double R0 { get; set; } // 初始电阻 (Ω)
public double Alpha { get; set; } // 电阻温度系数 (1/K)
public double RadiusMM { get; set; } // 探头半径 (mm)
public int CoilCount { get; set; } // 探头环数
}
public struct TestConfig
{
public double P0 { get; set; } // 输出功率 (W)
public double SampleDensity { get; set; } // 样品密度 (kg/m³)
}
public struct CalculationResult
{
public bool IsValid { get; set; }
public double ThermalConductivity { get; set; } // λ - W/(m·K)
public double ThermalDiffusivity { get; set; } // α - m²/s
public double SpecificHeatCapacity { get; set; } // Cp - J/(kg·K)
public double CorrectionTime { get; set; } // tc - s
public double RSquared { get; set; }
public string ValidationMessage { get; set; }
}
#endregion
#region GB/T 32064-2015
private const double TAU_MAX_SQUARED_LOWER = 0.3; // τ_max²的最小值国标5.3.4要求)
private const double TAU_MAX_SQUARED_UPPER = 1.0; // τ_max²的最大值国标5.3.4要求)
private const double PROBING_DEPTH_RATIO_LOWER = 1.1; // 探测深度/探头半径最小值
private const double PROBING_DEPTH_RATIO_UPPER = 2.0; // 探测深度/探头半径最大值
private const double MIN_DATA_COUNT = 100; // 最小数据点数国标5.3.3.3
private const double MIN_TIME_INTERVAL = 0.1; // 最小时间间隔(s)国标5.3.3.3
private double PI_POW_1_5 = Math.Pow(Math.PI, 1.5); // π^(3/2)
#endregion
#region
private readonly BridgeConfig _bridge;
private readonly ProbeConfig _probe;
private readonly TestConfig _test;
private IInterpolation _tauDInterpolation;
#endregion
#region
public GB32064Calculator(BridgeConfig bridge, ProbeConfig probe, TestConfig test)
{
_bridge = bridge;
_probe = probe;
_test = test;
ValidateConfigurations();
InitializeTauDTable();
}
#endregion
#region
private void ValidateConfigurations()
{
var errors = new List<string>();
if (_probe.R0 <= 0) errors.Add("探头初始电阻R0必须大于0");
if (_probe.Alpha <= 0) errors.Add("探头温度系数α必须大于0");
if (_probe.RadiusMM <= 0) errors.Add("探头半径必须大于0");
if (_probe.CoilCount < 1) errors.Add("探头环数应大于0");
if (_bridge.Rs <= 0) errors.Add("串联电阻Rs必须大于0");
if (_bridge.RL < 0) errors.Add("引线电阻RL不能为负");
if (_test.P0 <= 0) errors.Add("输出功率P0必须大于0");
if (_test.SampleDensity <= 0) errors.Add("样品密度必须大于0");
if (errors.Count > 0)
{
throw new ArgumentException($"配置验证失败:\n{string.Join("\n", errors)}");
}
}
#endregion
#region
public CalculationResult Calculate(double[] timeArray, double[] deltaUArray, double currentMA = 120.0)
{
try
{
// 1. 数据验证
if (!ValidateInputData(timeArray, deltaUArray))
{
return InvalidResult("输入数据验证失败");
}
double I0 = currentMA / 1000.0; // mA转A
// 2. 计算ΔT(t) - 国标公式(3)
double[] deltaT = CalculateTemperatureIncrements(deltaUArray, I0);
// 3. 迭代求解热扩散系数α和校正时间tc
var (alpha, tc) = IterateThermalDiffusivity(timeArray, deltaT);
// 4. 验证τ_max²是否符合国标要求
double r = _probe.RadiusMM / 1000.0;
double tmax = timeArray.Max();
double tauMaxSquared = CalculateTauMaxSquared(tmax, tc, alpha, r);
if (tauMaxSquared < TAU_MAX_SQUARED_LOWER || tauMaxSquared > TAU_MAX_SQUARED_UPPER)
{
return InvalidResult($"τ_max²({tauMaxSquared:F3})不在国标要求范围[{TAU_MAX_SQUARED_LOWER}, {TAU_MAX_SQUARED_UPPER}]内,测试无效");
}
// 5. 计算导热系数λ - 国标公式(4)
double lambda = CalculateThermalConductivity(timeArray, deltaT, alpha, tc);
// 6. 计算比热容Cp - Cp = λ / (ρ × α)
double cp = CalculateSpecificHeatCapacity(lambda, alpha);
// 7. 验证测试结果有效性
var validation = ValidateTestResults(timeArray, alpha, tc, tauMaxSquared);
// 8. 计算R²
double rSquared = CalculateRSquared(timeArray, deltaT, alpha, tc, lambda);
return new CalculationResult
{
IsValid = validation.IsValid,
ThermalConductivity = lambda,
ThermalDiffusivity = alpha,
SpecificHeatCapacity = cp,
CorrectionTime = tc,
RSquared = rSquared,
ValidationMessage = string.Join("\n", validation.Messages)
};
}
catch (Exception ex)
{
return InvalidResult($"计算失败: {ex.Message}");
}
}
/// <summary>
/// 计算温度增量ΔT(t) - 国标公式(3)
/// </summary>
private double[] CalculateTemperatureIncrements(double[] deltaUArray, double I0)
{
double[] deltaT = new double[deltaUArray.Length];
for (int i = 0; i < deltaUArray.Length; i++)
{
double deltaU = deltaUArray[i];
// 国标公式(3): ΔT(t) = (Rs + RL + R0) × ΔU(t) / [(I0 × Rs - ΔU(t)) × α × R0]
double numerator = (_bridge.Rs + _bridge.RL + _probe.R0) * deltaU;
double denominator = (I0 * _bridge.Rs - deltaU) * _probe.Alpha * _probe.R0;
if (Math.Abs(denominator) < 1e-12)
{
throw new InvalidOperationException($"分母接近0无法计算ΔT(t)");
}
deltaT[i] = numerator / denominator;
}
return deltaT;
}
/// <summary>
/// 迭代求解热扩散系数α和校正时间tc
/// </summary>
private (double alpha, double tc) IterateThermalDiffusivity(double[] timeArray, double[] deltaT)
{
// 初始猜测值
double bestAlpha = 1e-6; // 常见建筑材料的热扩散系数
//double bestAlpha = 3.58e-7; // 常见建筑材料的热扩散系数
double bestTc = timeArray.Max() * 0.01; // 测试时间的1%作为初始校正时间
double bestError = double.MaxValue;
// 使用最小二乘法迭代优化
int maxIterations = 200;
double convergenceThreshold = 1e-8;
for (int iteration = 0; iteration < maxIterations; iteration++)
{
double alphaStep = bestAlpha * 0.1 / (iteration + 1);
double tcStep = 0.001 / (iteration + 1);
var candidates = new List<(double alpha, double tc, double error)>
{
(bestAlpha, bestTc, CalculateFitError(timeArray, deltaT, bestAlpha, bestTc)),
(bestAlpha + alphaStep, bestTc, CalculateFitError(timeArray, deltaT, bestAlpha + alphaStep, bestTc)),
(bestAlpha - alphaStep, bestTc, CalculateFitError(timeArray, deltaT, bestAlpha - alphaStep, bestTc)),
(bestAlpha, bestTc + tcStep, CalculateFitError(timeArray, deltaT, bestAlpha, bestTc + tcStep)),
(bestAlpha, bestTc - tcStep, CalculateFitError(timeArray, deltaT, bestAlpha, bestTc - tcStep))
};
var bestCandidate = candidates.OrderBy(c => c.error).First();
double improvement = bestError - bestCandidate.error;
if (improvement > 0)
{
bestAlpha = bestCandidate.alpha;
bestTc = bestCandidate.tc;
bestError = bestCandidate.error;
}
// 收敛条件
if (Math.Abs(improvement) < convergenceThreshold && iteration > 20)
{
break;
}
}
return (bestAlpha, bestTc);
}
/// <summary>
/// 计算拟合误差
/// </summary>
private double CalculateFitError(double[] timeArray, double[] deltaT, double alpha, double tc)
{
double totalError = 0;
int count = 0;
double r = _probe.RadiusMM / 1000.0;
for (int i = 0; i < timeArray.Length; i++)
{
if (timeArray[i] > tc)
{
double tau = CalculateTau(timeArray[i], tc, alpha, r);
if (tau >= TAU_MAX_SQUARED_LOWER && tau <= TAU_MAX_SQUARED_UPPER)
{
try
{
double dTau = _tauDInterpolation.Interpolate(tau);
// 理论ΔT = [P0 / (π^(3/2) × r × λ)] × D(τ)
// 这里假设λ=1因为我们只关心相对误差
double theoreticalDeltaT = _test.P0 * dTau / (PI_POW_1_5 * r);
double error = Math.Pow(deltaT[i] - theoreticalDeltaT, 2);
totalError += error;
count++;
}
catch
{
continue;
}
}
}
}
return count > 0 ? Math.Sqrt(totalError / count) : double.MaxValue;
}
/// <summary>
/// 计算导热系数λ - 国标公式(4),使用国标范围内的数据
/// </summary>
private double CalculateThermalConductivity(double[] timeArray, double[] deltaT, double alpha, double tc)
{
double r = _probe.RadiusMM / 1000.0;
var validPoints = new List<(double dTau, double deltaT)>();
// 收集有效数据点(严格按国标τ范围筛选)
int validCount = 0;
int totalCount = 0;
for (int i = 0; i < timeArray.Length; i++)
{
totalCount++;
// 只使用校正后的数据t > t_c
if (timeArray[i] > tc)
{
double tau = CalculateTau(timeArray[i], tc, alpha, r);
// 只使用τ在国标要求范围内的数据点
if (tau >= TAU_MAX_SQUARED_LOWER && tau <= TAU_MAX_SQUARED_UPPER)
{
try
{
double dTau = _tauDInterpolation.Interpolate(tau);
validPoints.Add((dTau, deltaT[i]));
validCount++;
}
catch
{
continue;
}
}
}
}
if (validPoints.Count < 10)
{
throw new InvalidOperationException(
$"有效数据点不足({validPoints.Count}),无法计算导热系数\n" +
$"国标要求:在τ范围[{TAU_MAX_SQUARED_LOWER}, {TAU_MAX_SQUARED_UPPER}]内应有足够数据点");
}
// 线性回归ΔT(τ) = [P₀ / (π^(3/2) × r × λ)] × D(τ)
double[] dTauArray = validPoints.Select(p => p.dTau).ToArray();
double[] deltaTArray = validPoints.Select(p => p.deltaT).ToArray();
var (slope, intercept) = LinearRegression(dTauArray, deltaTArray);
// 检查斜率是否合理
if (Math.Abs(slope) < 1e-12)
{
throw new InvalidOperationException("回归斜率接近0计算结果不可靠");
}
// 计算导热系数 λ = P₀ / (π^(3/2) × r × slope) - 国标公式(4)
double lambda = _test.P0 / (PI_POW_1_5 * r * slope);
return lambda;
}
/// <summary>
/// 计算无量纲时间τ - 国标公式(5)
/// τ = √[(t - t_c) / (r²/a)]
/// 等价于:τ = √[(t - t_c) * a / r²]
/// </summary>
private double CalculateTau(double time, double tc, double alpha, double r)
{
if (time <= tc)
{
return 0;
}
// 国标公式(5): τ = √[(t - t_c) / (r²/a)]
double theta = (r * r) / alpha; // 特征时间 θ = r²/a
double tau = Math.Sqrt((time - tc) / theta);
return tau;
}
/// <summary>
/// 计算比热容Cp - Cp = λ / (ρ × α)
/// </summary>
private double CalculateSpecificHeatCapacity(double lambda, double alpha)
{
if (alpha <= 0 || _test.SampleDensity <= 0)
{
throw new InvalidOperationException("热扩散系数或密度无效,无法计算比热容");
}
double cp = lambda / (_test.SampleDensity * alpha);
return cp;
}
/// <summary>
/// 验证测试结果 - 严格遵循国标5.3.4节要求
/// </summary>
private ValidationResult ValidateTestResults(double[] timeArray, double alpha, double tc, double tauMaxSquared)
{
var result = new ValidationResult { IsValid = true };
double r = _probe.RadiusMM / 1000.0; // 转换为米
double tmax = timeArray.Max();
// 1. 验证τ_max²是否满足国标要求
if (tauMaxSquared < TAU_MAX_SQUARED_LOWER)
{
result.IsValid = false;
result.Messages.Add($"τ_max²({tauMaxSquared:F3}) < {TAU_MAX_SQUARED_LOWER},测试时间不足,结果无效");
}
else if (tauMaxSquared > TAU_MAX_SQUARED_UPPER)
{
result.IsValid = false;
result.Messages.Add($"τ_max²({tauMaxSquared:F3}) > {TAU_MAX_SQUARED_UPPER},测试时间过长,结果无效");
}
else
{
result.Messages.Add($"τ_max²({tauMaxSquared:F3})满足国标要求[{TAU_MAX_SQUARED_LOWER}, {TAU_MAX_SQUARED_UPPER}]");
}
// 2. 计算探测深度 ΔP_probe = 2√(a·t_max) 国标5.3.4
double probingDepth = 2 * Math.Sqrt(alpha * tmax);
double probingDepthRatio = probingDepth / r;
// 3. 验证探测深度是否满足国标要求
if (probingDepthRatio < PROBING_DEPTH_RATIO_LOWER)
{
result.IsValid = false;
result.Messages.Add($"探测深度/探头半径({probingDepthRatio:F2}) < {PROBING_DEPTH_RATIO_LOWER},样品厚度可能不足");
}
else if (probingDepthRatio > PROBING_DEPTH_RATIO_UPPER)
{
result.IsValid = false;
result.Messages.Add($"探测深度/探头半径({probingDepthRatio:F2}) > {PROBING_DEPTH_RATIO_UPPER},可能超出测试范围");
}
else
{
result.Messages.Add($"探测深度/探头半径({probingDepthRatio:F2})满足国标要求[{PROBING_DEPTH_RATIO_LOWER}, {PROBING_DEPTH_RATIO_UPPER}]");
}
// 4. 验证数据点数量国标5.3.3.3要求采集次数大于100次
if (timeArray.Length < MIN_DATA_COUNT)
{
result.Messages.Add($"警告:采集次数({timeArray.Length})少于国标要求的{MIN_DATA_COUNT}次");
}
else
{
result.Messages.Add($"采集次数({timeArray.Length})满足国标要求(≥{MIN_DATA_COUNT}次)");
}
// 5. 验证数据采集间隔国标5.3.3.3要求不小于0.1s
if (timeArray.Length > 1)
{
double minInterval = timeArray[1] - timeArray[0];
for (int i = 2; i < timeArray.Length; i++)
{
double interval = timeArray[i] - timeArray[i - 1];
if (interval < minInterval) minInterval = interval;
}
if (minInterval < MIN_TIME_INTERVAL)
{
result.Messages.Add($"警告:数据采集间隔({minInterval:F3}s)小于国标要求的{MIN_TIME_INTERVAL}s");
}
else
{
result.Messages.Add($"数据采集间隔({minInterval:F3}s)满足国标要求(≥{MIN_TIME_INTERVAL}s)");
}
}
// 6. 生成详细验证报告
result.Messages.Insert(0, $"【国标GB/T 32064-2015测试结果有效性验证】");
result.Messages.Insert(1, $"测试总时间: {tmax:F2}s校正时间: {tc:F4}s");
result.Messages.Insert(2, $"热扩散系数α: {alpha:E6} m²/s");
result.Messages.Insert(3, $"探头半径r: {r * 1000:F2}mm探测深度: {probingDepth * 1000:F2}mm");
result.Messages.Insert(4, $"验证依据5.3.4节 测试结果有效性验证");
return result;
}
/// <summary>
/// 计算τ_max²
/// </summary>
private double CalculateTauMaxSquared(double tmax, double tc, double alpha, double r)
{
// 国标公式τ_max² = (t_max - t_c) × a / r²
if (r <= 0) throw new ArgumentException("探头半径必须大于0");
if (tmax <= tc) return 0;
double tauMaxSquared = (tmax - tc) * alpha / (r * r);
return tauMaxSquared;
}
/// <summary>
/// 计算R²拟合优度
/// </summary>
private double CalculateRSquared(double[] timeArray, double[] deltaT, double alpha, double tc, double lambda)
{
var observed = new List<double>();
var predicted = new List<double>();
double r = _probe.RadiusMM / 1000.0;
for (int i = 0; i < timeArray.Length; i++)
{
if (timeArray[i] > tc)
{
double tau = CalculateTau(timeArray[i], tc, alpha, r);
if (tau >= TAU_MAX_SQUARED_LOWER && tau <= TAU_MAX_SQUARED_UPPER)
{
try
{
double dTau = _tauDInterpolation.Interpolate(tau);
double theoretical = _test.P0 * dTau / (PI_POW_1_5 * r * lambda);
observed.Add(deltaT[i]);
predicted.Add(theoretical);
}
catch
{
continue;
}
}
}
}
if (observed.Count < 2) return 0;
double meanObserved = observed.Average();
double ssTotal = observed.Sum(o => Math.Pow(o - meanObserved, 2));
double ssResidual = observed.Zip(predicted, (o, p) => Math.Pow(o - p, 2)).Sum();
if (Math.Abs(ssTotal) < 1e-12) return 0;
double rSquared = 1 - (ssResidual / ssTotal);
return rSquared;
}
#endregion
#region
private CalculationResult InvalidResult(string message)
{
return new CalculationResult
{
IsValid = false,
ValidationMessage = message,
ThermalConductivity = 0,
ThermalDiffusivity = 0,
SpecificHeatCapacity = 0,
CorrectionTime = 0,
RSquared = 0
};
}
private bool ValidateInputData(double[] timeArray, double[] deltaUArray)
{
if (timeArray == null || deltaUArray == null)
{
throw new ArgumentNullException("时间和电压数据不能为空");
}
if (timeArray.Length != deltaUArray.Length)
{
throw new ArgumentException("时间和电压数据长度不一致");
}
if (timeArray.Length < 20)
{
throw new ArgumentException($"数据点数量({timeArray.Length})不足至少需要20个点");
}
// 检查时间单调递增
for (int i = 1; i < timeArray.Length; i++)
{
if (timeArray[i] <= timeArray[i - 1])
{
throw new ArgumentException($"时间数据必须严格单调递增,第{i}点不符合要求");
}
}
return true;
}
private (double slope, double intercept) LinearRegression(double[] x, double[] y)
{
if (x.Length != y.Length || x.Length < 2)
throw new ArgumentException("线性回归需要至少2个数据点且x、y长度相等");
double xAvg = x.Average();
double yAvg = y.Average();
double numerator = 0;
double denominator = 0;
for (int i = 0; i < x.Length; i++)
{
numerator += (x[i] - xAvg) * (y[i] - yAvg);
denominator += (x[i] - xAvg) * (x[i] - xAvg);
}
if (Math.Abs(denominator) < 1e-12)
throw new InvalidOperationException("数据方差太小,无法进行线性回归");
double slope = numerator / denominator;
double intercept = yAvg - slope * xAvg;
return (slope, intercept);
}
#endregion
#region τ-D(τ)
private void InitializeTauDTable()
{
// 使用国标理论公式计算 D(τ) = (√(τ²+1) - 1) / τ
var tauList = new List<double>();
var dList = new List<double>();
// 根据国标要求τ范围通常为0.01-3.0
for (double tau = 0.01; tau <= 3.0; tau += 0.01)
{
tauList.Add(tau);
double d = (Math.Sqrt(tau * tau + 1.0) - 1.0) / tau;
dList.Add(d);
}
double[] tauValues = tauList.ToArray();
double[] dValues = dList.ToArray();
_tauDInterpolation = CubicSpline.InterpolateAkimaSorted(tauValues, dValues);
}
#endregion
#region
private class ValidationResult
{
public bool IsValid { get; set; }
public List<string> Messages { get; } = new List<string>();
}
#endregion
#region
/// <summary>
/// 用于土壤测试的参数设置建议(仅建议,不影响计算)
/// </summary>
public static class SoilTestRecommendations
{
/// <summary>
/// 根据土壤类型获取建议的测试参数
/// </summary>
/// <param name="soilType">土壤类型(干土、湿土、冻土等)</param>
/// <returns>建议的测试时间范围</returns>
public static (double minTime, double maxTime) GetRecommendedTestTime(string soilType)
{
// 土壤热扩散系数典型范围1e-7 到 1e-6 m²/s
// 使用典型探头半径6.4mm计算
double r = 0.0064; // 米
// 根据τ_max² = (t * a) / r²反推时间
// t = τ_max² * r² / a
// 对于导热系数较低的土壤(干土、冻土)
if (soilType.Contains("冻土") || soilType.Contains("干土"))
{
double alpha = 0.5e-6; // 较低的热扩散系数
double t_min = TAU_MAX_SQUARED_LOWER * r * r / alpha;
double t_max = TAU_MAX_SQUARED_UPPER * r * r / alpha;
return (t_min, t_max);
}
// 对于导热系数较高的土壤(湿土)
else if (soilType.Contains("湿土"))
{
double alpha = 1.2e-6; // 较高的热扩散系数
double t_min = TAU_MAX_SQUARED_LOWER * r * r / alpha;
double t_max = TAU_MAX_SQUARED_UPPER * r * r / alpha;
return (t_min, t_max);
}
// 默认值
else
{
double alpha = 1e-6; // 典型热扩散系数
double t_min = TAU_MAX_SQUARED_LOWER * r * r / alpha;
double t_max = TAU_MAX_SQUARED_UPPER * r * r / alpha;
return (t_min, t_max);
}
}
}
#endregion
}
}