本文主要分享了安德森的《计算流体力学基础与应用》一书中:“全亚声速等熵喷管流动的CFD解法 — 守恒型方程”的程序求解。水平有限,请见谅!
亚声速——超声速等熵喷管流动的CFD解法的非守恒型方程程序见之前的笔者的博客:
《计算流体力学基础与应用 》- 约翰 D. 安德森
《Computational Fluid Dynamics》- John D. Anderson
第 7 章 拟一维喷管流动的数值解
亚声速——超声速等熵喷管流动的CFD解法(非守恒型方程)
%%
% Author: CXT
% Date: 2021/4/20
% Theme: 全亚声速等熵喷管流动
%% 物理符号(无量纲量);
% x —— 空间位置;
% A —— 喷管面积;
% rho —— 密度;
% V —— 速度;
% p —— 压力;
% N —— 计算节点数;
%%
clc;
clear;
close all;
%% 初始条件;
N = 31;% 计算节点数;
x = 0:(3-0)/(N-1):3; % 计算区间;
n1 = 1200; %时间步数;
n2 = 400;
rho0 = 1 - 0.023*x;
T0 = 1 - 0.009333*x;
V0 = 0.05 + 0.11*x;
%% 喷管形状;
A = (1 + 2.2*(x - 1.5).^2).*(x<=1.5) + (1 + 0.2223*(x - 1.5).^2).*(x > 1.5);
A = A./1;
%% 出口与入口压力之比;
f = 0.9;
%% 计算与绘图;
rho_t = rho0;
T_t = T0;
V_t = V0;
tic;
for i = 1:n1
[rho_t, V_t, T_t, p_t, Ma] = OneStepNozzle(rho_t, V_t, T_t, N, x, A, f);
if (i == n2)
Q1 = rho_t.*A.*V_t;% 500步的质量流量;
p1 = p_t;
elseif (i == n1)
Q2 = rho_t.*A.*V_t;% 5000步的质量流量;
p2 = p_t;
end
end
toc
% 不同时刻质量流量的变化
Q0 = rho0.*A.*V0;% 初始质量流量;
figure(1);
plot(x, Q1,'r');% 500步的质量流量;
hold on;
plot(x, Q2, 'g');% 5000步的质量流量;
plot(x, Q0,'b');% 初始质量流量;
title("不同时刻质量流量的变化");
hold off;
% 不同时刻压力分布的变化;
p0 = rho0.*T0;
figure(2);
plot(x, p1, 'r');
hold on;
plot(x, p2, 'g');
plot(x, p0, 'b');
title("不同时刻压力分布的变化");
hold off;
function [rho_t, V_t, T_t, p_t, Ma] = OneStepNozzle(rho, V, T, N, X, A, f)
%[rho_t, V_t, T_t, p_t, Ma] = OneStepNozzle(rho, V, T, N, X, A, f) 拟一维喷管流动的一个时间步长的计算
% rho —— 初始密度;
% V —— 初始速度;
% T —— 初始温度;
% N —— 计算节点数;
% X —— 计算区间;
% A —— 喷管形状参数;
% f —— 出口压力与入口压力之比;
% rho_t —— 下一步的密度;
% V_t —— 下一步的速度;
% T_t —— 下一步的温度;
% p_t —— 下一步的压力;
% Ma —— 马赫数;
%% 默认参数;
Delta_x = (X(end)-X(1))/(N - 1); %空间步长;
gama = 1.4; %绝热指数;
C = 0.5; %柯朗数;
%% 预估步计算;
% 向前差分;
% 只计算内点;
partial_rho = zeros(1, 3/Delta_x + 1);
partial_V = zeros(1, 3/Delta_x + 1);
partial_T = zeros(1, 3/Delta_x + 1);
for i = 1:N - 1
partial_rho(i) = - V(i)*(rho(i+1) - rho(i))/Delta_x...
- rho(i)*(V(i+1) - V(i))/Delta_x...
- rho(i)*V(i)*(log(A(i+1)) - log(A(i)))/Delta_x;
partial_V(i) = - V(i)*(V(i+1) - V(i))/Delta_x...
- 1/gama*((T(i+1) - T(i))/Delta_x...
+ T(i)/rho(i)*(rho(i+1)-rho(i))/Delta_x);
partial_T(i) = - V(i)*(T(i+1) - T(i))/Delta_x...
- (gama - 1)*T(i)*((V(i+1) - V(i))/Delta_x...
+ V(i)*(log(A(i+1)) - log(A(i)))/Delta_x);
end
%% 计算时间步长;
Delta_t = min(C * Delta_x ./ (V + sqrt(T)));
%% 计算预估值
bar_rho = rho + partial_rho * Delta_t;
bar_V = V + partial_V * Delta_t;
bar_T = T + partial_T * Delta_t;
%% 计算校正步;
% 向后差分;
% 只计算内点;
bar_partial_rho = zeros(1, 3/Delta_x + 1);
bar_partial_V = zeros(1, 3/Delta_x + 1);
bar_partial_T = zeros(1, 3/Delta_x + 1);
for i = 2:N-1
bar_partial_rho(i) = - bar_V(i)*(bar_rho(i) - bar_rho(i-1))/Delta_x...
- bar_rho(i)*(bar_V(i) - bar_V(i-1))/Delta_x...
- bar_rho(i)*bar_V(i)*(log(A(i)) - log(A(i-1)))/Delta_x;
bar_partial_V(i) = - bar_V(i)*(bar_V(i) - bar_V(i-1))/Delta_x...
- ((bar_T(i) - bar_T(i-1))/Delta_x...
+ bar_T(i)*(bar_rho(i) - bar_rho(i-1))/(bar_rho(i)*Delta_x))/gama;
bar_partial_T(i) = - bar_V(i)*(bar_T(i) - bar_T(i-1))/Delta_x...
- (gama - 1)*bar_T(i)*((bar_V(i) - bar_V(i-1))/Delta_x...
+ bar_V(i)*(log(A(i)) - log(A(i-1)))/Delta_x);
end
%% 计算平均时间导数;
partial_rho_av = (partial_rho + bar_partial_rho)/2;
partial_V_av = (partial_V + bar_partial_V)/2;
partial_T_av = (partial_T + bar_partial_T)/2;
%% 校正值;
% t + Delta_t 时的参数;
rho_t = rho + partial_rho_av*Delta_t;
V_t = V + partial_V_av*Delta_t;
T_t = T + partial_T_av*Delta_t;
p_t = rho_t.*T_t;
%% 边界点处的流场变量;
% 入流边界;
rho_t(1) = 1; % 滞止参数;
V_t(1) = 2*V_t(2) - V_t(3); % 允许边界上的 V 是可变化的;
T_t(1) = 1; % 滞止参数;
p_t(1) = rho_t(1)*T_t(1); % 滞止参数;
% 出流边界;
p_t(N) = f; %%f是给定值;
T_t(N) = 2*T_t(N-1) - T_t(N-2);
rho_t(N) = f/T_t(N);
V_t(N) = 2*V_t(N-1) - V_t(N-2);% 允许边界上的V是可变化的;
%% 马赫数
Ma = V_t./sqrt(T_t);
end
#pragma once
#include
#include
#include //setprecision(3)
using namespace std;
class Nozzle
{
private:
double X_first;
double X_end;//计算区间[X_first, X_end];
double* A; //喷管面积;
double* rho;//密度;
double* T;//温度;
double* V;//速度;
int N; //计算节点数;
int iterations;//迭代次数;
double F;//出口与入口压力之比;
protected:
double* p;//压力;
double* Ma;//马赫数;
double Delta_x;// 空间步长;
double Delta_t;//时间步长;
double gama;// 绝热指数;
double C;// 柯朗数;
中间变量
double* partial_rho;
double* partial_V;
double* partial_T;
double* bar_rho;
double* bar_V;
double* bar_T;
double* bar_partial_rho;
double* bar_partial_V;
double* bar_partial_T;
double* partial_rho_av;
double* partial_V_av;
double* partial_T_av;
public:
void _init();
Nozzle(double x_f, double x_e, double a[], double rho0[], double T0[],
double V0[], int n, int it, double f);
Nozzle(const Nozzle& N);
void oneStepmacCormack();
void iterativeCompute();//迭代计算;
void print();//输出最终的计算结果;
~Nozzle();
};
#include "OneStepNozzle.h"
void Nozzle::_init()
{
this->Delta_x = (X_end - X_first) / (N - 1);// 空间步长;
this->Delta_t = -1;//时间步长;
this->gama = 1.4;// 绝热指数;
this->C = 0.5;// 柯朗数;
//创建向量;
//this->rho = new double[N];
//this->V = new double[N];
//this->T = new double[N];
this->partial_rho = new double[N];
this->partial_V = new double[N];
this->partial_T = new double[N];
this->bar_rho = new double[N];
this->bar_V = new double[N];
this->bar_T = new double[N];
this->bar_partial_rho = new double[N];
this->bar_partial_V = new double[N];
this->bar_partial_T = new double[N];
this->partial_rho_av = new double[N];
this->partial_V_av = new double[N];
this->partial_T_av = new double[N];
this->p = new double[N];
this->Ma = new double[N];
}
Nozzle::Nozzle(double x_f,double x_e,double a[], double rho0[],double T0[],
double V0[], int n,int it, double f):
X_first(x_f), X_end(x_e),A(a),rho(rho0),V(V0),T(T0), N(n), iterations(it), F(f)
{
//用户需要输入:计算区间[x_f,x_e];
// 喷管形状向量 a[];
// 初始密度 rho0[];
// 初始温度T0[];
// 初始速度V0[];
// 节点数 n;
// 迭代次数 it;
// 出口与入口压力之比 f;
_init();
}
Nozzle::Nozzle(const Nozzle& _N)
{
//浅拷贝;
this->N = _N.N;
this->X_first = _N.X_first;
this->X_end = _N.X_end;
this->Delta_x = _N.Delta_x;
this->Delta_t = _N.Delta_t;
this->gama = _N.gama;
this->C = _N.C;
this->iterations = _N.iterations;
this->F = _N.F;
//深拷贝;
this->rho = new double(*_N.rho);
this->V = new double(*_N.V);
this->T = new double(*_N.T);
this->partial_rho = new double(*_N.partial_rho);
this->partial_V = new double(*_N.partial_V);
this->partial_T = new double(*_N.partial_T);
this->bar_rho = new double(*_N.bar_rho);
this->bar_V = new double(*_N.bar_V);
this->bar_T = new double(*_N.bar_T);
this->bar_partial_rho = new double(*_N.bar_partial_rho);
this->bar_partial_V = new double(*_N.bar_partial_V);
this->bar_partial_T = new double(*_N.bar_partial_T);
this->partial_rho_av = new double(*_N.partial_rho_av);
this->partial_V_av = new double(*_N.partial_V_av);
this->partial_T_av = new double(*_N.partial_T_av);
this->p = new double(*_N.p);
this->Ma = new double(*_N.Ma);
this->A = new double(*_N.A);
}
void Nozzle::oneStepmacCormack()//使用Mac Cormack法计算;
{
预估步计算;
// 向前差分;
// 只计算内点;
for (int i = 0; i <= N - 2; i++)
{
partial_rho[i] = -V[i] * (rho[i + 1] - rho[i]) / Delta_x
- rho[i] * (V[i + 1] - V[i]) / Delta_x
- rho[i] * V[i] * (log(A[i + 1]) - log(A[i])) / Delta_x;
partial_V[i] = -V[i] * (V[i + 1] - V[i]) / Delta_x
- 1 / gama * ((T[i + 1] - T[i]) / Delta_x
+ T[i] / rho[i] * (rho[i + 1] - rho[i]) / Delta_x);
partial_T[i] = -V[i] * (T[i + 1] - T[i]) / Delta_x
- (gama - 1) * T[i] * ((V[i + 1] - V[i]) / Delta_x
+ V[i] * (log(A[i + 1]) - log(A[i])) / Delta_x);
}
partial_rho[N-1] = 0;
partial_V[N-1] = 0;
partial_T[N-1] = 0;
计算最小时间步长;
Delta_t = C * Delta_x / (V[0] + sqrt(T[0]));//将首节点计算的时间步长赋值给 Delta_t;
for (int i = 1;i < N;i++)//从第个二节点计算到最后一个节点;
{
if (C * Delta_x / (V[i] + sqrt(T[i])) <= this->Delta_t)
{
this->Delta_t = C * Delta_x / (V[i] + sqrt(T[i]));
}
}
// 计算预估值
for (int i = 0;i < N;i++)
{
bar_rho[i] = rho[i] + partial_rho[i] * Delta_t;
bar_V[i] = V[i] + partial_V[i] * Delta_t;
bar_T[i] = T[i] + partial_T[i] * Delta_t;
}
计算校正步;
// 向后差分;
// 只计算内点;
for (int i = 1;i <= N - 1;i++)
{
bar_partial_rho[i] = -bar_V[i] * (bar_rho[i] - bar_rho[i - 1]) / Delta_x
- bar_rho[i] * (bar_V[i] - bar_V[i - 1]) / Delta_x
- bar_rho[i] * bar_V[i] * (log(A[i]) - log(A[i - 1])) / Delta_x;
bar_partial_V[i] = -bar_V[i] * (bar_V[i] - bar_V[i - 1]) / Delta_x
- ((bar_T[i] - bar_T[i - 1]) / Delta_x
+ bar_T[i] * (bar_rho[i] - bar_rho[i - 1]) / (bar_rho[i] * Delta_x)) / gama;
bar_partial_T[i] = -bar_V[i] * (bar_T[i] - bar_T[i - 1]) / Delta_x
- (gama - 1) * bar_T[i] * ((bar_V[i] - bar_V[i - 1]) / Delta_x
+ bar_V[i] * (log(A[i]) - log(A[i - 1])) / Delta_x);
}
bar_partial_rho[0] = 0;
bar_partial_T[0] = 0;
bar_partial_V[0] = 0;
计算平均时间导数;
for (int i = 0;i < N;i++)
{
partial_rho_av[i] = (partial_rho[i] + bar_partial_rho[i]) / 2;
partial_V_av[i] = (partial_V[i] + bar_partial_V[i]) / 2;
partial_T_av[i] = (partial_T[i] + bar_partial_T[i]) / 2;
}
校正值;
// t + Delta_t 时的参数;
for (int i = 0;i < N;i++)
{
rho[i] = rho[i] + partial_rho_av[i] * Delta_t;
V[i] = V[i] + partial_V_av[i] * Delta_t;
T[i] = T[i] + partial_T_av[i] * Delta_t;
p[i] = rho[i] * T[i];
}
边界点处的流场变量;
// 入流边界;
rho[0] = 1;// 滞止参数;
V[0] = 2 * V[1] - V[2];// 允许边界上的 V 是可变化的;
T[0] = 1;// 滞止参数;
p[0] = rho[0] * T[0];// 滞止参数;
// 出流边界;
p[N - 1] = this->F;
T[N - 1] = 2 * T[N - 2] - T[N - 3];
rho[N - 1] = this->F / T[N - 1];
V[N - 1] = 2 * V[N - 2] - V[N - 3];
马赫数
for (int i = 0;i < N;i++)
{
Ma[i] = V[i] / sqrt(T[i]);
}
}
void Nozzle::iterativeCompute()//迭代计算;
{
for (int it = 0;it < this->iterations;it++)
{
oneStepmacCormack();
}
}
void Nozzle::print()
{
iterativeCompute();
cout << "节点" << "\t" << "密度" << "\t" << "速度" << "\t"
<< "温度" << "\t" << "压力" << "\t" << "马赫数" << endl;
for (int i = 0;i < N;i++)
{
cout << i + 1 << "\t"
<< setprecision(4) << rho[i] << "\t"
<< setprecision(4) << V[i] << "\t"
<< setprecision(4) << T[i] << "\t"
<< setprecision(4) << p[i] << "\t"
<< setprecision(4) << Ma[i] << endl;
}
}
Nozzle::~Nozzle()
{
//delete[] rho;
//delete[] V;
//delete[] T;
delete[] partial_rho;
delete[] partial_V;
delete[] partial_T;
delete[] bar_rho;
delete[] bar_V;
delete[] bar_T;
delete[] bar_partial_rho;
delete[] bar_partial_V;
delete[] bar_partial_T;
delete[] partial_rho_av;
delete[] partial_V_av;
delete[] partial_T_av;
delete[] p;
delete[] Ma;
}
/*
Author: CXT
Date : 2021 / 4 / 20
Theme : 拟一维全亚声速等熵喷管
%物理符号(无量纲量);
x —— 空间位置;
A —— 喷管面积;
rho —— 密度;
V —— 速度;
p —— 压力;
N —— 计算节点数;
*/
#include
#include //sqrt();
#include
#include "OneStepNozzle.h"
using namespace std;
void test()
{
double x_f = 0;//计算区间[x_f,x_e];
double x_e = 3;
int n = 31;//节点数;
int it = 5000;//迭代次数;
double f = 0.93;//出口与入口压力之比;
double* x = new double[n];
double* a = new double[n];
double* rho0 = new double[n];
double* T0 = new double[n];
double* V0 = new double[n];
//准备节点;
for (int i = 0; i<n; i++)
{
x[i] = x_f + (x_e - x_f) / (n - 1) * i;
a[i] = (1 + 2.2 * pow(x[i] - 1.5,2)) * (x[i] <= 1.5)
+ (1 + 0.2223 * pow(x[i] - 1.5, 2)) * (x[i] > 1.5);
rho0[i] = 1 - 0.023 * x[i];
T0[i] = 1 - 0.009333 * x[i];
V0[i] = 0.05 + 0.11 * x[i];
}
Nozzle N1(x_f,x_e,a,rho0,T0,V0,n,it,f);
N1.print();
delete[] x;
delete[] a;
delete[] rho0;
delete[] T0;
delete[] V0;
}
int main()
{
clock_t start = clock();
test();
clock_t end = clock();
cout << "Total time: " << end - start << "ms" << endl;
system("pause");
return 0;
}