栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

[流体输配管网] 树状管网水力分析、牛顿-拉夫森算法/哈代-克罗斯算法求解环能量方程组、牛顿-拉夫森算法求解节点校正压力方程组 的 Matlab 实现与例题求解

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

[流体输配管网] 树状管网水力分析、牛顿-拉夫森算法/哈代-克罗斯算法求解环能量方程组、牛顿-拉夫森算法求解节点校正压力方程组 的 Matlab 实现与例题求解

1.版权声明

% 管网的水力学特性分析
% Analysis of Hydraulic Characteristics of Pipe Network
% 作者:中山大学 2019 级 廉
% Author: SYSU 2019 CheapMiao

% 参考书目:《给水排水管网系统 第四版》 刘遂庆 9787112254743 中国建筑工业出版社

% 如果原题目中的节点编号不是从 1 开始的连续整数,那么需要重新编号

% 管网恒定流方程组求解条件
% 1.节点流量或压力必须有一个已知
% 2.管网中至少有一个定压节点

% mathcad 的求解器确实方便……

2.函数库

function hf = HazenWilliamsFormula(q, L, D, Cw, n)
    % 海曾威廉公式 提一个 q 出来 hf=10.67/Cw^1.852/D^4.87*L*q*abs(q)^(n-1)
    % 管段流量 q 单位 m^3/s
    % 管段长度 L 单位 m
    % 管段直径 D 单位 m
    % 管材粗糙系数(海曾-威廉系数) Cw 
    
    hf = 10.67./Cw.^1.852./D.^4.87.*L.*q.*abs(q).^(n-1);
    % D = 0 的管段的水头损失定义为 0
    for i = 1:1:size(D,1)
        if abs(D(i)) < 1e-10
            hf(i) = 0;
        end
    end
end

function hf = ManningFormula(q, L, D, nM)
    % 曼宁公式 hf=10.29*n^2*q^2/D^5.333*L
    % 管段长度 L 单位 m
    % 管段直径 D 单位 m
    % 曼宁粗糙系数 nM
    
    hf = 10.29.*nM.^2.*q.^2./D.^(5.333).*L;
    % D = 0 的管段的水头损失定义为 0
    for i = 1:1:size(D,1)
        if abs(D(i)) < 1e-10
            hf(i) = 0;
        end
    end
end

function hp = HydraulicCharacteristicFormula(he, sp, qp, n)
    % 水力特性公式 提一个 q 出来 hp=he-sp*qp*abs(qp)^(n-1)
    % 水泵扬程 hp 单位 m
    % 水泵流量 qp 单位 m^3/s
    % 水泵静扬程 he 单位 m
    % 水泵内阻系数 sp
    % 与水头损失计算指数公式相同的指数 n
    
    hp = he-sp.*qp.*abs(qp).^(n-1);
end

function [vector_var, vector_var_subnum] = FindVars(vector)
    % vector 是一个包含数值和符号变量的符号类型的列向量
    
    % vector 之中的变量的记录数组
    vector_var = [];
    % vector 之中的变量的下标的记录数组
    vector_var_subnum = [];
    
    % 寻找 vector 之中的变量
    for i = 1:1:size(vector,1)
        % 如果可以 double,说明是赋的数值
        try
            % tmp 没用,只是接收值而已
            tmp = double(vector(i));
        % 如果不可以 double,说明是赋的变量,记录变量和下标
        catch ME
            vector_var = [vector_var;vector(i)];
            vector_var_subnum = [vector_var_subnum;i];
        end
    end
end

function [q, Q] = NodalFlowEqsSolver(A, q, Q)
    % 节点流量方程组求解器
    % 节点流量方程组 A*q+Q=0
    % 关联矩阵 A 已知
    % 管段流量 q 单位 m^3/s 部分为未知数
    % 节点流量 Q 单位 m^3/s 部分为未知数
    
    % 1.
    % 现在已经把节点流量方程组解出来了
    % 但是返回的解是一个结构体,不能通过 double 强制类型转换
    % 我还以为这个结构体可以像 lua 那样直接 table[key] 访问,但是不行
    
    % 这样的话,我想要自动地把求解结果放到一个向量里面就有点难办
    % 它 matlab 的 结构体居然不支持通过字符串键访问值
    % 必须要显式的圆点访问
    % 也就是说,我解出了 q,我还需要 q = [Sol.q1 Sol.q2 ... Sol.q9] 这样赋值
    % 这灵活性太低了……比如 q 的长度一变,就要重新改代码
    % 而且参与求解的变量里面还不单单有 q,修改代码的时候还要考虑 Q 的赋值
    % 那么接着就是要考虑 q 的位置和 Q 的位置,好麻烦
    
    % 我想着有没有一个办法让结构体不返回一个结构体,而是直接返回各个值
    % 官方的文档里面是有这样的例子的
    
    % % Sol 会返回 1*1 的结构体
    % Sol = solve(eq_NodalFlow,vars);
    % 
    % % Sol 会返回 1*1 的结构体
    % Sol = zeros(size(vars,1),1);
    % Sol = solve(eq_NodalFlow,vars);
    % 
    % % Sol 会返回 1*1 的结构体
    % Sol = sym(zeros(size(vars,1),1));
    % Sol = solve(eq_NodalFlow,vars);
    % 
    % % Sol 会返回 1*1 的结构体
    % syms Sol [size(vars,1),1];
    % Sol = solve(eq_NodalFlow,vars);
    
    % 但是后面我没试出来返回多个值的情况
    
    % 然后我就想着使用 eval
    % 一开始的想法是用 eval 返回一个变量名,如下代码所示
    
    % % 会报错:动态结构体引用的参数的计算结果必须为有效字段名称。
    % Sol = solve(eq_NodalFlow,vars);
    % Sol.(eval('q1'));
    
    % 这个代码的问题在于,eval 实际上是返回了一个变量,那么实际上传入 Sol 的 fieldname 是一个变量
    % 但是传入 Sol 的 fieldname 应该是一个常量文本
    
    % 因此接下来的方法就是要更进一步,把所有东西都放到 eval 里面!这就是!奥义·动态代码!
    
    % 首先是要动态生成文本
    
    % ['hello','world'] 自动拼接成一个字符串
    % ['Sol.',string(vars(i,1))] string(['Sol.',string(vars(i))]) 是一个 1*2 的 string 类型的数组,没有自动拼接
    % 所以后者需要使用 strcat
    
    % 然后要知道 q Q 里面哪些未知的符号变量
    % 由于传入的 q Q 都是 sym 类型的,所以不能通过 class 判断哪个是赋的数值
    % 但是可以通过类型转换的方式试探,如果能够 double 那就是赋的数值,如果不是,那就是变量
    % 这还是写振动力学求解 w 的时候带来的启发
    % 因此写了 FindVars 函数
    
    % 2.
    % 如果只有一个变量,那么 solve 可以直接解得一个数值
    % 因此需要 try 一下 Sol 是否只是一个数值,代码如下
    
    % % 如果 Sol 的类型为符号,说明只有一个变量
    % if class(Sol) == 'sym'
    %     % 转换成 double 类型
    %     try
    %         Sol = double(Sol);
    %     % 不能转换成 double 类型
    %     catch ME
    %         
    %     end
    % end
    
    % 对 例5.1 运行,报错为:
    % 矩阵维度必须一致。
    % 
    % 出错 AnalysisOfHydraulicCharacteristicsOfPipeNetwork>NodalFlowEqsSolver (第 284 行)
    %     if class(Sol) == 'sym'
    
    % debug 代码如下
    
    % class(Sol)
    % % 如果 Sol 的类型为符号,说明只有一个变量
    % if class(Sol) == 'sym'
    %     1
    %     % 转换成 double 类型
    %     try
    %         Sol = double(Sol);
    %     % 不能转换成 double 类型
    %     catch ME
    %         
    %     end
    % end
    
    % 实际没有输出 1,说明对于结构体,不适用 class(Sol) == 'sym'
    % 但是可以输出 ans = 'struct',说明对于结构体可以使用 class
    % 那么还有哪里有问题呢……没搞懂
    % 只好把 if class(Sol) == 'sym' 放到 try 里面了,看上去很怪,但是没办法
    
    % 3.
    % 如果 Sol 中出现了 0-1 empty sym,说明该方程没有解
    
    % q Q 之中的变量的下标的记录数组
    [q_var, q_var_subnum] = FindVars(q);
    [Q_var, Q_var_subnum] = FindVars(Q);
    
    % 节点流量方程组
    eq_NodalFlow = A*q+Q == 0;
    % 待求解的变量
    vars = [q_var;Q_var];
    % 求解方程组
    Sol = solve(eq_NodalFlow,vars);
    
    % 转换成 double 类型
    try
        % 如果 Sol 的类型为符号,说明只有一个变量
        if class(Sol) == 'sym'
            Sol = double(Sol);
            % 把求解得到的变量从 Sol 之中拿出来
            if size(q_var_subnum,1) == 1
                q(q_var_subnum(1)) = Sol;
            elseif size(Q_var_subnum,1) == 1
                Q(Q_var_subnum(1)) = Sol;
            end
        end
    % 不能转换成 double 类型
    catch ME
        if class(Sol) == 'struct'
            % 把求解得到的变量从 Sol 结构体之中拿出来 
            for i = 1:1:size(q_var,1)
                % 检查解的存在性
                if size(eval(strcat('Sol.',string(q_var(i)))),1) == 0
                    error('该方程无解');
                end
                % 存在解,拿出
                q(q_var_subnum(i)) = eval(strcat('Sol.',string(q_var(i))));
            end
            for i = 1:1:size(Q_var,1)
                % 检查解的存在性
                if size(eval(strcat('Sol.',string(Q_var(i)))),1) == 0
                    error('该方程无解');
                end
                % 存在解,拿出
                Q(Q_var_subnum(i)) = eval(strcat('Sol.',string(Q_var(i))));
            end
        end
    end
    
    % 转换成 double 类型
    q = double(q);
    Q = double(Q);
end

function H = PressureDropEqsSolver(A, h, H)
    % 管段压降方程组 A'*H=h
    % 关联矩阵 A 
    % 节点压降 H 单位 m
    % 管段压降 h 单位 m 默认全部已知
    
    % 1.
    % 节点水头有时候解不出来,可能就是单纯的系数矩阵的秩与增广矩阵的秩不同
    % 这样的话就必须要手算了……
    % 严格来说节点流量方程似乎也会遇到这样情况?
    
    % 2.
    % 先根据已知变量的下标,把 A' 中已知为数值的那列去掉,保留其他,作为新的系数矩阵 co
    % 使用 A' 中已知为数值的那列与 H 相乘,移项到右边,最后得到新的列向量 b
    % 根据 x=Ab 可以把作为变量的 H 求出来,接下来就是把解放到 H 中
    
    % 3.
    % 似乎不管什么情况都可以使用 x=Ab 来解,这样的话,solve 似乎完全没有必要了
    % 我只是担心有的时候还是需要解更多奇怪的变量
    
    % q Q 之中的变量的下标的记录数组
    [H_var, H_var_subnum] = FindVars(H);
    
    % 对于 Ax=b
    % 构造系数矩阵 A
    for i = 1:1:size(H_var_subnum,1)
        co(:,i) = A(H_var_subnum(i),:);
    end
    % 列向量 b 初始化
    b = h;
    % 构造列向量 b
    for i = 1:1:size(A',2)
        % 如果在下表记录数组中找到了 i,说明 i 对应了变量,不变
        if find(H_var_subnum==i)
            % null
        % 如果在下表记录数组中没有找到 i,说明 i 对应了已知量,需要移项到右边  
        else
            % 需要去掉 A' 的第 i 列,即去掉 A 的第 i 行
            tmp = A(i,:);
            % 移项到右边
            for j = 1:1:size(tmp,2)
                b(j) = b(j) - tmp(j)*H(i);
            end
        end
    end
    % 构造增广矩阵
    augmented = [co,b];
    
    % 如果系数矩阵的秩与增广矩阵的秩相同,说明有解,可以直接用 solve 解
    if rank(co) == rank(augmented)
        % 管段压降方程组
        eq_PressureDrop = A'*H == h;
        % 待求解的变量
        vars = [H_var];
        % 求解方程组
        Sol = solve(eq_PressureDrop,vars);
        
        % 转换成 double 类型
        try
            % 如果 Sol 的类型为符号,说明只有一个变量
            if class(Sol) == 'sym'
                Sol = double(Sol);
                % 把求解得到的变量从 Sol 之中拿出来
                if size(H_var_subnum,1) == 1
                    H(H_var_subnum(1)) = Sol;
                end
            end
        % 不能转换成 double 类型
        catch ME
            if class(Sol) == 'struct'
                 % 把求解得到的变量从 Sol 结构体之中拿出来
                for i = 1:1:size(H_var,1)
                    % 检查解的存在性
                    if size(eval(strcat('Sol.',string(H_var(i)))),1) == 0
                        error('该方程无解');
                    end
                    % 存在解,拿出
                    H(H_var_subnum(i)) = eval(strcat('Sol.',string(H_var(i))));
                end
            end
        end
    % 如果系数矩阵的秩与增广矩阵的秩不同,说明无解,不能用 solve 解
    else
        % 解 Ax=b
        H_tmp = cob;
        % 把求解得到的变量放回 H
        for i = 1:1:size(H_tmp)
            H(H_var_subnum(i)) = H_tmp(i);
        end
    end
    
    % 转换成 double 类型
    H = double(H);
end

function sf = GetPipeFrictionLossCoefficient(L, D, Cw, varargin)
    % 通过指数形式的海曾威廉公式计算管段的摩阻系数 sf=10.67/Cw^1.852/D^4.87*L
    % 通过指数形式的曼宁公式计算管段的摩阻系数 sf=10.29*n^2/D^5.333*L
    % 海曾威廉公式 hf=10.67/Cw^1.852/D^4.87*L*q*abs(q)^(n-1)
    % 曼宁公式 hf=10.29*n^2*q^2/D^5.333*L
    % 水头损失公式的指数形式 hf=sf*q^n
    % 管段长度 L 单位 m
    % 管段直径 D 单位 m
    % 管材粗糙系数(海曾-威廉系数) Cw 
    % 曼宁粗糙系数 nM
    
    % 通过指数形式的曼宁公式计算管段的摩阻系数
    if size(varargin,1)
        if varargin{1} == 'Manning'
            n = Cw;
            sf = 10.29.*n.^2./D.^5.333.*L;
        end
    % 默认通过指数形式的海曾威廉公式计算管段的摩阻系数
    else
        sf = 10.67./Cw.^1.852./D.^4.87.*L;
    end
    % D = 0 的管段的水头损失定义为 0
    for i = 1:1:size(D,1)
        if abs(D(i)) < 1e-10
            sf(i) = 0;
        end
    end
end

function s = GetPipeResistanceCoefficient(L, D, sp, Cw, varargin)
    % 计算管段阻力系数 = 管段的摩阻系数 + 泵站的摩阻系数
    % 管段阻力系数 s
    % 管段长度 L 单位 m
    % 管段直径 D 单位 m
    % 水泵内阻系数(泵站的摩阻系数) sp
    % 管材粗糙系数(海曾-威廉系数) Cw
    % 曼宁粗糙系数 nM
    
    % 通过指数形式的曼宁公式计算管段的摩阻系数
    if size(varargin,1)
        if varargin{1} == 'Manning'
            sf = GetPipeFrictionLossCoefficient(L, D, Cw, 'Manning');
        end
    % 默认通过指数形式的海曾威廉公式计算管段的摩阻系数
    else
        sf = GetPipeFrictionLossCoefficient(L, D, Cw);
    end
    s = sf+sp;
end

function z = GetPipeDampingCoefficient(s, q, n)
    % 计算管段阻尼系数 z=n*s*abs(q)^(n-1);
    % 管段阻尼系数 z 
    % 管段阻力系数 s
    % 管段流量 q 单位 m^3/s
    % 与水头损失计算指数公式相同的指数 n
    
    z = n.*s.*abs(q).^(n-1);
end

function delta_h = GetWaterheadClosureError(Bf, h)
    % 计算水头闭合差 Δh=Bf*h
    % 水头闭合差 Δh 单位 m
    % 基本回路矩阵 Bf 顺时针为正
    % 管段压降 h 单位 m
    
    delta_h = Bf*h;
end

function bool = IsErrorAllowable(delta_h, eh) 
    % 例:判断各水头闭合差是否都小于最大允许闭合差
    % 水头闭合差 Δh 单位 m
    % 环水头闭合差的最大允许值 eh 单位 m
    
    bool = true;
    for i = 1:1:size(delta_h,1)
        if abs(delta_h(i)) > eh
            bool = false;
            return;
        end
    end
end

function F = GetLoopFlowCorrectionCoefficient(Bf, z)
    % 牛顿-拉夫森算法 计算与环校正流量的非线性函数等效的系数的矩阵 
    %     Σ(i∈Rl)(zi(0)), l = j,系数矩阵的对角元素
    % F = -zi(0),          i 为相邻环 l 和 j 的公共管段
    %     0,              l ≠ j 且环 l 和 j 不相邻
    % 其实这个书上的系数矩阵的计算公式有一点不对,就是计算公共管段的时候,假设了公共管段只有一段
    % 实际上有若干段是公共的完全是可能的嘛
    % 因此正确公式应该是
    %     Σ(i∈Rl)(zi(0)),   l = j,系数矩阵的对角元素
    % F = -Σ(i∈Rl)(zi(0)),  i 为相邻环 l 和 j 的公共管段
    %     0,                l ≠ j 且环 l 和 j 不相邻
    % 基本回路矩阵 Bf 顺时针为正
    % 管段阻尼系数 z
    
    % 基本回路矩阵的绝对值
    Bf_abs = abs(Bf);
    % 系数矩阵的对角线
    F_dia = Bf_abs*z;
    % 系数矩阵初始化
    F = diag(F_dia);
    % 获得系数矩阵的非对角元素
    for i = 1:1:size(F,1)
        for j = 1:1:size(F,2)
            % 非对角线
            if i ~= j
                % 第 i 个环和第 j 个环的公共边列向量
                pipe_shared = Bf_abs(i,:).*Bf_abs(j,:);
                % 系数矩阵的非对角元素
                F(i,j) = -pipe_shared*z;
            end
        end
    end
end

function q = CorrectQuantityOfFlow(Bf, q, delta_q)
    % 矫正各管段流量 qi(k+1)=qi(k)±Δql(k), i∈Rl
    % 基本回路矩阵 Bf 顺时针为正
    % 管段流量 q 单位 m^3/s
    % 管段矫正流量 delta_q 单位 m^3/s
    
    for i = 1:1:size(Bf,1)
        q = q + delta_q(i)*Bf(i,:)';
    end
end

function [hf, hp, h] = NewtonRaphsonProcess_LoopFlowCorrection(Bf, q, L, D, s, he, sp, Cw, n, eh, varargin)
    % 牛顿-拉夫森算法 环流量校正 判断各水头闭合差是否都小于最大允许闭合差,否则迭代
    % 基本回路矩阵 Bf 顺时针为正
    % 管段流量 q 单位 m^3/s
    % 管段长度 L 单位 m
    % 管段直径 D 单位 m
    % 管段阻力系数 s
    % 水泵静扬程 he 单位 m
    % 水泵内阻系数 sp
    % 管材粗糙系数(海曾-威廉系数) Cw 或曼宁公式系数 nM
    % 与水头损失计算指数公式相同的指数 n
    % 环水头闭合差的最大允许值 eh 单位 m
    
    % 牛顿-拉夫森算法 判断各水头闭合差是否都小于最大允许闭合差,否则迭代
    % do...while 型
    while 1
        % 通过指数形式的曼宁公式计算各管段水头损失
        if size(varargin,1)
            if varargin{1} == 'Manning'
                nM = Cw;
                hf = ManningFormula(q, L, D, nM);
            end
        % 默认通过指数形式的海曾威廉公式计算各管段水头损失
        else
            hf = HazenWilliamsFormula(q, L, D, Cw, n);
        end
        % 计算各管段泵站扬程
        hp = HydraulicCharacteristicFormula(he, sp, q, n);
        % 计算各管段压降
        h = hf - hp;
        % 计算管段阻尼系数 z
        z = GetPipeDampingCoefficient(s, q, n);
        % 计算与环校正流量的非线性函数等效的系数的矩阵
        F = GetLoopFlowCorrectionCoefficient(Bf, z);
        % 计算水头闭合差
        delta_h = GetWaterheadClosureError(Bf, h);
        
        % 求解基本环能量方程组
        delta_q = -Fdelta_h;
        % 矫正各管段流量(平差计算)
        q = CorrectQuantityOfFlow(Bf, q, delta_q);

        % 误差允许,结束迭代
        if IsErrorAllowable(delta_h, eh)
            break;
        end
    end
end

function [hf, hp, h] = HardyClausProcess_LoopFlowCorrection(Bf, q, L, D, s, he, sp, Cw, n, eh, varargin)
    % 哈代-克罗斯算法 环流量校正 判断各水头闭合差是否都小于最大允许闭合差,否则迭代
    % 基本回路矩阵 Bf 顺时针为正
    % 管段流量 q 单位 m^3/s
    % 管段长度 L 单位 m
    % 管段直径 D 单位 m
    % 管段阻力系数 s
    % 水泵静扬程 he 单位 m
    % 水泵内阻系数 sp
    % 管材粗糙系数(海曾-威廉系数) Cw 或曼宁公式系数 nM
    % 与水头损失计算指数公式相同的指数 n
    % 环水头闭合差的最大允许值 eh 单位 m
    
    % 哈代-克罗斯算法 判断各水头闭合差是否都小于最大允许闭合差,否则迭代
    % do...while 型
    while 1
        % 通过指数形式的曼宁公式计算各管段水头损失
        if size(varargin,1)
            if varargin{1} == 'Manning'
                nM = Cw;
                hf = ManningFormula(q, L, D, nM);
            end
        % 默认通过指数形式的海曾威廉公式计算各管段水头损失
        else
            hf = HazenWilliamsFormula(q, L, D, Cw, n);
        end
        % 计算各管段泵站扬程
        hp = HydraulicCharacteristicFormula(he, sp, q, n);
        % 计算各管段压降
        h = hf - hp;
        % 计算管段阻尼系数 z
        z = GetPipeDampingCoefficient(s, q, n);
        % 计算水头闭合差
        delta_h = GetWaterheadClosureError(Bf, h);
        % 基本回路矩阵的绝对值
        Bf_abs = abs(Bf);
        % 系数矩阵的对角线
        F_dia = Bf_abs*z;
        
        % 求解基本环能量方程组
        delta_q = -delta_h./F_dia;
        % 矫正各管段流量(平差计算)
        q = CorrectQuantityOfFlow(Bf, q, delta_q);
        
        % 误差允许,结束迭代
        if IsErrorAllowable(delta_h, eh) 
            break;
        end
    end    
end

function [hf, hp, h] = ImprovedHardyClausProcess_LoopFlowCorrection(Bf, q, L, D, s, he, sp, Cw, n, eh, varargin)
    % 改进哈代-克罗斯法 环流量校正
    % 基本回路矩阵 Bf 顺时针为正
    % 管段流量 q 单位 m^3/s
    % 管段长度 L 单位 m
    % 管段直径 D 单位 m
    % 管段阻力系数 s
    % 水泵静扬程 he 单位 m
    % 水泵内阻系数 sp
    % 管材粗糙系数(海曾-威廉系数) Cw 或曼宁公式系数 nM
    % 与水头损失计算指数公式相同的指数 n
    % 环水头闭合差的最大允许值 eh 单位 m
    
    % 1.
    % 改"各环同时平差"为"每次只平差一个环"
    % 各环同时平差时, 每个环平差时未考虑前面已平差环传来的闭合差, 不能将其传递出去, 故收敛速度慢
    % 且同时平差不能选择平差顺序, 不能避免对闭合差已经小于允许值的环进行多元的平差计算
    % 每次只平一个环, 平差后立即更新管段流量, 后面的平差利用新的管段流量计算闭合差, 则平差收敛速度得到很大提高
    % 平差时优先平差闭合差最大的环, 这样传递出去的闭合差最大, 计算效率最高
    % 平差接近最终解时管段流量变化量已经很小, 故在若干次平差后可将阻尼系数固定不变从而节省部分计算量
    % [1]张涛,蔡毅,李湧.环状管网自动平差研究[J].山西建筑,2008,(19):364-365.
    
    % 改进哈代-克罗斯算法 判断各水头闭合差是否都小于最大允许闭合差,否则迭代
    % do...while 型
    while 1
        % 通过指数形式的曼宁公式计算各管段水头损失
        if size(varargin,1)
            if varargin{1} == 'Manning'
                nM = Cw;
                hf = ManningFormula(q, L, D, nM);
            end
        % 默认通过指数形式的海曾威廉公式计算各管段水头损失
        else
            hf = HazenWilliamsFormula(q, L, D, Cw, n);
        end
        % 计算各管段泵站扬程
        hp = HydraulicCharacteristicFormula(he, sp, q, n);
        % 计算各管段压降
        h = hf - hp;
        % 计算管段阻尼系数 z
        z = GetPipeDampingCoefficient(s, q, n);
        % 计算水头闭合差
        delta_h = GetWaterheadClosureError(Bf, h);
        % 基本回路矩阵的绝对值
        Bf_abs = abs(Bf);
        % 系数矩阵的对角线
        F_dia = Bf_abs*z;
        % 寻找闭合差绝对值最大的环
        [~, delta_h_max_subnum] = max(abs(delta_h));
        
        % 求解基本环能量方程组 但只解闭合差绝对值最大的环
        delta_q = zeros(size(delta_h));
        delta_q(delta_h_max_subnum) = -delta_h(delta_h_max_subnum)./F_dia(delta_h_max_subnum);
        % 矫正各管段流量(平差计算)
        q = CorrectQuantityOfFlow(Bf, q, delta_q);
        
        % 误差允许,结束迭代
        if IsErrorAllowable(delta_h, eh) 
            break;
        end
    end
end

function c = GetNodalPressureCorrectionCoefficient(A, q, s, H_var_subnum, n)
    % 牛顿-拉夫森算法 计算与节点校正流量的非线性函数等效的系数的矩阵 
    %           Σ(k∈Si)(1/(n*sjk*|qjk(0)|^(n-1))), 系数矩阵的主对角元素, Sj——节点 j 的关联集
    % ∂G/∂ΔHk = -1/(n*sjk*|qjk(0)|^(n-1))         , 节点 j 和 k 衔接, 第 j 行第 k 列元素及其对称元素
    %           0                                 , 节点 j 和 k 不衔接, 第 j 行第 k 列元素及其对称元素
    % 关联集就是看与这个点相连的其他节点
    % 衔接就是只看 j 和 k 决定的这个管段
    % 关联矩阵 A
    % 管段流量 q 单位 m^3/s
    % 管段阻力系数 s
    
    % 1.
    % 噢!这个公式分母不是 q^n 而是 q^(n-1)
    % 所以公式不是
    % c(i,j) = c(i,j) + 1./n./s(col(k))./q(col(k))./abs(q(col(k)))^(n-1);
    % c(i,j) = -1./n./s(col(k))./q(col(k))./abs(q(col(k)))^(n-1);
    % 而是
    % c(i,j) = c(i,j) + 1./n./s(col(k))./abs(q(col(k)))^(n-1);
    % c(i,j) = -1./n./s(col(k))./abs(q(col(k)))^(n-1);
    
    % 系数矩阵初始化
    c = zeros(size(H_var_subnum,1),size(H_var_subnum,1));
    % 构造系数矩阵
    for i = 1:1:size(H_var_subnum,1)
        for j = 1:1:size(H_var_subnum,1)
            % 对角线元素,看关联集
            % H_var_subnum(i) 是由 H_var_subnum 决定的第几个 H 的序号
            if i == j
                % 从关联矩阵中第 H_var_subnum(i) 个节点对应的第 H_var_subnum(i) 行中找到含非零元素的列数 col
                % col 即表示与第 H_var_subnum(i) 个节点相连的管段编号
                col = find(A(H_var_subnum(i),:)); 
                % 对于每一个与第 i 个节点相连的管段
                for k = 1:1:size(col,2)
                    c(i,j) = c(i,j) + 1./n./s(col(k))./abs(q(col(k)))^(n-1);
                end
            % 非对角线元素,看衔接
            else
                % 从关联矩阵中第 H_var_subnum(i) 个节点对应的第 H_var_subnum(i) 行中找到含非零元素的列数 col
                % col 即表示与第 H_var_subnum(i) 个节点相连的管段编号
                col = find(A(H_var_subnum(i),:));
                % 对于每一个与第 H_var_subnum(i) 个节点相连的管段
                for k = 1:1:size(col,2)
                    % 如果在第 k 个与第 H_var_subnum(i) 个节点相连的管段中找到了非零元素
                    % 那么检查这个元素是否是第 j 个节点,即这个元素是否在第 j 行
                    if find(find(A(:,col(k)))==j)
                        c(i,j) = -1./n./s(col(k))./abs(q(col(k)))^(n-1);
                    end
                end    
            end
        end
    end
end

function [q, H] = NewtonRaphsonProcess_NodalPressureCorrection(A, H, H_var_subnum, Q, L, D, sp, Cw, n, eq, varargin)
    % 牛顿-拉夫森算法 节点压力校正 判断各水头闭合差是否都小于最大允许闭合差,否则迭代
    % 关联矩阵 A 
    % 节点压降 H 单位 m
    % 管段流量 q 单位 m^3/s
    % 水泵内阻系数 sp
    % 管段阻力系数 s
    % 管材粗糙系数(海曾-威廉系数) Cw 或曼宁公式系数 nM
    % 与水头损失计算指数公式相同的指数 n
    % 节点流量闭合差的最大允许值 eq 单位 L/s 或节点压力的最大允许值 ε
    
    % 1.
    % 节点压力平差计算步骤
    % 1.确定已知节点压力,设定位置压力节点的初始压力 Hi(0)
    % 2.用当前节点压力 Hi 计算各管段水头损失和管段流量 hj=HFj-HTj 和 qj=(hj/sj)^(1/n),(j=1,2,...,M)
    % 3.计算各节点流量闭合差 ΔQi=Σ(j∈Si)((aj)*qj)+Qi,(i=1,2,...,N)
    % aj = 1,当节点 i 是管段 j 的出点;aj = -1,当节点 i 是管段 j 的入点
    % 4.计算节点校正压力 
    % 牛顿-拉夫森算法 求解节点校正压力方程组,得到其系数矩阵
    %           Σ(k∈Si)(1/(n*sjk*|qjk(0)|^(n-1))), 系数矩阵的主对角元素, Sj——节点 j 的关联集
    % ∂G/∂ΔHk = -1/(n*sjk*|qjk(0)|^(n-1))         , 节点 j 和 k 衔接, 第 j 行第 k 列元素及其对称元素
    %           0                                 , 节点 j 和 k 不衔接, 第 j 行第 k 列元素及其对称元素
    % 节点压力平差法 ΔHi=-ΔQi/Σ(j∈Si)(cij),(i=1,2,...,N)
    % 5.如果任一节点校正压力 ΔHi(k)>ε,计算新的节点压力:Hi(k+1)=Hi(k)+ΔHi(k),(i=1,2,...,N,k 为迭代计算次数),返回步骤 2
    % 6.如果 ΔHi(k)<ε,平差计算完成,求管段流量和节点自由压力
    
    % 2.
    % 本来打算通过如下代码传入计算系数矩阵的参数
    
    % % 向量初始化
    % q_var = zeros(size(H_var_subnum));
    % s_var = zeros(size(H_var_subnum));
    % % 构造
    % for i = 1:1:size(H_var_subnum,1)
    %     q_var(i) = q(H_var_subnum(i));
    %     s_var(i) = s(H_var_subnum(i));
    % end
    % 
    % % 计算与节点校正流量的非线性函数等效的系数的矩阵
    % c = GetNodalPressureCorrectionCoefficient(A, q_var, s_var, n)
    
    % 但是这时不正确的,因为砍掉的自由度是要砍掉节点,而这种做法砍掉了管段
    % 结果例如,(1)~(7) 的节点计算 c 时需要用到管段 [8],但是却访问不到
    % 这个时候的 GetNodalPressureCorrectionCoefficient 也是根据已经消掉管段自由度来设计的,所以还要重新做一个
    % 比如向 GetNodalPressureCorrectionCoefficient 传入 H_var_subnum
    
    % 3.
    % 初始时水源节点 (1) 和 (5) 的泵站扬程未知,使得 he 未知,故不能使用如下代码在一开始确定一个 H 的初值
    
    % % 默认通过指数形式的海曾威廉公式计算各管段水头损失
    % hf = HazenWilliamsFormula(q, L, D, Cw, n);
    % % 计算各管段泵站扬程
    % hp = HydraulicCharacteristicFormula(he, sp, q, n);
    % % 计算各管段压降
    % h = hf - hp;
    % % 求解管段压降方程组
    % H = PressureDropEqsSolver(A, h, H)
    
    % 4.
    % 根据 qj=(hj/sj)^(1/n),(j=1,2,...,M) 先计算出 q 时,不能立即根据 Aq+Q=0 算出 Q,然后使用这个 Q 值计算 ΔQ
    % 也就是说,我将 Q 误认为一个具有初值,只受 ΔQ 影响的值
    % 实际上,不能在迭代的过程中通过 Aq+Q=0 算出 Q,因为这个时候还存在着节点流量闭合差
    % 就是说,Aq+Q=0不完全成立,误差是节点流量闭合差
    % 书本上,q 的取值就是取最后一次迭代时计算出的 q
    
    % 取第一次迭代时计算出的 q,根据 Aq+Q=0 计算出 Q
    
    % Q = 8×1    
    %    -0.1051
    %    -0.0221
    %     0.0372
    %     0.0346
    %    -0.0429
    %    -0.0182
    %     0.0903
    %     0.0262
        
    % 如下代码所示,取最后一次迭代时计算出的 q,根据 Aq+Q=0 计算出 Q
    
    % % 关联矩阵 A 
    % A = [
    %      1     0     0     0     0     0     0     0     0
    %     -1     1     0     0     1     0     0     0     0
    %      0    -1     1     0     0     1     0     0     0
    %      0     0    -1    -1     0     0     1     0     0
    %      0     0     0     1     0     0     0     0     0
    %      0     0     0     0    -1     0     0     1     0
    %      0     0     0     0     0    -1     0    -1     1
    %      0     0     0     0     0     0    -1     0    -1];
    % % 课本得出的管段流量结果
    % q_test = [0.0989+0.0989 0.0828 0.0113 0.0168+0.0168 0.0997 0.0203 0.0241 0.0645 0.0026]';
    % % 展示结果
    % disp(-A*q_test)
    
    % >>disp(-A*q_test)
    %    -0.1978
    %     0.0153
    %     0.0512
    %     0.0208
    %    -0.0336
    %     0.0352
    %     0.0822
    %     0.0267
    
    % 而节点流量初值为
    
    % Q = [
    %     -198.08
    %     14.55
    %     51.17
    %     20.77
    %     -33.42
    %     35.03
    %     82.33
    %     27.65]'/1000;
    
    % 这就验证了 q 的取值就是取最后一次迭代时计算出的 q

    % 5.
    % 一开始,第一次迭代计算出来的 ΔQ 和书本中给出的表格数据不符
    % 然后我就验算了一下书本上面是否是对的
    
    % 书本上 q1 = 0.1038 m^3/s q10 = 0.1038 m^3/s Q1 = -0.19808 m^3/s 根据公式 ΔQi=Σ(j∈Si)(qj)+Qi,(i=1,2,...,N) 得
    % ΔQ1 = q1 + q10 + Q1 = 0.1038+0.1038-0.19808 m^3/s = 0.0095 m^3/s
    % 书本表格数据为 ΔQ1 = 0.0095 m^3/s 与计算数据相符
    
    % 书本上 q1 = 0.1038 m^3/s q10 = 0.1038 m^3/s q2 = 0.0708 m^3/s q5 = 0.0564 m^3/s Q2 = 0.01455 m^3/s
    % 根据公式得 ΔQ2 = q1 + q10 + q2 + q5 + Q2 = 0.1038+0.1038+0.0708+0.0564+0.01455 m^3/s = 0.3493 m^3/s
    % 书本表格数据为 ΔQ2 = -0.0611 m^3/s 与计算数据不符
    
    % 我感觉这应该是还是需要在 qi 的前面加一个正负号的系数,书上写少了,比如
    % ΔQ2 = -q1 - q10 + q2 + q5 + Q2 = -0.1038-0.1038+0.0708+0.0564+0.01455 m^3/s = -0.0658 m^3/s
    % 这样就跟书本数据相差不大了
    
    % 因此 公式 ΔQi=Σ(j∈Si)(qj)+Qi,(i=1,2,...,N) 应该还是少了一个正负号,正确的为
    % ΔQi=Σ(j∈Si)((aj)*qj)+Qi,(i=1,2,...,N)
    % aj = 1,当节点 i 是管段 j 的出点;aj = -1,当节点 i 是管段 j 的入点
    
    % 6.
    % 在 gitee 上搜到了一个同样是 给水管网节点压力平差计算 的程序
    % https://gitee.com/q_rain/PipeNetModel/tree/master
    % 但是我看不懂他那个计算公式为什么那么写:
    % Qi = Σ(j∈Si)((aj)*qj)-0.5/dhfdq*H(k)
    % 当前管段的另一端节点的节点编号 k
    % 沿程水损对流量的导数 dhfdq = d(hf)/d(q) = n*sp*|q|^(n-1)

    % 7.
    % 又算错了,但是不知道原因,对比数据也不可靠的时候,还是只能自己手算了
    % 比如我第二个算错了
    % 已知在 delta_Q_var(i) = delta_Q_var(i) + Q(H_var_subnum(i)); 中
    % i=2时,-0.1348+0.0146=-0.1203
    % 但是实际上应为 -0.2056+0.0708+0.0564+0.01455=-0.0638 与书中数据表相近
    % 又发现 -0.2056+0.0708=-0.1348
    % 所以错误就是 delta_Q_var(i) = delta_Q_var(i) + node1*q(col(k)); 少执行了一次
    % 截到那一步,发现本来应该是 +0.0564 的 node1*q(col(k)) 现在算出来是 0
    % 那就说明这个时候算出来的 node1=0,就说明没有在关联矩阵里面找到管段端点
    % 要不是关联矩阵写错了,要不是找点找错了
    % 重新看了一遍关联矩阵,确认没有错,那就是找点找错了
    % 然后就发现是 node1 = A(H_var_subnum(i),k); 写错了,正确的应该是
    % node1 = A(H_var_subnum(i),col(k));
    
    % 通过指数形式的曼宁公式计算各管段水头损失
    if size(varargin,1)
        if varargin{1} == 'Manning'
            nM = Cw;
            % 计算管段阻力系数 s
            s = GetPipeResistanceCoefficient(L, D, sp, nM, 'Manning');
        else
            % 计算管段阻力系数 s
            s = GetPipeResistanceCoefficient(L, D, sp, Cw);
        end
    % 默认通过指数形式的海曾威廉公式计算各管段水头损失
    else
        % 计算管段阻力系数 s
        s = GetPipeResistanceCoefficient(L, D, sp, Cw);
    end
    
    delta_Q_var = zeros(size(H_var_subnum));
    H_var = zeros(size(H_var_subnum));
    % 构造
    for i = 1:1:size(H_var_subnum,1)
        H_var(i) = H(H_var_subnum(i));
    end
    
    % do...while 型
    while(1)
        % 2.用当前节点压力 Hi 计算各管段水头损失和管段流量 hj=HFj-HTj 和 qj=(hj/sj)^(1/n),(j=1,2,...,M)
        h = A'*H;
        q = (h./s).^(1/n);
        % 3.计算各节点流量闭合差 ΔQi=Σ(j∈Si)(qj)+Qi,(i=1,2,...,N)
        % 第 i 个闭合差对应第 H_var_subnum(i) 个节点
        for i = 1:1:size(H_var_subnum,1)
            col = find(A(H_var_subnum(i),:)); 
            % 对于每一个与第 H_var_subnum(i) 个节点相连的管段
            for k = 1:1:size(col,2)
                % node1 为查找结果的第 k 个管段中作为起点的第 H_var_subnum(i) 个节点的值
                % 即 node1 是查找结果的第 k 个管段中的一个端点的值
                node1 = A(H_var_subnum(i),col(k));
                % node1 是 q 前的系数
                delta_Q_var(i) = delta_Q_var(i) + node1*q(col(k));
            end
            delta_Q_var(i) = delta_Q_var(i) + Q(H_var_subnum(i));
        end

        % 计算与节点校正流量的非线性函数等效的系数的矩阵
        c = GetNodalPressureCorrectionCoefficient(A, q, s, H_var_subnum, n);
        % 4.计算节点校正压力
        delta_H_var = -cdelta_Q_var;
        % 通过自定义的节点压力校正公式计算节点压力
        if size(varargin,1)
            if class(varargin{2}) == 'function_handle'
                fun = varargin{2};
                % 节点压力校正公式
                for i = 1:1:size(H_var_subnum,1)
                    H(H_var_subnum(i)) = fun(H(H_var_subnum(i)),delta_H_var(i));
                end
            end
        % 默认节点压力校正公式计算节点压力
        else
            % 节点压力校正公式
            for i = 1:1:size(H_var_subnum,1)
                H(H_var_subnum(i)) = H(H_var_subnum(i)) + delta_H_var(i);
            end
        end
        
        % 误差允许,求解完毕
        if IsErrorAllowable(delta_Q_var,eq)
            return;
        end
    end
end

% 节点压力平差算法求解节点矫正压力方程组的函数就懒得写了,就也只是改一下 c 而已

3.树状管网水力分析


% 书本 例5.1 树状管网水力分析

clear;

% 已知条件

% 关联矩阵 A
A = [
    1	0	0	0	0	0	0	0	0
    -1	1	0	0	0	0	0	0	0
    0	-1	1	0	1	0	0	0	0
    0	0	-1	1	0	0	0	0	0
    0	0	0	-1	0	0	0	0	0
    0	0	0	0	-1	1	0	0	1
    0	0	0	0	0	-1	1	0	0
    0	0	0	0	0	0	-1	1	0
    0	0	0	0	0	0	0	-1	0
    0	0	0	0	0	0	0	0	-1];
% 管段流量 q 单位 L/s 部分为未知数
syms q [size(A,2) 1] real;
% 节点流量 Q 单位 L/s 部分为未知数
syms Q1 real;
Q = sym([Q1;5.37;16.11;7.16;4.48;30.74;7.52;7.07;3.67;11.63]);

% 管段长度 L 单位 m
L = [600;300;150;250;450;230;190;205;650];
% 管段直径 D 单位 m
D = [400;400;150;100;300;200;150;100;150]/1000;
% 水泵静扬程 he 单位 m
he = [42.6;0;0;0;0;0;0;0;0];
% 水泵内阻系数 sp
sp = [311.1;0;0;0;0;0;0;0;0];
% 水泵流量 qp 单位 m^3/s
% 等于管段流量 q

% 设计水头 H 单位 m
syms H [size(A,1) 1] real; 
H(1) = sym(7.8);
% 节点地面标高 H_att 单位 m 
H_att = [9.8;11.5;11.8;15.2;17.4;13.3;12.8;13.7;12.5;15];

% 海曾威廉系数 Cw
Cw = 100;
% 与水头损失计算指数公式相同的指数 n
n = 1.852;

% 求解节点流量方程组
[q, Q] = NodalFlowEqsSolver(A, q, Q);
% 管段流量 q 单位 m^3/s 全部已知
q = q/1000;
% 节点流量 Q 单位 m^3/s 全部已知
Q = Q/1000;
% 管段流速 v 单位 m/s
v = q./(pi*D.^2/4);
% 计算各管段水头损失
hf = HazenWilliamsFormula(q, L, D, Cw, n);
% 计算各管段泵站扬程
hp = HydraulicCharacteristicFormula(he, sp, q, n);
% 计算各管段压降
h = hf - hp;
% 求解管段压降方程组
H = PressureDropEqsSolver(A, h, H);
% 计算自由水压
H_free = H - H_att;

4.管网环方程组水力分析 牛顿-拉夫森算法求解环能量方程组

% 书本 例5.2 管网环方程组水力分析 牛顿-拉夫森算法求解环能量方程组

clear;

% 已知条件

% 题中节点编号 重新编号
% 2           1
% 3           2
% 4           3
% 6           4
% 7           5
% 8           6

% 题中管段编号 重新编号
% 2           1
% 3           2
% 5           3
% 6           4
% 7           5
% 8           6
% 9           7

% 关联矩阵 A 
A = [
     1     0     1     0     0     0     0
    -1     1     0     1     0     0     0
     0    -1     0     0     1     0     0
     0     0    -1     0     0     1     0
     0     0     0    -1     0    -1     1
     0     0     0     0    -1     0    -1];
% 基本回路矩阵 Bf 顺时针为正
Bf = [
     1     0    -1     1     0    -1     0
     0     1     0    -1     1     0    -1];
% 管段流量 q 单位 m^3/s 全部已知
q = [0.08990 0.00627 0.08990 0.03246 0.02265 0.05487 0.00500]';
% 节点流量 Q 单位 m^3/s 部分为未知数
syms Q6 real;
Q = sym([-0.17980 0.05117 -0.01638 0.03503 0.08233 Q6]');

% 管段长度 L 单位 m
L = [650 550 330 350 360 590 490]';
% 管段直径 D 单位 m
D = [0.300 0.200 0.300 0.200 0.20 0.300 0.100]';
% 水泵静扬程 he 单位 m
he = [0 0 0 0 0 0 0]';
% 水泵内阻系数 sp
sp = [0 0 0 0 0 0 0]';
% 水泵流量 qp 单位 m^3/s
% 等于管段流量 q

% 设计水头 H 单位 m
syms H [size(A,1) 1] real; 
H(6) = sym(41.50);
% 节点地面标高 H_att 单位 m 
H_att = [18.80 19.10 22.00 18.30 17.30 17.50]';

% 海曾威廉系数 Cw
Cw = 110;
% 与水头损失计算指数公式相同的指数 n
n = 1.852;
% 环水头闭合差的最大允许值 eh 单位 m
eh = 0.01;

% 求解节点流量方程组
[q, Q] = NodalFlowEqsSolver(A, q, Q);
% 计算管段阻力系数 s
s = GetPipeResistanceCoefficient(L, D, sp, Cw);
% 牛顿-拉夫森算法
[hf, hp, h] = NewtonRaphsonProcess_LoopFlowCorrection(Bf, q, L, D, s, he, sp, Cw, n, eh);
% 管段流速 v 单位 m/s
v = q./(pi*D.^2/4);
% 求解管段压降方程组
H = PressureDropEqsSolver(A, h, H);
% 计算自由水压
H_free = H - H_att;

5.管网环方程组水力分析 哈代-克罗斯算法求解环能量方程组


% 书本 例5.3 管网环方程组水力分析 哈代-克罗斯算法求解环能量方程组

clear;

% 已知条件

% 题中节点编号 重新编号
% 0           1
% 1           2
% 2           3
% 3           4
% 4           5
% 5           6
% 6           7
% 7           8
% 8           9

% 关联矩阵 A 
A = [
     0     0     0     0     0     0     0     0     0     1     1
     1     0     0     0     0     0     0     0     0    -1     0
    -1     1     0     0     1     0     0     0     0     0     0
     0    -1     1     0     0     1     0     0     0     0     0
     0     0    -1    -1     0     0     1     0     0     0     0
     0     0     0     1     0     0     0     0     0     0    -1
     0     0     0     0    -1     0     0     1     0     0     0
     0     0     0     0     0    -1     0    -1     1     0     0
     0     0     0     0     0     0    -1     0    -1     0     0];
% 基本回路矩阵 Bf 顺时针为正
Bf = [
     0     1     0     0    -1     1     0    -1     0     0     0
     0     0     1     0     0    -1     1     0    -1     0     0
     1     1     1    -1     0     0     0     0     0     1    -1];
% 管段流量 q 单位 m^3/s 全部已知
q = [198.08 81.17 10.00 33.42 102.36 20.00 22.65 67.33 5.00 198.08 33.42]'/1000;
% 节点流量 Q 单位 m^3/s 全部已知
% 不需要求解
% 管段长度 L 单位 m
L = [320 650 550 270 330 350 360 590 490 0 0]';
% 管段直径 D 单位 m
D = [2^(2/5.333)*300 300 200 2^(2/5.333)*200 300 200 200 300 100 0 0]'/1000;
% 水泵静扬程 he 单位 m
he = [48.96 0 0 0 0 0 0 0 0 12 48]';
% 水泵内阻系数 sp
sp = [138.5 0 0 0 0 0 0 0 0 0 0]';
% 水泵流量 qp 单位 m^3/s
% 等于管段流量 q

% 设计水头 H 单位 m
syms H [size(A,1) 1] real; 
H(1) = sym(0);
H(5) = sym(48.00);
% 节点地面标高 H_att 单位 m 
H_att = [0 13.60 18.80 19.10 22.00 32.20 18.30 17.30 17.50]';

% 海曾威廉系数 Cw
Cw = 110;
% 与水头损失计算指数公式相同的指数 n
n = 1.852;
% 环水头闭合差的最大允许值 eh 单位 m
eh = 0.01;

% 计算管段阻力系数 s
s = GetPipeResistanceCoefficient(L, D, sp, Cw);
s = 1.0e+05 *[
    0.0021
    0.0059
    0.0435
    0.0073
    0.0030
    0.0277
    0.0285
    0.0054
    1.5631
         0
         0];
% 哈代-克罗斯算法
[hf, hp, h] = HardyClausProcess_LoopFlowCorrection(Bf, q, L, D, s, he, sp, Cw, n, eh);
% 管段流速 v 单位 m/s
v = q./(pi*D.^2/4);
% 求解管段压降方程组
H = PressureDropEqsSolver(A, h, H);
% 计算自由水压
H_free = H - H_att;

6.习题1

% 书本 习题5.1 对于 例5.3
% 管段水头损失改用曼宁公式计算,取 n = 0.012
% 将水塔高度提高 0.5m,即节点 (5) 水头改为 48.50m,其余数据不变
% 使用改进哈代-克罗斯平差算法

clear;

% 已知条件

% 题中节点编号 重新编号
% 0           1
% 1           2
% 2           3
% 3           4
% 4           5
% 5           6
% 6           7
% 7           8
% 8           9

% 关联矩阵 A 
A = [
     0     0     0     0     0     0     0     0     0     1     1
     1     0     0     0     0     0     0     0     0    -1     0
    -1     1     0     0     1     0     0     0     0     0     0
     0    -1     1     0     0     1     0     0     0     0     0
     0     0    -1    -1     0     0     1     0     0     0     0
     0     0     0     1     0     0     0     0     0     0    -1
     0     0     0     0    -1     0     0     1     0     0     0
     0     0     0     0     0    -1     0    -1     1     0     0
     0     0     0     0     0     0    -1     0    -1     0     0];
% 基本回路矩阵 Bf 顺时针为正
Bf = [
     0     1     0     0    -1     1     0    -1     0     0     0
     0     0     1     0     0    -1     1     0    -1     0     0
     1     1     1    -1     0     0     0     0     0     1    -1];
% 管段流量 q 单位 m^3/s 全部已知
q = [198.08 81.17 10.00 33.42 102.36 20.00 22.65 67.33 5.00 198.08 33.42]'/1000;
% 节点流量 Q 单位 m^3/s 全部已知
% 不需要求解
% 管段长度 L 单位 m
L = [320 650 550 270 330 350 360 590 490 0 0]';
% 管段直径 D 单位 m
D = [2^(2/5.333)*300 300 200 2^(2/5.333)*200 300 200 200 300 100 0 0]'/1000;
% 水泵静扬程 he 单位 m 书本 P85 的图 5.7 对管段 [10]、[11] 的水泵静扬程的标注多了负号
he = [48.96 0 0 0 0 0 0 0 0 12 48]';
% 水泵内阻系数 sp
sp = [138.5 0 0 0 0 0 0 0 0 0 0]';
% 水泵流量 qp 单位 m^3/s
% 等于管段流量 q

% 设计水头 H 单位 m
syms H [size(A,1) 1] real; 
H(1) = sym(0);
H(6) = sym(48.5);
% 节点地面标高 H_att 单位 m 
H_att = [0 13.60 18.80 19.10 22.00 32.20 18.30 17.30 17.50]';

% 曼宁粗糙系数 nM
nM = 0.012;
% 与水头损失计算指数公式相同的指数 n
n = 1.852;
% 环水头闭合差的最大允许值 eh 单位 m
eh = 0.01;

% 计算管段阻力系数 s
s = GetPipeResistanceCoefficient(L, D, sp, nM, 'Manning');
% 改进的哈代-克罗斯算法
[hf, hp, h] = ImprovedHardyClausProcess_LoopFlowCorrection(Bf, q, L, D, s, he, sp, nM, n, eh, 'Manning');
% 管段流速 v 单位 m/s
v = q./(pi*D.^2/4);
% 求解管段压降方程组
H = PressureDropEqsSolver(A, h, H);
% 计算自由水压
H_free = H - H_att;

7.管网节点方程组水力分析 牛顿-拉夫森算法求解节点校正压力方程组


% 书本 例5.4 管网节点方程组水力分析 牛顿-拉夫森算法求解节点校正压力方程组

% 1.
% 书本 P87 的式 (5.35) 的关联矩阵的第一行应写为 1 0 1 0 0 0 0

% 2.
% 排掉了之前的 bug,计算的时候发现,q 算的不对
% 如果 L, D 都没错的话,那就只能是 sp 的问题了
% 因此我才发现题目没说明 sp,就真的是全为零,这书也太坑了吧
    
% 3.
% 不知道为什么,书 P93 的管网节点压力平差计算过程数据表中,没有把管段 [9],[10],[11] 的数据展示出来
% 本来就很难比对我的计算结果对不对,他不写的话,直接就根本比对不聊了
% 更别说我还是把并联管道化简到一根了,难顶

clear;

% 关联矩阵 A 
A = [
     1     0     0     0     0     0     0     0     0
    -1     1     0     0     1     0     0     0     0
     0    -1     1     0     0     1     0     0     0
     0     0    -1    -1     0     0     1     0     0
     0     0     0     1     0     0     0     0     0
     0     0     0     0    -1     0     0     1     0
     0     0     0     0     0    -1     0    -1     1
     0     0     0     0     0     0    -1     0    -1];
% 节点流量 Q 单位 m^3/s 初值已知
Q = [-198.08 14.55 51.17 20.77 -33.42 35.03 82.33 27.65]'/1000;
% 管段长度 L 单位 m
L = [320 650 550 270 330 350 360 590 490]';
% 管段直径 D 单位 m
D = [2^(2/5.333)*300 300 200 2^(2/5.333)*200 300 200 200 300 100]'/1000;
% 水泵静扬程 he 单位 m 未知
he = [0 0 0 0 0 0 0 0 0]';
% 水泵内阻系数 sp 题目虽然没有给出,但是根据题目解答给出的值,应该是和 例5.3 相同的数值
sp = [0 0 0 0 0 0 0 0 0]';
% 水泵流量 qp 单位 m^3/s
% 等于管段流量 q

% 节点水头初值 H 单位 m
H = [46 43 40 39 40 42 39 37.5]';
% 要求解的节点水头的下标
H_var_subnum = [1 2 3 4 5 6 7]';
% 节点压力的校正公式
H_CorrectionFun = @(H,delta_H) H+0.5*delta_H;
% 节点地面标高 H_att 单位 m 
H_att = [13.60 18.80 19.10 22.00 32.20 18.30 17.30 17.50]';

% 海曾威廉系数 Cw
Cw = 110;
% 与水头损失计算指数公式相同的指数 n
n = 1.852;
% 节点流量闭合差的最大允许值 eq 单位 L/s
% 取太小了矩阵就奇异了,没办法,就取大一点吧
eq = 0.01;

% 牛顿-拉夫森算法
[q, H] = NewtonRaphsonProcess_NodalPressureCorrection(A, H, H_var_subnum, Q, L, D, sp, Cw, n, eq, 'n', H_CorrectionFun);
% 管段流速 v 单位 m/s
v = q./(pi*D.^2/4);
% 计算自由水压
H_free = H - H_att;

8.习题2


% 书本 习题5.2 

% 管网恒定流方程组
% 1.质量守恒方程 Σ(j∈Ωi)(qij)+Qi=0
% 2.能量守恒方程 Σ(j∈Λi)(hi,j)=0
% 3.物理关系方程 hi,j=si,j*qi,j^α
% 管段流量 qi,j
% 管段水头损失 hi,j
% 节点流量 Qi
% 与节点 i 关联的节点集合 Ωi
% 环 i 的管段集合 Λi
% 管段摩阻 si,j 当节点 i 与节点 j 不相关联,si,j -> ∞
% 指数 α

% 当水塔高度未知时,应给定水塔供水流量
% 即已知节点流量,该节点为定流节点,通过水力分析可以求解出节点压力,从而确定水塔高度
% 当水塔高度已经确定时
% 即已知该节点水头,该节点为定压节点,通过水力分析可以求解出节点流量,从而确定水塔供水量

% 1.
% 如上所述,如果水塔高度要确定节点水头的话,那就是地面标高为零才能让我这么干了
% 所以我把地面标高都设为了 0

% 关联矩阵 A 
A = [
     1     0     0
     0     1     0
     0     0     1
    -1    -1    -1];
% 管段流量 q 单位 m^3/s 全部未知
q = sym(zeros(size(A,2),1));
% 节点流量 Q 单位 m^3/s 全部未知
Q = sym(zeros(size(A,1),1));
% 管段长度 L 单位 m
L = [200 150 300]';
% 管段直径 D 单位 m
D = [100 100 150]'/1000;
% 水泵静扬程 he 单位 m
he = [0 0 0]';
% 水泵内阻系数 sp
sp = [0 0 0]';
% 水泵流量 qp 单位 m^3/s
% 等于管段流量 q

% 设计水头 H 单位 m
% 每题有所不同
% 节点地面标高 H_att 单位 m 
H_att = [0 0 0 0]';

% 海曾威廉系数 Cw
Cw = 100;
% 与水头损失计算指数公式相同的指数 n
n = 1.852;
% 节点流量闭合差的最大允许值 eq 单位 L/s
eq = 0.01;

% 第一问

% 设计水头 H 单位 m
H = [25.20 24.70 28.30 20]';
% 求解管段压降方程组
h = A'*H;
% 计算管段阻力系数 s
s = GetPipeResistanceCoefficient(L, D, sp, Cw);
% 根据 qj=(hj/sj)^(1/n),(j=1,2,...,M) he 都为零
q = (h./s).^(1/n);
% 求解节点流量方程组
Q = -A*q;

% 第二问

% 水塔供水,说明用水点水头低于水塔水头
% 水塔进水,说明用水点水头高于水塔水头
% 水塔不供水也不进水,说明用水点水头与水塔水头相等

% 综上,第二问情境下,用水点与水塔水头中位数相等

% 设计水头 H 单位 m
H = [25.20 24.70 28.30 25.20]';
% 求解管段压降方程组
h = A'*H;
% 计算管段阻力系数 s
s = GetPipeResistanceCoefficient(L, D, sp, Cw);
% 根据 qj=(hj/sj)^(1/n),(j=1,2,...,M) he 都为零
q = (h./s).^(1/n);
% 求解节点流量方程组
Q = -A*q;

% 第三问

% 设计水头 H 初值 单位 m
H = [25.20 24.70 28.30 20]';
% 水头变量下标记录
H_var_subnum = [4]';
% 设置 Q 初值 单位 L/s
Q_initial = [-16 -16 -18 50]';
% do...while 型
while 1
    % 设置 Q 初值 单位 m^3/s
    Q = Q_initial/1000;
    % 牛顿-拉夫森算法
    [q, H] = NewtonRaphsonProcess_NodalPressureCorrection(A, H, H_var_subnum, Q, L, D, sp, Cw, n, eq);
    % 求解节点流量方程组
    Q = -A*q;
    % 解得用水点的用水量少于 50 L/s,那么给初值增加一点
    if Q(4)-50/1000 < -(1e-5)
        Q_initial(4) =  Q_initial(4) + 1;
    % 解得用水点的用水量少于 50 L/s,那么给初值减少一点
    elseif Q(4)-50/1000 > 1e-5
        Q_initial(4) =  Q_initial(4) - 1;
    % 解得用水点的用水量与 50 L/s 之间的误差在允许范围内,退出迭代
    else
        break;
    end
end
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/675859.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号