<think>
我们有一个MATLAB函数`main`,它执行了一系列操作,包括数据处理、灰色关联分析、拟合以及绘图。下面我将逐段分析代码,并解释每个部分的功能和函数调用。
### 1. 初始化和数据读取
```matlab
function main()
%%%%%%%%%%%%%%%%%%%%%%%-------注入与产出数据处理-------%%%%%%%%%%%%%%%%%%%%%%
close all; clear; clc;
```
- `close all`: 关闭所有图形窗口。
- `clear`: 清除工作区变量。
- `clc`: 清除命令窗口。
```matlab
%形成生产井数据
[num,txt]=xlsread('pro.xls');
proname = num(:,1); %n*1---井名的列向量
num = num(:,2:end); %num(n,:)为第n口井在各时间点上的数据,行为井
```
- `xlsread('pro.xls')`: 从Excel文件`pro.xls`中读取数据。`num`是数值数据,`txt`是文本数据。
- `proname = num(:,1)`: 提取第一列作为生产井的井名(列向量)。
- `num = num(:,2:end)`: 提取从第二列到最后一列的数据,即各时间点上的生产数据(每行代表一口井,每列代表一个时间点)。
```matlab
%num = num';
[hNum,lNum] = size(num);
for i=1:2:lNum
pro1(:,(1+i)/2) = num(:,i) + num(:,i+1); %产液
pro1_hsl(:,(1+i)/2) = num(:,i+1)./(num(:,i) + num(:,i+1)+0.00001*ones(length(num(:,i+1)),1)); %含水率
end
```
- 注意:原代码中`num = num';`被注释掉了,所以`num`的行是井,列是时间点(每个月两个数据点?)。
- `size(num)`: 获取`num`矩阵的大小,`hNum`是行数(井的数量),`lNum`是列数(时间点数量)。
- 循环`i=1:2:lNum`: 每次取两列(相邻两个月)进行处理。
- `pro1(:, (1+i)/2) = num(:,i) + num(:,i+1)`: 将相邻两个月的产油量和产水量相加,得到产液量(即总产量)。注意:`(1+i)/2`会生成1, 1.5, 2, ...,但MATLAB下标只能是整数,这里实际上会取整(向下取整?)或者报错?实际上,当`i`为奇数时,`(1+i)/2`为整数(1,2,3,...)。
- `pro1_hsl(:, (1+i)/2) = num(:,i+1)./(...)`: 计算含水率(产水量/产液量)。为了避免除以0,分母加了一个很小的数(0.00001)。
```matlab
%%%%形成注入数据
[num,txt]=xlsread('inj.xls');
injname = num(2:end,1)'; %n*1---井名的行向量
num = num';
inj1=num(:,2:end); %inj1(:,n)为第n口井在各时间点上的数据,行为时间
```
- 读取注入井数据文件`inj.xls`。
- `injname = num(2:end,1)'`: 第一列(从第二行开始)作为注入井名称,并转置为行向量。
- `num = num'`: 转置整个矩阵。
- `inj1 = num(:,2:end)`: 去掉第一列(井名),剩下的数据是各时间点的注入量。此时,`inj1`的每一列代表一口注入井,每一行代表一个时间点。
```matlab
%提取计算数据,3个月为1组
pro1 = pro1'; pro1_hsl = pro1_hsl';
for i=1:3:size(pro1,1)
pro((2+i)/3,:) = pro1(i,:)+pro1(i+1,:)+pro1(i+2,:);
pro_hsl(:,(2+i)/3) = (pro1_hsl(i,:)+pro1_hsl(i+1,:)+pro1_hsl(i+2,:))./3; %含水率
inj((2+i)/3,:) = inj1(i,:)+inj1(i+1,:)+inj1(i+2,:);
end
```
- 将生产井的产液量和含水率转置(现在行是时间,列是井)。
- 循环`i=1:3:size(pro1,1)`: 每3个月(三个时间点)进行累加(产液量和注入量)或平均(含水率)。
- `pro((2+i)/3,:) = ...`: 累加三个月的产液量,形成新的时间段数据(季度数据)。
- `pro_hsl(:, (2+i)/3) = ...`: 取三个月的含水率平均值。
- `inj((2+i)/3,:) = ...`: 累加三个月的注入量。
### 2. 井位坐标处理
```matlab
[cdpro,tmp] = xlsread('coord_pro.xls'); %cdpro(n,:)--第n口井xy坐标
procdname=cdpro(:,1); %procdname列井名
[cdinj,tmp] = xlsread('coord_inj.xls');%cdinj(n,:)--第n口井xy坐标,
injcdname = cdinj(:,1); %列井名
```
- 读取生产井和注入井的坐标文件。
- 提取井名和坐标。
```matlab
%计算注入井和各生产井之间的距离
for i = 1:size(cdinj,1)
for j=1:size(cdpro,1)
inj_pro_dis(j,i) = norm(cdpro(j,:)-cdinj(i,:));
end
end
```
- 双重循环计算每口注入井到每口生产井的欧氏距离(`norm`函数)。
- `inj_pro_dis`矩阵:行对应生产井,列对应注入井。
```matlab
for i=1:size(inj_pro_dis,2)
[y I]=sort(inj_pro_dis(:,i),'ascend');
dis_sort(:,i)=I;
end
```
- 对每口注入井(每列),将生产井按照距离从小到大排序,`I`是排序后的索引(即生产井的序号)。
- `dis_sort`矩阵:每列对应一口注入井,存储按距离从小到大排序的生产井索引。
### 3. 灰色关联度分析
```matlab
xi=pro'; [rows,cols]=size(xi); p= 0.3 %含水率波动太小
```
- 这里选择使用产液量(`pro`)作为分析数据(因为注释了含水率)。但注意前面代码中`pro`是季度产液量(时间×井),转置后`xi`是井×时间。
- `rows`是生产井数量,`cols`是时间段数量。
- `p`是灰色关联分析中的分辨系数。
```matlab
for k = 1:size(inj,2)
x0 = inj(:,k)'; %母数据(注入井k在每个时间段的注入量)
%求差值
for i = 1:rows
deta0i(i,:)=abs(xi(i,:) - x0);
end
detamax = max(max(deta0i)); detamin = min(min(deta0i)); %得到最大最小值
%求灰色关联系数
for i = 1:rows %rows个油井
for j = 1:cols
kc0i(i,j) = (detamin + p*detamax)/(deta0i(i,j)+p*detamax);
end
end
out(:,k) = mean(kc0i,2);
```
- 对每口注入井(`k`从1到注入井数量):
- `x0`为该注入井在所有时间段的注入量(行向量)。
- 计算每个生产井(`xi(i,:)`)与注入井`k`的绝对差值`deta0i`。
- 计算整个差值矩阵的最大值`detamax`和最小值`detamin`。
- 计算灰色关联系数矩阵`kc0i`(每个元素对应一个时间点上一个生产井与注入井的关联程度)。
- 对每个生产井,计算关联系数在时间上的平均值`out(:,k)`(列向量)。
```matlab
%井距离限制
for i = 1:rows
if inj_pro_dis(i,k) > inj_pro_dis(dis_sort(12,k),k)
out(i,k) = out(i,k) - 1;
end
% if(size(find(xi(i,:)<=0.01),2) > size(xi,2)*0.7) %去掉很多数据为0的井
% out(i,k) = out(i,k) - 2;
% end
end
end
```
- 对每口注入井的关联度进行距离惩罚:如果生产井`i`到注入井`k`的距离大于该注入井对应的第12近的生产井的距离(即`dis_sort(12,k)`对应的距离),则关联度减去1(降低关联度)。
- 注释掉的部分:如果某个生产井在超过70%的时间段内产液量接近于0(<=0.01),则关联度再减去2(这里未使用)。
```matlab
for i=1:size(out,2)
[y I]=sort(out(:,i),'descend');
well(:,i)=I;
end
```
- 对每口注入井,根据调整后的关联度`out(:,i)`从大到小排序,得到关联度最高的生产井索引(存储在`well`矩阵中,每列对应一口注入井,按关联度从高到低排列生产井索引)。
### 4. 拟合(计算连通系数和延迟)
```matlab
nadaxls = zeros(size(proname,2),size(injname,2));
```
- 初始化`nadaxls`矩阵(生产井数量×注入井数量),用于存储连通系数。
```matlab
for i = 1:size(inj,2) %注入井
pron = [pro(:,well(1,i)) pro(:,well(2,i)) pro(:,well(3,i)) pro(:,well(4,i)) pro(:,well(5,i)) ...,
pro(:,well(6,i)) pro(:,well(7,i)) pro(:,well(8,i)) pro(:,well(9,i)) pro(:,well(10,i))];
thiswell = [proname(well(1,i)) proname(well(2,i)) proname(well(3,i)) proname(well(4,i)) proname(well(5,i)) ...,
proname(well(6,i)) proname(well(7,i)) proname(well(8,i)) proname(well(9,i)) proname(well(10,i))];
j = 1;
injn = inj(:,i);
while(1)
if sum(pron(j,:) < 0.01) == size(pron,2)
pron(j ,:) = [];
injn(j ,:) = [];
j = j-1;
end
j = j+1;
if(j == size(pron,1))
break
end
end
[nadaout(:,i),dout(:,i)] = cal_nada(pron,injn,i,thiswell);
i
for kk = 1:10
if (nadaout(kk,i) < 0.01)
nadaout(kk,i) = 0;
end
nadaxls(well(kk,i),i) = nadaout(kk,i);
delayxls(well(kk,i),i) = dout(kk,i);
end
end
```
- 对每口注入井`i`:
- `pron`:提取关联度排名前10的生产井的产液量(按季度),每列是一个生产井,每行是一个时间段(季度)。
- `thiswell`:这10口生产井的井名。
- `injn`:当前注入井`i`的注入量(按季度)。
- 循环去除产液量全为0(接近于0)的时间段(行):如果某一行(时间段)所有生产井的产液量都小于0.01,则删除该行(同时删除注入量的对应行)。
- 调用函数`cal_nada(pron, injn, i, thiswell)`,返回两个向量:
- `nadaout(:,i)`: 10口生产井的连通系数(向量)。
- `dout(:,i)`: 10口生产井的延迟(向量)。
- 将连通系数和延迟分别存储到矩阵`nadaxls`和`delayxls`中(注意索引:`well(kk,i)`是第`kk`个关联的生产井的全局索引)。
### 5. 保存结果
```matlab
dlmwrite('xlsnada',nadaxls);
dlmwrite('xlsdelay',delayxls);
dlmwrite('xlsnada8',nadaout);
dlmwrite('xlsdelay8',dout);
dlmwrite('nadaout',nadaout);
dlmwrite('nadout',dout);
dlmwrite('bbb',nadaxls);
```
- 使用`dlmwrite`将矩阵写入文本文件(默认分隔符为逗号)。
### 6. 绘图
```matlab
for i=1:size(well,2)
figure(i);
plot(inj1(:,i),'bv-','LineWidth',2);hold on;
plot(pro1(:,well(1,i)),'r-','LineWidth',1);hold on;
... % 类似地画出其余5口生产井的数据
% 添加图例
s1 = num2str(injname(i)); ... % 注入井名称
... % 生产井名称
legend(s1,s2,s3,s4,s5,s6);
Title(strcat('注入井:',num2str(injname(i))));
... % 保存图片
end
```
- 为每口注入井绘制一张图:注入量和关联度最高的5口生产井的产液量(原始月度数据)随时间变化的曲线。
```matlab
%成果图
figure(size(well,2) + 1);
plot(cdpro(:,1),cdpro(:,2),'ro','MarkerFaceColor','r');hold on;
... % 标注生产井名称
plot(cdinj(:,1),cdinj(:,2),'bo','MarkerFaceColor','b');hold on;
... % 标注注入井名称
mindis = 500;
for i=1:size(well,2)
... % 绘制注入井到生产井的连线(三角形),其宽度代表连通系数
end
axis square;
print(gcf,'-dpng','成果图.png');
```
- 绘制所有井的坐标图(生产井为红色,注入井为蓝色),并标注名称。
- 对每口注入井,为其关联度前10的生产井绘制从注入井到生产井的三角形(表示连通性),三角形的大小由连通系数`nadaout`决定(通过函数`plot_nada`实现)。
### 7. 自定义函数`plot_nada`
```matlab
function plot_nada(injpos,propos,nada,heigh)
if nada > 0.001
... % 计算三角形的位置并绘制填充三角形
end
end
```
- 参数:注入井位置`injpos`,生产井位置`propos`,连通系数`nada`,基准高度`heigh`(500)。
- 当连通系数大于0.001时,计算一个三角形(以注入井为顶点,底边中点位于注入井到生产井的连线上,且底边宽度与连通系数成正比),并用红色填充。
### 总结
该代码主要功能:
1. **数据处理**:从Excel文件中读取生产井和注入井的月度数据,并聚合成季度数据。
2. **井位距离计算**:计算注入井与生产井之间的欧氏距离,并排序。
3. **灰色关联分析**:计算每口注入井与各生产井的关联度,并考虑距离惩罚。
4. **拟合**:对每口注入井关联度最高的10口生产井,调用`cal_nada`函数计算连通系数和延迟时间。
5. **绘图**:
- 绘制每口注入井及其关联生产井的产液量曲线。
- 绘制井位分布图,并用三角形表示注入井与生产井之间的连通性(三角形大小表示连通系数)。
### 注意
- 代码中调用了自定义函数`cal_nada`,但在提供的代码中未给出其实现。
- 代码中的`pro1`和`pro1_hsl`等变量在循环后未被清除,可能会占用较多内存。
- 在去除全零时间段的循环中,使用`while`循环并动态删除行,同时调整索引,需要小心。
### 相关问题
1. 灰色关联分析中的分辨系数$p$如何影响分析结果?[^1]
2. 在计算灰色关联系数时,为什么需要加入一个很小的常数(0.00001)?[^1]
3. 如何理解`plot_nada`函数中绘制的三角形?其几何参数如何反映连通系数?[^1]
</think>
以下是对您提供的MATLAB代码的逐行详细分析,包括函数调用和执行逻辑的解释。代码主要实现油田生产数据分析,包括数据处理、灰色关联分析、连通系数计算和可视化。
---
### **1. 初始化和数据加载**
```matlab
function main()
close all; clear; clc;
```
- `close all`:关闭所有图形窗口
- `clear`:清除工作区变量
- `clc`:清除命令窗口内容
- `function main()`:定义主函数入口
```matlab
[num,txt]=xlsread('pro.xls');
proname = num(:,1); % 生产井名称(列向量)
num = num(:,2:end); % 生产数据矩阵(井×时间点)
```
- `xlsread('pro.xls')`:读取生产井Excel文件
- `num`:数值数据
- `txt`:文本数据(未使用)
- `proname`:提取第一列作为井名
- `num(:,2:end)`:保留生产数据(移除井名列)
```matlab
[hNum,lNum] = size(num);
for i=1:2:lNum
pro1(:,(1+i)/2) = num(:,i) + num(:,i+1); % 产液量计算
pro1_hsl(:,(1+i)/2) = num(:,i+1)./(num(:,i) + num(:,i+1)+0.00001*ones(...)); % 含水率计算
end
```
1. `size(num)`:获取数据矩阵维度
2. 循环处理每两个月的数据:
- **产液量**:相邻两月数据相加
- **含水率**:产水量/(产油量+产水量),避免除以0加小常数`0.00001`
- `ones(length(...),1)`:生成全1向量用于广播计算
---
### **2. 注入数据处理**
```matlab
[num,txt]=xlsread('inj.xls');
injname = num(2:end,1)'; % 注入井名称(行向量)
num = num';
inj1 = num(:,2:end); % 注入数据(时间×井)
```
- 读取注入井数据并转置
- `inj1`:注入量数据矩阵,每列代表一口注入井
```matlab
pro1 = pro1'; pro1_hsl = pro1_hsl';
for i=1:3:size(pro1,1)
pro((2+i)/3,:) = sum(pro1(i:i+2,:),1); % 季度产液量
pro_hsl(:,(2+i)/3) = mean(pro1_hsl(i:i+2,:)); % 季度平均含水率
inj((2+i)/3,:) = sum(inj1(i:i+2,:),1); % 季度注入量
end
```
- 数据按季度(3个月)聚合:
- **季度产液量**:三个月产液量之和
- **季度含水率**:三个月平均值
- **季度注入量**:三个月注入量之和
- `sum(...,1)`:沿行方向求和
- `mean(...)`:沿行方向求平均
---
### **3. 井位坐标处理**
```matlab
[cdpro,tmp] = xlsread('coord_pro.xls'); % 生产井坐标
procdname = cdpro(:,1); % 生产井名
[cdinj,tmp] = xlsread('coord_inj.xls'); % 注入井坐标
injcdname = cdinj(:,1); % 注入井名
```
- 读取井位坐标文件
- 提取井名和坐标数据
```matlab
for i = 1:size(cdinj,1)
for j=1:size(cdpro,1)
inj_pro_dis(j,i) = norm(cdpro(j,:)-cdinj(i,:)); % 欧氏距离计算
end
end
```
- 计算所有注入井-生产井对的欧氏距离
- `norm(A-B)`:计算向量A和B的欧氏距离 $$ \| \mathbf{A} - \mathbf{B} \|_2 $$
```matlab
for i=1:size(inj_pro_dis,2)
[y,I] = sort(inj_pro_dis(:,i),'ascend');
dis_sort(:,i) = I; % 距离排序索引
end
```
- 对每口注入井,将生产井按距离升序排序
- `sort(...,'ascend')`:升序排序,返回排序索引 `I`
---
### **4. 灰色关联分析**
```matlab
xi = pro'; [rows,cols] = size(xi); p = 0.3;
for k = 1:size(inj,2)
x0 = inj(:,k)'; % 注入数据作为母序列
for i = 1:rows
deta0i(i,:) = abs(xi(i,:) - x0); % 绝对差值
end
detamax = max(max(deta0i)); % 最大差值
detamin = min(min(deta0i)); % 最小差值
```
- 灰色关联分析核心步骤:
1. 母序列 `x0`:注入井数据
2. 子序列 `xi`:生产井数据
3. 计算绝对差值矩阵 $$ \Delta_i(k) = |x_0(k) - x_i(k)| $$
```matlab
for i = 1:rows
for j = 1:cols
kc0i(i,j) = (detamin + p*detamax)/(deta0i(i,j) + p*detamax); % 关联系数
end
end
out(:,k) = mean(kc0i,2); % 平均关联度
```
- **关联系数计算**:
$$
\gamma_i(k) = \frac{\Delta_{\min} + \rho \Delta_{\max}}{\Delta_i(k) + \rho \Delta_{\max}}
$$
- $\rho=0.3$ 为分辨系数
- `mean(kc0i,2)`:计算每口生产井的平均关联度
```matlab
for i = 1:rows
if inj_pro_dis(i,k) > inj_pro_dis(dis_sort(12,k),k)
out(i,k) = out(i,k) - 1; % 距离惩罚
end
end
end
```
- **距离惩罚机制**:关联度排名12之后的生产井关联度减1
- 筛选出与注入井关联度最高的生产井
---
### **5. 连通系数计算**
```matlab
nadaxls = zeros(size(proname,2), size(injname,2));
for i = 1:size(inj,2)
pron = [pro(:, well(1:10,i))]; % 提取Top10生产井数据
thiswell = proname(well(1:10,i)); % 井名
injn = inj(:,i); % 当前注入井数据
% 清除无效数据(产量<0.01)
j = 1;
while j <= size(pron,1)
if all(pron(j,:) < 0.01)
pron(j,:) = []; injn(j) = [];
else
j = j+1;
end
end
[nadaout(:,i), dout(:,i)] = cal_nada(pron, injn, i, thiswell); % 核心拟合函数
end
```
- **数据清洗**:移除产量接近0的时间段
- **核心拟合**:调用 `cal_nada()` 函数计算:
- `nadaout`:连通系数向量
- `dout`:延迟时间向量
- 结果存储到矩阵 `nadaxls` 和 `delayxls`
---
### **6. 结果输出与可视化**
```matlab
dlmwrite('xlsnada', nadaxls); % 写入连通系数
dlmwrite('xlsdelay', delayxls); % 写入延迟时间
for i=1:size(well,2)
figure(i);
plot(inj1(:,i), 'bv-', 'LineWidth',2); hold on;
plot(pro1(:,well(1,i)), 'r-', 'LineWidth',1);
... % 绘制Top5生产井曲线
legend(injname(i), proname(well(1:5,i)));
title(['注入井:', num2str(injname(i))]);
print(gcf, '-dpng', ['z_', num2str(injname(i)), '.png']);
end
```
- **文件输出**:`dlmwrite()` 将结果写入文本文件
- **单井可视化**:对每口注入井绘制其与Top5生产井的产量/注入量曲线
- `plot()`:标准绘图函数
- `print()`:保存为PNG图像
```matlab
figure(size(well,2)+1);
plot(cdpro(:,1), cdpro(:,2), 'ro', 'MarkerFaceColor','r'); % 生产井位置
text(cdpro(:,1)+5, cdpro(:,2)+5, num2str(procdname));
plot(cdinj(:,1), cdinj(:,2), 'bo', 'MarkerFaceColor','b'); % 注入井位置
text(cdinj(:,1)+5, cdinj(:,2)+5, num2str(injcdname));
mindis = 500;
for i=1:size(well,2)
for k=1:10
plot_nada(cdinj(i,:), cdpro(well(k,i),:), nadaout(k,i), mindis); % 绘制连通线
end
end
print(gcf, '-dpng', '成果图.png');
```
- **井位分布图**:
- 红色圆圈:生产井位置
- 蓝色圆圈:注入井位置
- **连通关系可视化**:调用 `plot_nada()` 绘制注入井-生产井的连通三角形
- 三角形大小表示连通系数大小
---
### **7. 自定义函数分析**
#### **plot_nada() 函数**
```matlab
function plot_nada(injpos, propos, nada, heigh)
if nada > 0.001
distmp = norm(injpos - propos); % 计算井间距离
fdk = nada*heigh/(distmp - nada*heigh); % 比例因子
B = (injpos + fdk*propos)/(1+fdk); % 三角形顶点计算
% 三角形坐标计算
l = nada * heigh * tan(nada/2);
a = injpos(1); b = injpos(2);
m = B(1); n = B(2);
C1x = m - l*(a-m)/norm([a-m, b-n]);
... % 类似计算其他顶点
fill([a C1x C2x a], [b C1y C2y b], 'r'); % 绘制填充三角形
end
end
```
- **功能**:绘制表示井间连通性的三角形
- **输入参数**:
- `injpos`:注入井坐标
- `propos`:生产井坐标
- `nada`:连通系数
- `heigh`:基准高度(500)
- **关键计算**:
1. 三角形顶点位置基于连通系数动态计算
2. `fill()`:绘制红色填充三角形,面积正比于连通系数
#### **cal_nada() 函数**
- **未提供实现**:但从上下文推断其功能为:
- 输入:生产井产量矩阵 `pron`,注入井数据 `injn`
- 输出:连通系数向量 `nadaout`,延迟时间向量 `dout`
- 可能算法:时间序列相关性分析或最小二乘拟合
---
### **代码总结**
1. **数据流**:
```mermaid
graph LR
A[原始Excel数据] --> B[季度聚合]
B --> C[灰色关联分析]
C --> D[连通系数计算]
D --> E[可视化输出]
```
2. **核心算法**:
- 灰色关联分析评估井间关系
- 基于地理距离的惩罚机制
- 连通系数的空间可视化
3. **关键技术点**:
- 时间序列数据聚合(月→季度)
- 矩阵运算优化循环(`mean()`, `sum()`)
- 基于欧氏距离的空间分析
- 动态几何图形生成
---
### **相关问题**
1. 灰色关联分析中分辨系数$ρ$的选择如何影响井间关联度排序结果?[^1]
2. 在计算含水率时加入小常数$0.00001$在数值稳定性中的作用是什么?[^1]
3. `plot_nada`函数生成的三角形面积如何量化表示井间连通性?[^1]
4. 如何扩展此代码处理大规模油田(>1000口井)的数据?[^2][^3]
5. 时间延迟$dout$的计算可能采用哪些时间序列分析方法?[^4]
[^1]: 参考灰色系统理论和数值计算方法
[^2]: 参考UKF滤波在大规模系统中的应用
[^3]: 参考交互式多模型(IMM)滤波算法
[^4]: 参考自适应IMM算法中的协方差更新