Reservoir Simulator Starter 水库调度模拟器stater
水库调度模拟工具包摘要 Reservoir Simulator Starter 是一个基于 Spring Boot 的水库调度模拟工具,支持水文数据处理、多关系表计算及常规调度模拟。主要功能包括: 水文数据处理:自动将自然年流量转为水文年数据 关系表计算:支持水位-库容、流量-水位、水头-出力等插值 调度模拟:基于保证出力算法,输出水位、库容、发电量等结果 灵活配置:通过YAML文件调整参数,支持
·
1. 概述
Reservoir Simulator Starter 是一个基于 Spring Boot 的水库调度模拟工具包,支持从文本文件读取水文数据,并通过常规调度算法进行水库运行模拟。该工具包适用于水库调度、发电量计算、保证率分析等场景。
2. 功能特性
- 水文数据处理:支持流量数据的自动重排(自然年转水文年)
- 多关系表支持:水位-库容、流量-水位、水头-出力等关系表插值计算
- 常规调度模拟:基于保证出力的水库调度算法
- 结果输出:生成上游水位、库容、发电流量、出力、弃水、水头、发电量等完整结果
- 灵活配置:通过配置文件调整水库参数和文件路径
- 高性能:优化的插值算法和迭代计算
3. 快速开始
3.1 添加依赖
在 pom.xml 中添加以下依赖:
<dependency>
<groupId>com.water.reservoir</groupId>
<artifactId>reservoir-simulator-starter</artifactId>
<version>1.0.0</version>
</dependency>
3.2 配置文件
在 application.yml 中配置水库参数和文件路径:
reservoir-simulator:
input-path: "classpath:data/" #输入文件夹路径支持类路径、相对路径、绝对路径
output-path: "output/" #输出文件夹路径支持 相对路径、绝对路径
# 输入文件名
flow-file: "流量.txt"
level-volume-file: "水位库容.txt"
flow-level-file: "水位流量.txt"
head-power-file: "水头出力.txt"
# 输出文件名
hydro-flow-file: "水文年排列的流量.txt" #临时使用看出转化水文年是否正确
upstream-level-file: "常规调度下的上游水位.txt"
reservoir-volume-file: "常规调度下的库容.txt"
gen-flow-file: "常规调度下的发电流量.txt"
power-output-file: "常规调度下的出力.txt"
water-loss-file: "常规调度下的弃水.txt"
net-head-file: "常规调度下的水头.txt"
energy-file: "常规调度下的发电量.txt"
# 模拟参数
dead-water-level: 328.5 # 死水位
early-flood-limit-level: 346.30 # 防洪限制水位-前汛期
late-flood-limit-level: 346.80 # 防洪限制水位-后汛期
normal-storage-level: 347.0 # 正常库容
power-coefficient: 7.74 # 出力系数
guaranteed-output: 1.51 # 保证出力
convergence-precision: 0.00001 #检验值
relaxation-factor: 0.3 # 加权数
y: 48 # 流量资料年数
T: 365 #一年月数
M: 12 # 入流流量数据个数
NUM1: 24 # 水位库容关系点数
NUM2: 12 #下泄流量与水位关系点数
NUM3: 3 #水头限制出力关系点数
3.3 数据文件格式
流量数据文件(流量.txt)
- 格式:每月流量数据(m³/s),按自然年顺序排列(1月-12月)
- 数据量:48年 × 12月 = 576个数据点
- 示例:
120.5 115.3 98.7 85.4 76.2 68.9 75.4 89.6 105.7 118.9 125.6 130.2
118.7 112.9 95.8 82.1 74.5 67.3 73.8 87.9 103.5 116.8 123.4 128.7
...
水位-库容关系文件(水位库容.txt)
- 格式:两列数据,第一列为水位(m),第二列为对应库容(亿m³)
- 数据点:24个
- 示例:
328.5 12.5
330.0 13.8
331.5 15.2
...
347.0 25.6
下泄流量-下游水位关系文件(水位流量.txt)
- 格式:两列数据,第一列为下泄流量(m³/s),第二列为下游水位(m)
- 数据点:12个
- 示例:
100 325.8
200 326.2
300 326.7
...
1000 328.5
水头-预想出力关系文件(水头出力.txt)
- 格式:两列数据,第一列为水头(m),第二列为预想出力(万kW)
- 数据点:3个
- 示例:
15.0 1.2
18.0 1.5
20.0 1.8
4. 核心 API
4.1 ReservoirSimulatorService 服务类
方法说明
-
simulate()- 功能:执行完整的水库调度模拟
- 返回值:
SimulationResult,包含模拟统计结果
-
testSimulation()- 功能:测试方法,使用默认配置进行模拟
- 返回值:
SimulationResult
示例代码
@RestController
@RequestMapping("/api/reservoir")
public class ReservoirController {
@Autowired
private ReservoirSimulatorService reservoirSimulatorService;
@PostMapping("/simulate")
public ResponseEntity<SimulationResult> simulate() {
try {
SimulationResult result = reservoirSimulatorService.simulate();
return ResponseEntity.ok(result);
} catch (Exception e) {
return ResponseEntity.badRequest().build();
}
}
@GetMapping("/test")
public ResponseEntity<SimulationResult> test() {
SimulationResult result = reservoirSimulatorService.testSimulation();
return ResponseEntity.ok(result);
}
}
4.2 SimulationResult 结果类
字段说明
totalEnergy:总发电量(kWh)averagePower:平均出力(万kW)reliability:保证率failureYears:破坏年份数message:执行消息
示例输出
{
"totalEnergy": 1254000000.0,
"averagePower": 1.48,
"failureYears": 0,
"reliability": 0.9583,
"message": "水库模拟完成"
}
4.3 核心算法流程
水库调度模拟的核心算法流程如下:
public class ReservoirSimulatorService {
private final ReservoirSimulatorProperties properties;
@Autowired
ResourceLoader resourceLoader;
public ReservoirSimulatorService(ReservoirSimulatorProperties properties) {
this.properties = properties;
}
public SimulationResult simulate() {
try {
return performSimulation();
} catch (Exception e) {
log.error("水库调度模拟执行失败", e);
throw new RuntimeException("水库调度模拟失败", e);
}
}
public SimulationResult testSimulation() {
log.info("开始测试水库调度模拟...");
SimulationResult result = simulate();
log.info("测试模拟完成,保证率: {}", result.getReliability());
return result;
}
private SimulationResult performSimulation() {
int T = this.properties.getT();
int NUM1 = this.properties.getNUM1();
int NUM2 = this.properties.getNUM2();
int NUM3 = this.properties.getNUM3();
int Y = this.properties.getY();
int M = this.properties.getM();
float Z_SS = (float) this.properties.getDeadWaterLevel(); // 死水位
float Z_QFH = (float) this.properties.getEarlyFloodLimitLevel(); // 防洪限制水位-前汛期
float Z_HFH = (float) this.properties.getLateFloodLimitLevel(); // 防洪限制水位-后汛期
float Z_ZC = (float) this.properties.getNormalStorageLevel(); // 正常蓄水位
// 定义变量
float[] Q = new float[T + 1]; // 流量数组,索引从1开始
float[] vcz = new float[NUM1]; // 库容
float[] zsy_cz = new float[NUM1]; // 上游水位
float[] qcz = new float[NUM2]; // 下泄流量
float[] zxy_cz = new float[NUM2]; // 下游水位
float[] z_mao = new float[NUM3]; // 水头
float[] n_yx = new float[NUM3]; // 出力限制
int i, j;
int n = 1;
// 初始化
Q[0] = 0.0f;
// 1. 读取流量.txt
String flowFileName = buildFilePath(properties.getInputPath(), properties.getFlowFile());
log.info("正在读取文件:{}", flowFileName);
try (Scanner fQ1i = getFileScanner(flowFileName)) {
for (i = 0; i < Y; i++) {
for (j = 0; j < M; j++) {
if (fQ1i.hasNextFloat()) {
Q[n] = fQ1i.nextFloat();
n++;
}
}
}
} catch (Exception e) {
log.error("无法读取文件:流量.txt", e);
System.out.println("can't open 流量.txt file");
System.exit(0);
}
// 2.创建"水文年排列的流量.txt" 重新读取数据到 Q 数组(从索引1开始)
String hydroFlowFile = buildFilePath(properties.getOutputPath(), properties.getHydroFlowFile(), true);
log.info("正在创建文件:{}", hydroFlowFile);
try (PrintWriter fQsw = new PrintWriter(hydroFlowFile)) {
// 第一部分:输出 Q[5] 到 Q[T]
for (i = 5; i <= T; i++) {
fQsw.printf("%6.2f\t", Q[i]); // 格式化输出:6字符宽,2位小数,制表符分隔
if ((i - 4) % 12 == 0) { // 每12个月换行
fQsw.println();
}
}
// 第二部分:输出 Q[1] 到 Q[4]
for (i = 1; i <= 4; i++) {
fQsw.printf("%6.2f\t", Q[i]);
}
// 可选:最后换行
fQsw.println();
} catch (FileNotFoundException e) {
log.error("无法创建文件:水文年排列的流量.txt", e);
System.out.println("无法创建文件:水文年排列的流量.txt");
System.exit(0);
}
// 3. 读取水位库容.txt
String waterLevelFileName = buildFilePath(properties.getInputPath(), properties.getLevelVolumeFile());
log.info("正在读取文件:{}", waterLevelFileName);
// 3. 读取水位库容.txt
try (Scanner fv_z = getFileScanner(waterLevelFileName)) {
for (i = 0; i < NUM1; i++) {
zsy_cz[i] = fv_z.nextFloat();
vcz[i] = fv_z.nextFloat();
}
} catch (Exception e) {
log.error("无法读取文件:水位库容.txt", e);
System.out.println("can't open 水位库容.txt file");
System.exit(0);
}
// 4. 读取水位流量.txt
String flowLevelFile = buildFilePath(properties.getInputPath(), properties.getFlowLevelFile());
log.info("正在读取文件:{}", flowLevelFile);
try (Scanner fq_z = getFileScanner(flowLevelFile)) {
for (i = 0; i < NUM2; i++) {
zxy_cz[i] = fq_z.nextFloat();
qcz[i] = fq_z.nextFloat();
}
} catch (Exception e) {
log.error("无法读取文件:水位流量.txt", e);
System.out.println("can't open 水位流量.txt file");
System.exit(0);
}
// 5. 读取水头出力.txt
String headPowerFile = buildFilePath(properties.getInputPath(), properties.getHeadPowerFile());
log.info("正在读取文件:{}", headPowerFile);
try (Scanner fn_z = getFileScanner(headPowerFile)) {
for (i = 0; i < NUM3; i++) {
z_mao[i] = fn_z.nextFloat();
n_yx[i] = fn_z.nextFloat();
}
} catch (Exception e) {
log.error("无法读取文件:水头出力.txt", e);
System.out.println("can't open 水头出力.txt file");
System.exit(0);
}
// 常规调度部分
float[] Qfd = new float[T + 1]; // 实际发电流量
float[] Qfd2 = new float[T + 1]; //
float Qfd3; //
float[] Qfd1 = new float[T + 1]; // 试算发电流量
float[] zsy = new float[T + 1]; // 上游水位
float[] zxy = new float[T + 1]; // 下游水位
float[] dzs = new float[T + 1]; // 水头损失
float[] dz = new float[T + 1]; // 水头损失
float[] dzs1 = new float[T + 1]; // 初始水头损失
float[] q_qs = new float[T + 1]; // 弃水
float[] v = new float[T + 2]; // 库容序列
float r = (float) this.properties.getConvergencePrecision(); // 收敛精度
float s = (float) this.properties.getRelaxationFactor(); // 加权系数
float k = (float) this.properties.getPowerCoefficient(); // 出力系数
float NP = (float) this.properties.getGuaranteedOutput(); // 保证出力
float[] N = new float[T + 1]; // 出力序列
float Vmax, Vmin; // 库容上下限
float Nyu; // 预想出力
int[] n_yt = new int[T + 1]; // 每月小时数
float yn = 0; // 破坏年份数
float b_y; // 保证率
float[] E = new float[T + 1]; // 发电量
// 初始化库容
v[1] = chazhi(Z_SS, zsy_cz, vcz, NUM1);
Vmin = chazhi(Z_SS, zsy_cz, vcz, NUM1);
// 主循环:逐月计算
for (i = 1; i < T + 1; i++) {
int month = i % 12;
// 汛期非汛期库容的限制值
if (month <= 3 && month >= 1) {
Vmax = chazhi(Z_QFH, zsy_cz, vcz, NUM1);
} else if (month <= 6 && month >= 4) {
Vmax = chazhi(Z_HFH, zsy_cz, vcz, NUM1);
} else {
Vmax = chazhi(Z_ZC, zsy_cz, vcz, NUM1);
}
// 设置每月小时数
if (month == 1 || month == 3 || month == 4 || month == 6 || month == 8 || month == 9 || month == 11) {
n_yt[i] = 744; //每月31天的月份
} else if (month == 10) {
n_yt[i] = 696; // 2月
} else {
n_yt[i] = 720; // 30天
}
// 迭代求解发电流量
for (; ; ) {
zsy[i] = chazhi(v[i], vcz, zsy_cz, NUM1); //插值计算上游水位
zxy[i] = chazhi(Qfd1[i], qcz, zxy_cz, NUM2); //插值计算下游水位
dzs1[i] = (float) (8.057 * Math.pow(10, -4) * Qfd1[i] * Qfd1[i] * 4); //水头损失值
if (dzs1[i] < 7) //水头损失确认
dzs[i] = dzs1[i];
else
dzs[i] = 7;
dz[i] = zsy[i] - zxy[i] - dzs[i];//实际水头
Qfd[i] = (float) (NP * Math.pow(10, 4) / (k * dz[i]));//由保证出力及水头计算发电流量
if (Math.abs(Qfd[i] - Qfd1[i]) >= r)//判断假设的发电流量是否合理
Qfd1[i] = s * Qfd[i] + (1 - s) * Qfd1[i];//重新定义发电流量的试算值
else
break;
}
Qfd2[i] = (Qfd[i] + Qfd1[i]) / 2;
v[i + 1] = (float) (v[i] + (Q[i] - Qfd2[i]) * 2.63 / 100); //由水量平衡计算末时段库容
Nyu = chazhi(dz[i], z_mao, n_yx, NUM3);//预想出力限制部分
if (v[i + 1] >= Vmin && v[i + 1] <= Vmax)//库容为限制条件,计算不同情况下的实际发电流量
{
N[i] = NP;
Qfd[i] = Qfd2[i];
} else if (v[i + 1] < Vmin) //降低出力
{
Qfd[i] = (float) (Q[i] + (v[i] - Vmin) * 100 / 2.63);
N[i] = (float) (Qfd[i] * k * dz[i] * Math.pow(10, -4));
v[i + 1] = (float) (v[i] + (Q[i] - Qfd[i]) * 2.63 / 100);
} else {
Qfd3 = (float) (Q[i] + (v[i] - Vmax) * 100 / 2.63); //加大出力
N[i] = (float) (Qfd3 * k * dz[i] * Math.pow(10, -4));
if (N[i] > Nyu) {
N[i] = Nyu;
Qfd[i] = (float) (Nyu * Math.pow(10, 4) / (k * dz[i]));
q_qs[i] = Qfd3 - Qfd[i];
} else
Qfd[i] = Qfd3;
v[i + 1] = (float) (v[i] + (Q[i] - Qfd3) * 2.63 / 100);
}
E[i] = N[i] * n_yt[i];
}
// 计算保证率
for (i = 0; i < Y; i++) {
for (j = 1; j < M + 1; j++) {
if (N[i * 12 + j] < NP) {
yn++;
break;
}
}
}
b_y = (Y - yn) / Y;
System.out.printf("%.3f\n", yn);
System.out.printf("%.6f\n", b_y);
// 输出结果到文件
String[] OUTPUT_FILES = {
buildFilePath(properties.getOutputPath(), properties.getUpstreamLevelFile(), true), // 上游水位
buildFilePath(properties.getOutputPath(), properties.getReservoirVolumeFile(), true), // 库容
buildFilePath(properties.getOutputPath(), properties.getGenFlowFile(), true), // 发电流量
buildFilePath(properties.getOutputPath(), properties.getPowerOutputFile(), true), // 出力
buildFilePath(properties.getOutputPath(), properties.getWaterLossFile(), true), // 弃水
buildFilePath(properties.getOutputPath(), properties.getNetHeadFile(), true), // 水头
buildFilePath(properties.getOutputPath(), properties.getEnergyFile(), true) // 发电量
};
PrintWriter[] writers = new PrintWriter[OUTPUT_FILES.length];
try {
writers[0] = new PrintWriter(OUTPUT_FILES[0]); // 上游水位
writers[1] = new PrintWriter(OUTPUT_FILES[1]); // 库容
writers[2] = new PrintWriter(OUTPUT_FILES[2]); // 发电流量
writers[3] = new PrintWriter(OUTPUT_FILES[3]); // 出力
writers[4] = new PrintWriter(OUTPUT_FILES[4]); // 弃水
writers[5] = new PrintWriter(OUTPUT_FILES[5]); // 水头
writers[6] = new PrintWriter(OUTPUT_FILES[6]); // 发电量
for (i = 1; i <= T; i++) {
writers[0].printf("%.2f\t", zsy[i]);
writers[1].printf("%.2f\t", v[i]);
writers[2].printf("%.2f\t", Qfd[i]);
writers[3].printf("%.2f\t", N[i]);
writers[4].printf("%.2f\t", q_qs[i]);
writers[5].printf("%.2f\t", dz[i]);
writers[6].printf("%.2f\t", E[i]);
if (i % 12 == 0) {
for (PrintWriter w : writers) w.println();
}
}
} catch (IOException e) {
System.err.println("写入文件失败:" + e.getMessage());
} finally {
for (PrintWriter w : writers) {
if (w != null) w.close();
}
}
System.out.println("end");
return buildSimulationResult(b_y, yn, zsy, v, Qfd, N, q_qs, dz, E, T);
}
}
5. 输出文件说明
模拟完成后会生成以下8个结果文件:
5.1 常规调度下的上游水位.txt
- 内容:每月上游水位(m)
- 格式:12个月为一行,共48行
5.2 常规调度下的库容.txt
- 内容:每月库容(亿m³)
- 格式:12个月为一行,共48行
5.3 常规调度下的发电流量.txt
- 内容:每月发电流量(m³/s)
- 格式:12个月为一行,共48行
5.4 常规调度下的出力.txt
- 内容:每月出力(万kW)
- 格式:12个月为一行,共48行
5.5 常规调度下的弃水.txt
- 内容:每月弃水流量(m³/s)
- 格式:12个月为一行,共48行
5.6 常规调度下的水头.txt
- 内容:每月净水头(m)
- 格式:12个月为一行,共48行
5.7 常规调度下的发电量.txt
- 内容:每月发电量(kWh)
- 格式:12个月为一行,共48行
5.8 水文年排列的流量.txt
- 内容:依据水文年排列的流量
- 格式:12个月为一行,共48行
6. 扩展功能
6.1 自定义水位关系
可以通过修改配置文件中的关系文件来支持不同的水库特性:
reservoir:
simulator:
level-volume-file: "自定义水位库容关系.txt"
flow-level-file: "自定义水位流量关系.txt"
6.2 调整调度策略
修改水库参数来调整调度策略:
reservoir:
simulator:
dead-water-level: 330.0 # 调整死水位
guaranteed-output: 1.8 # 调整保证出力
early-flood-limit-level: 345.0 # 调整汛限水位
7. 测试说明
引入依赖后可以使用以下方法进行测试:
7.1 单元测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class ReservoirApplicationTests {
@Autowired
private ReservoirSimulatorService reservoirSimulatorService;
@Test
public void testSimulation() {
SimulationResult result = reservoirSimulatorService.testSimulation();
System.out.println("模拟结果:");
System.out.println("破坏年: " + result.getFailureYears() );
System.out.println("保证率: " + String.format("%.4f", result.getReliability()));
}
}
更多推荐




所有评论(0)