|
|
@@ -99,11 +99,11 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
switch (type)
|
|
|
{
|
|
|
case "daily":
|
|
|
- return buildDailyReport(actualList, pureForecastList);
|
|
|
+ return buildDailyReport(actualList, pureForecastList, forecastList);
|
|
|
case "monthly":
|
|
|
- return buildMonthlyReport(actualList, pureForecastList);
|
|
|
+ return buildMonthlyReport(actualList, pureForecastList, forecastList);
|
|
|
case "yearly":
|
|
|
- return buildYearlyReport(actualList, pureForecastList);
|
|
|
+ return buildYearlyReport(actualList, pureForecastList, forecastList);
|
|
|
default:
|
|
|
return Collections.emptyList();
|
|
|
}
|
|
|
@@ -175,7 +175,7 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
* 构建日报
|
|
|
*/
|
|
|
private List<ShortTermPowerForecastReportVO> buildDailyReport(List<DataElectricityLocal> actualList,
|
|
|
- List<ForecastPowerAverage> forecastList)
|
|
|
+ List<ForecastPowerAverage> forecastList, List<DataShortTermPowerForecastReport> rawForecastList)
|
|
|
{
|
|
|
Map<String, List<DataElectricityLocal>> dailyMap = groupByDay(actualList);
|
|
|
Map<String, List<ForecastPowerAverage>> forecastDailyMap = groupForecastByDay(forecastList);
|
|
|
@@ -189,7 +189,7 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
|
|
|
ShortTermPowerForecastReportVO vo = new ShortTermPowerForecastReportVO();
|
|
|
vo.setTime(day);
|
|
|
- computeDailyMetrics(vo, records, forecastRecords);
|
|
|
+ computeDailyMetrics(vo, records, forecastRecords, rawForecastList);
|
|
|
result.add(vo);
|
|
|
}
|
|
|
return result;
|
|
|
@@ -199,7 +199,7 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
* 构建月报
|
|
|
*/
|
|
|
private List<ShortTermPowerForecastReportVO> buildMonthlyReport(List<DataElectricityLocal> actualList,
|
|
|
- List<ForecastPowerAverage> forecastList)
|
|
|
+ List<ForecastPowerAverage> forecastList, List<DataShortTermPowerForecastReport> rawForecastList)
|
|
|
{
|
|
|
Map<String, List<DataElectricityLocal>> monthlyMap = groupByMonth(actualList);
|
|
|
Map<String, List<ForecastPowerAverage>> forecastMonthlyMap = groupForecastByMonth(forecastList);
|
|
|
@@ -214,7 +214,7 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
|
|
|
ShortTermPowerForecastReportVO vo = new ShortTermPowerForecastReportVO();
|
|
|
vo.setTime(month);
|
|
|
- computeDailyMetrics(vo, records, forecastRecords);
|
|
|
+ computeDailyMetrics(vo, records, forecastRecords, rawForecastList);
|
|
|
result.add(vo);
|
|
|
}
|
|
|
return result;
|
|
|
@@ -224,7 +224,7 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
* 构建年报
|
|
|
*/
|
|
|
private List<ShortTermPowerForecastReportVO> buildYearlyReport(List<DataElectricityLocal> actualList,
|
|
|
- List<ForecastPowerAverage> forecastList)
|
|
|
+ List<ForecastPowerAverage> forecastList, List<DataShortTermPowerForecastReport> rawForecastList)
|
|
|
{
|
|
|
Map<String, List<DataElectricityLocal>> yearlyMap = groupByYear(actualList);
|
|
|
Map<String, List<ForecastPowerAverage>> forecastYearlyMap = groupForecastByYear(forecastList);
|
|
|
@@ -239,7 +239,7 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
|
|
|
ShortTermPowerForecastReportVO vo = new ShortTermPowerForecastReportVO();
|
|
|
vo.setTime(year);
|
|
|
- computeDailyMetrics(vo, records, forecastRecords);
|
|
|
+ computeDailyMetrics(vo, records, forecastRecords, rawForecastList);
|
|
|
result.add(vo);
|
|
|
}
|
|
|
return result;
|
|
|
@@ -249,7 +249,7 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
* 计算指标(日报/月报/年报通用)
|
|
|
*/
|
|
|
private void computeDailyMetrics(ShortTermPowerForecastReportVO vo,
|
|
|
- List<DataElectricityLocal> actualRecords, List<ForecastPowerAverage> forecastRecords)
|
|
|
+ List<DataElectricityLocal> actualRecords, List<ForecastPowerAverage> forecastRecords, List<DataShortTermPowerForecastReport> rawForecastList)
|
|
|
{
|
|
|
// 预先构建预测数据映射(避免各方法重复调用 buildForecastMap)
|
|
|
Map<Long, BigDecimal> forecastMap = buildForecastMap(forecastRecords);
|
|
|
@@ -287,7 +287,7 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
vo.setMaximumErrorPassRate(passRate);
|
|
|
|
|
|
// 8. acc: 合格率
|
|
|
- BigDecimal acc = computeAccuracy(actualRecords, forecastMap, operatingCapacity);
|
|
|
+ BigDecimal acc = computeAccuracy(actualRecords, forecastMap, operatingCapacity, rawForecastList);
|
|
|
vo.setAcc(acc);
|
|
|
}
|
|
|
|
|
|
@@ -499,81 +499,20 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
return BigDecimal.valueOf(r).setScale(3, RoundingMode.HALF_UP);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 计算最大误差合格率
|
|
|
- */
|
|
|
- private BigDecimal computeMaximumErrorPassRate(List<DataElectricityLocal> actualRecords,
|
|
|
- Map<Long, BigDecimal> forecastMap,
|
|
|
- BigDecimal operatingCapacity)
|
|
|
- {
|
|
|
- if (operatingCapacity == null || operatingCapacity.compareTo(BigDecimal.ZERO) == 0)
|
|
|
- {
|
|
|
- return BigDecimal.ZERO;
|
|
|
- }
|
|
|
-
|
|
|
- String threshold = getErrorThreshold();
|
|
|
- if (StringUtils.isEmpty(threshold))
|
|
|
- {
|
|
|
- return BigDecimal.ZERO;
|
|
|
- }
|
|
|
-
|
|
|
- BigDecimal thresholdValue;
|
|
|
- try
|
|
|
- {
|
|
|
- thresholdValue = new BigDecimal(threshold.trim());
|
|
|
- }
|
|
|
- catch (NumberFormatException e)
|
|
|
- {
|
|
|
- return BigDecimal.ZERO;
|
|
|
- }
|
|
|
-
|
|
|
- BigDecimal passCount = BigDecimal.ZERO;
|
|
|
- BigDecimal totalCount = BigDecimal.ZERO;
|
|
|
- BigDecimal thousand = BigDecimal.valueOf(1000);
|
|
|
-
|
|
|
- for (DataElectricityLocal r : actualRecords)
|
|
|
- {
|
|
|
- if (r.getTm() == null || r.getActualPower() == null) continue;
|
|
|
-
|
|
|
- Long ts = r.getTm().getTime() / 1000;
|
|
|
- BigDecimal fp = forecastMap.get(ts);
|
|
|
- if (fp == null) continue;
|
|
|
-
|
|
|
- totalCount = totalCount.add(BigDecimal.ONE);
|
|
|
- BigDecimal errorMW = BigDecimal.valueOf(r.getActualPower())
|
|
|
- .subtract(fp)
|
|
|
- .abs()
|
|
|
- .divide(thousand, 10, RoundingMode.HALF_UP);
|
|
|
- BigDecimal allowed = thresholdValue.multiply(operatingCapacity);
|
|
|
- if (errorMW.compareTo(allowed) <= 0)
|
|
|
- {
|
|
|
- passCount = passCount.add(BigDecimal.ONE);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (totalCount.compareTo(BigDecimal.ZERO) == 0)
|
|
|
- {
|
|
|
- return BigDecimal.ZERO;
|
|
|
- }
|
|
|
-
|
|
|
- BigDecimal rate = passCount.divide(totalCount, 10, RoundingMode.HALF_UP)
|
|
|
- .multiply(BigDecimal.valueOf(100));
|
|
|
- return rate.setScale(3, RoundingMode.HALF_UP);
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 计算准确率 acc = (1 - sqrt(ΣSi / N)) * 100
|
|
|
*
|
|
|
* <p>其中:
|
|
|
* <ul>
|
|
|
- * <li>阈值 = 开机容量 * 0.2</li>
|
|
|
+ * <li>阈值 = 开机容量 * 0.5</li>
|
|
|
* <li>Si = ((Pmi - Ppi) / Pmi)² 当 Pmi >= 阈值</li>
|
|
|
* <li>Si = ((Pmi - Ppi) / 阈值)² 当 Pmi < 阈值</li>
|
|
|
* </ul>
|
|
|
*/
|
|
|
private BigDecimal computeAccuracy(List<DataElectricityLocal> actualRecords,
|
|
|
Map<Long, BigDecimal> forecastMap,
|
|
|
- BigDecimal operatingCapacity)
|
|
|
+ BigDecimal operatingCapacity,
|
|
|
+ List<DataShortTermPowerForecastReport> rawForecastList)
|
|
|
{
|
|
|
if (forecastMap == null || forecastMap.isEmpty() || operatingCapacity == null
|
|
|
|| operatingCapacity.compareTo(BigDecimal.ZERO) == 0)
|
|
|
@@ -581,8 +520,9 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
return BigDecimal.ZERO;
|
|
|
}
|
|
|
|
|
|
- // 阈值 = 开机容量(MW) * 0.2
|
|
|
- BigDecimal threshold = operatingCapacity.multiply(new BigDecimal("0.2")).setScale(3, RoundingMode.HALF_UP);
|
|
|
+ // 阈值 = 开机容量(MW) * 0.25
|
|
|
+ BigDecimal baseThreshold = new BigDecimal("0.25");
|
|
|
+ BigDecimal threshold = operatingCapacity.multiply(baseThreshold).setScale(3, RoundingMode.HALF_UP);
|
|
|
|
|
|
BigDecimal stotal = BigDecimal.ZERO;
|
|
|
int n = 0;
|
|
|
@@ -593,7 +533,7 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
if (r.getTm() == null || r.getActualPower() == null) continue;
|
|
|
|
|
|
Long ts = r.getTm().getTime() / 1000;
|
|
|
- BigDecimal fp = forecastMap.get(ts);
|
|
|
+ BigDecimal fp = getAccForecastReportPower(ts, rawForecastList);
|
|
|
if (fp == null) continue;
|
|
|
|
|
|
BigDecimal pmiMW = BigDecimal.valueOf(r.getActualPower()).divide(BigDecimal.valueOf(1000), precisionLength, RoundingMode.HALF_UP);
|
|
|
@@ -625,11 +565,11 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
|
|
|
// acc = (1 - sqrt(stotal / n)) * 100
|
|
|
double meanSquare = stotal.divide(BigDecimal.valueOf(n), precisionLength, RoundingMode.HALF_UP).doubleValue();
|
|
|
- log.info("meanSquare: {}", meanSquare);
|
|
|
+ log.debug("meanSquare: {}", meanSquare);
|
|
|
double deviation = Math.sqrt(meanSquare);
|
|
|
- log.info("deviation: {}", deviation);
|
|
|
- double acc = (1.0 - deviation) * 100.0;
|
|
|
- log.info("acc: {}", acc);
|
|
|
+ log.debug("deviation: {}", deviation);
|
|
|
+ double acc = deviation * 100.0;
|
|
|
+ log.debug("acc: {}", acc);
|
|
|
|
|
|
// 边界处理
|
|
|
if (acc < 0) acc = 0;
|
|
|
@@ -638,6 +578,34 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
return BigDecimal.valueOf(acc).setScale(3, RoundingMode.HALF_UP);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取日前timestamp预报数据
|
|
|
+ * @param timestamp
|
|
|
+ * @param rawForecastList
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private BigDecimal getAccForecastReportPower(long timestamp, List<DataShortTermPowerForecastReport> rawForecastList)
|
|
|
+ {
|
|
|
+ return rawForecastList.stream()
|
|
|
+ .filter(r -> r.getForecastTime() != null && r.getStartTime() != null && r.getForecastTime() == timestamp && isYesterday(timestamp, r.getStartTime()))
|
|
|
+ .max(Comparator.comparing(DataShortTermPowerForecastReport::getStartTime))
|
|
|
+ .map(r -> new BigDecimal(r.getPower()))
|
|
|
+ .orElse(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断datTimestamp是否是timestamp的前一天
|
|
|
+ * @param timestamp
|
|
|
+ * @param datTimestamp
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private boolean isYesterday(long timestamp, long datTimestamp)
|
|
|
+ {
|
|
|
+ return LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZONE_SHANGHAI).toLocalDate()
|
|
|
+ .minusDays(1)
|
|
|
+ .equals(LocalDateTime.ofInstant(Instant.ofEpochSecond(datTimestamp), ZONE_SHANGHAI).toLocalDate());
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 构建预测数据映射:时间戳(秒) -> power(kW)
|
|
|
*/
|
|
|
@@ -816,19 +784,6 @@ public class ShortTermPowerForecastReportServiceImpl implements IShortTermPowerF
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 获取误差阈值
|
|
|
- */
|
|
|
- private String getErrorThreshold()
|
|
|
- {
|
|
|
- List<SysDictData> dictList = dictTypeService.selectDictDataByType(DICT_TYPE_SHORT_TERM_ERROR_THRESHOLD);
|
|
|
- if (dictList != null && !dictList.isEmpty())
|
|
|
- {
|
|
|
- return dictList.get(0).getDictValue();
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 刷新阈值配置
|
|
|
*/
|