1547 lines
54 KiB
Java
1547 lines
54 KiB
Java
package com.interplug.qcast.biz.pwrGnrSimulation;
|
||
|
||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||
import com.interplug.qcast.biz.pwrGnrSimulation.dto.*;
|
||
import com.interplug.qcast.config.Exception.ErrorCode;
|
||
import com.interplug.qcast.config.Exception.QcastException;
|
||
import com.interplug.qcast.config.message.Messages;
|
||
import com.interplug.qcast.util.InterfaceQsp;
|
||
import java.awt.*;
|
||
import java.io.*;
|
||
import java.nio.charset.StandardCharsets;
|
||
import java.util.ArrayList;
|
||
import java.util.Arrays;
|
||
import java.util.List;
|
||
import java.util.Optional;
|
||
import java.util.function.Function;
|
||
import java.util.stream.Collectors;
|
||
import lombok.RequiredArgsConstructor;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.apache.commons.lang3.StringUtils;
|
||
import org.jsoup.nodes.Document;
|
||
import org.jsoup.nodes.Element;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.beans.factory.annotation.Value;
|
||
import org.springframework.http.HttpMethod;
|
||
import org.springframework.stereotype.Service;
|
||
|
||
@Slf4j
|
||
@Service
|
||
@RequiredArgsConstructor
|
||
public class PwrGnrSimService {
|
||
private final InterfaceQsp interfaceQsp;
|
||
|
||
@Autowired Messages message;
|
||
|
||
@Value("${qsp.url}")
|
||
private String qspUrl;
|
||
|
||
@Value("${qsp.simulation-guide-info-url}")
|
||
private String qspSimulationGuideInfUrl;
|
||
|
||
private final PwrGnrSimMapper pwrGnrSimMapper;
|
||
|
||
static String[][] module_data =
|
||
readValuesFromFileString2("template/pwrGnrSimulation/module_data.txt");
|
||
|
||
static String[][] pcs_data = readValuesFromFileString2("template/pwrGnrSimulation/pcs_data.txt");
|
||
|
||
double[] snow = readValuesFromFile("template/pwrGnrSimulation/snow.txt");
|
||
|
||
// cosθz
|
||
double[][] cosThetaZ = readValuesFromFile2("template/pwrGnrSimulation/cosThetaZ.txt");
|
||
|
||
// hinode_nichibotsu
|
||
double[][] hinode_nichibotsu =
|
||
readValuesFromFile2("template/pwrGnrSimulation/hinode_nichibotsu.txt");
|
||
|
||
double[] ion = readValuesFromFile("template/pwrGnrSimulation/ion.txt");
|
||
|
||
// 시간 각도
|
||
double[][] jikakudo = readValuesFromFile2("template/pwrGnrSimulation/jikakudo.txt");
|
||
|
||
double[] jimenhansharitsu = readValuesFromFile("template/pwrGnrSimulation/jimenhansharitsu.txt");
|
||
|
||
// 균시차
|
||
// double[] kinjisa = readValuesFromFile("template/pwrGnrSimulation/kinjisa.txt");
|
||
|
||
// 위도 경도[rad]
|
||
double[][] latlng = readValuesFromFile2("template/pwrGnrSimulation/latlng.txt");
|
||
|
||
double[][] rd = readValuesFromFile2("template/pwrGnrSimulation/rd.txt");
|
||
|
||
double[][] rdx = readValuesFromFile2("template/pwrGnrSimulation/rdx.txt");
|
||
|
||
// double[][] rt = readValuesFromFile2("template/pwrGnrSimulation/rt.txt");
|
||
|
||
double[][] rtx = readValuesFromFile2("template/pwrGnrSimulation/rtx.txt");
|
||
|
||
// 산란 일사
|
||
double[] sanrannissha = readValuesFromFile("template/pwrGnrSimulation/sanrannissha.txt");
|
||
|
||
// 수평면 일사
|
||
double[] suiheimennissha = readValuesFromFile("template/pwrGnrSimulation/suiheimennissha.txt");
|
||
|
||
// 태양 적위
|
||
double[] taiyousekii = readValuesFromFile("template/pwrGnrSimulation/taiyousekii.txt");
|
||
|
||
static double[][] peakcut_by_pcs_data =
|
||
readValuesFromFile2("template/pwrGnrSimulation/peakcut_by_pcs_data.txt");
|
||
|
||
static double[][] tashounen_data_deg0 =
|
||
readValuesFromFile2("template/pwrGnrSimulation/tashounen_data_deg0.txt");
|
||
|
||
static double[][] tashounen_data_deg90 =
|
||
readValuesFromFile2("template/pwrGnrSimulation/tashounen_data_deg90.txt");
|
||
|
||
static double[][] amp_peakcut_data_deg0 =
|
||
readValuesFromFile2("template/pwrGnrSimulation/amp_peakcut_data_deg0.txt");
|
||
|
||
static double[][] amp_peakcut_data_deg90 =
|
||
readValuesFromFile2("template/pwrGnrSimulation/amp_peakcut_data_deg90.txt");
|
||
|
||
/**
|
||
* 발전시뮬레이션 계산로직
|
||
*
|
||
* @param pwrGnrSimRequest
|
||
* @return
|
||
* @throws Exception
|
||
*/
|
||
public PwrGnrSimResponse selectPwrGnrSimulation(PwrGnrSimRequest pwrGnrSimRequest)
|
||
throws Exception {
|
||
|
||
// 데이터가 없어서 오류가 발생한 경우, 빈값으로 리턴
|
||
PwrGnrSimResponse exceptionRes = new PwrGnrSimResponse();
|
||
int[] exceptionData = new int[13];
|
||
exceptionRes.setFrcPwrGnrList(exceptionData);
|
||
|
||
// 견적서 정보를 조회한다.
|
||
PwrGnrSimPlanResponse planInfo = pwrGnrSimMapper.selectPlanInfo(pwrGnrSimRequest);
|
||
|
||
// 지역이 없으면 계산 불가능
|
||
if (planInfo == null || planInfo.getAreaId() == 0) {
|
||
log.error("지역값이 없음.");
|
||
return exceptionRes;
|
||
}
|
||
|
||
// 견적서의 지붕재와 아이템 정보 조회한다.
|
||
List<PwrGnrSimRoofResponse> roofItemList = pwrGnrSimMapper.selectRoofItemList(pwrGnrSimRequest);
|
||
// 지붕재 또는 지붕재에 아이템이 없음.
|
||
if (roofItemList == null || roofItemList.size() == 0) {
|
||
log.error("지붕재 또는 지붕재에 아이템이 없음.");
|
||
return exceptionRes;
|
||
}
|
||
|
||
List<PwrGnrSimRoofResponse> moduleList =
|
||
roofItemList.stream()
|
||
.filter(item -> "MODULE_".equals(item.getItemGroup())) // itemGroup이 MODULE_인 항목만 필터링
|
||
.collect(Collectors.groupingBy(PwrGnrSimRoofResponse::getRoofNo)) // roofNo별로 그룹화
|
||
.values()
|
||
.stream()
|
||
.flatMap(List::stream) // 그룹화된 리스트를 다시 평탄화
|
||
.collect(Collectors.toList());
|
||
|
||
// 모듈 아이템이 없음.
|
||
if (moduleList == null || moduleList.size() == 0) {
|
||
log.error("모듈 아이템이 없음.");
|
||
return exceptionRes;
|
||
}
|
||
|
||
List<PwrGnrSimRoofResponse> pcsList =
|
||
roofItemList.stream()
|
||
.filter(item -> "PC_".equals(item.getItemGroup())) // itemGroup이 PC_인 항목만 필터링
|
||
.collect(
|
||
Collectors.groupingBy(
|
||
PwrGnrSimRoofResponse::getItemId,
|
||
Collectors.reducing(
|
||
(item1, item2) -> {
|
||
// item1의 amount에 item2의 amount를 더한 새로운 객체 생성
|
||
item1.setAmount(
|
||
Integer.parseInt(item1.getAmount())
|
||
+ Integer.parseInt(item2.getAmount())
|
||
+ "");
|
||
return item1;
|
||
})))
|
||
.values()
|
||
.stream()
|
||
.flatMap(Optional::stream) // Optional을 평탄화
|
||
.collect(Collectors.toList());
|
||
|
||
// pcs 아이템이 없음.
|
||
if (pcsList == null || pcsList.size() == 0) {
|
||
log.error("PCS 아이템이 없음.");
|
||
return exceptionRes;
|
||
}
|
||
|
||
List<PwrGnrSimRoofResponse> roofList =
|
||
roofItemList.stream()
|
||
.collect(
|
||
Collectors.toMap(
|
||
PwrGnrSimRoofResponse::getRoofNo, // key는 roofNo로
|
||
Function.identity(), // value는 객체 자체로
|
||
(existing, replacement) -> existing // 중복 시 기존 객체 유지
|
||
))
|
||
.values()
|
||
.stream()
|
||
.collect(Collectors.toList());
|
||
|
||
int roofLength = roofList.size(); // Set의 크기 = 고유 roofNo 개수
|
||
|
||
// 견적서 정보 기준으로 발전시뮬레이션 계산을 진행한다.
|
||
PwrGnrSimRequest pwrGnrSimReq = new PwrGnrSimRequest();
|
||
pwrGnrSimReq.setSimulationPointNumber(planInfo.getAreaId() - 1);
|
||
double[] dKoubai = new double[roofLength];
|
||
double[] dHoui = new double[roofLength];
|
||
|
||
int[] dModuleInput1 = new int[roofLength];
|
||
int[] dModuleInput2 = new int[roofLength];
|
||
int[] dModuleInput3 = new int[roofLength];
|
||
|
||
// pcs 정보
|
||
int k = 0;
|
||
for (PwrGnrSimRoofResponse p : pcsList) {
|
||
if (k == 0) {
|
||
pwrGnrSimReq.setPcs1(p.getItemId());
|
||
pwrGnrSimReq.setPcsInput1(Integer.parseInt(p.getAmount()));
|
||
} else if (k == 1) {
|
||
pwrGnrSimReq.setPcs2(p.getItemId());
|
||
pwrGnrSimReq.setPcsInput2(Integer.parseInt(p.getAmount()));
|
||
} else {
|
||
pwrGnrSimReq.setPcs3(p.getItemId());
|
||
pwrGnrSimReq.setPcsInput3(Integer.parseInt(p.getAmount()));
|
||
}
|
||
}
|
||
// module 정보
|
||
k = 0;
|
||
for (PwrGnrSimRoofResponse m : moduleList) {
|
||
if (k == 0) {
|
||
pwrGnrSimReq.setModule1(m.getItemId());
|
||
} else if (k == 1) {
|
||
pwrGnrSimReq.setModule2(m.getItemId());
|
||
} else {
|
||
pwrGnrSimReq.setModule3(m.getItemId());
|
||
}
|
||
k++;
|
||
}
|
||
|
||
// 전체 시스템 용량
|
||
double dSpecification = 0.0;
|
||
|
||
int i = 0;
|
||
for (PwrGnrSimRoofResponse data : roofList) {
|
||
|
||
if (data.getClassType() == 0) { // 경사
|
||
dKoubai[i] =
|
||
(2 * Math.PI)
|
||
* ((Math.atan(Double.parseDouble(data.getSlopeAngle()) / 10) * 180 / Math.PI)
|
||
/ 360);
|
||
} else { // 각도
|
||
dKoubai[i] = (2 * Math.PI) * (Double.parseDouble(data.getSlopeAngle()) / 360);
|
||
}
|
||
|
||
dHoui[i] = Math.abs((2 * Math.PI) * (Double.parseDouble(data.getAzimuth()) / 360));
|
||
|
||
// 지붕별 모듈정보 셋팅
|
||
int j = 0;
|
||
for (PwrGnrSimRoofResponse m : moduleList) {
|
||
if (data.getRoofNo().equals(m.getRoofNo())) {
|
||
dSpecification += Double.parseDouble(m.getSpecification());
|
||
if (j == 0) {
|
||
dModuleInput1[i] = Integer.parseInt(m.getAmount());
|
||
} else if (j == 1) {
|
||
dModuleInput2[i] = Integer.parseInt(m.getAmount());
|
||
} else {
|
||
dModuleInput3[i] = Integer.parseInt(m.getAmount());
|
||
}
|
||
|
||
j++;
|
||
}
|
||
}
|
||
|
||
i++;
|
||
}
|
||
|
||
pwrGnrSimReq.setKoubai(dKoubai);
|
||
pwrGnrSimReq.setHoui(dHoui);
|
||
pwrGnrSimReq.setModuleInput1(dModuleInput1);
|
||
pwrGnrSimReq.setModuleInput2(dModuleInput2);
|
||
pwrGnrSimReq.setModuleInput3(dModuleInput3);
|
||
|
||
// 발전시뮬레이션 결과와 견적서 정보를 return 한다.
|
||
PwrGnrSimResponse pwrGnrSimRes = this.calcResults(pwrGnrSimReq, roofLength);
|
||
pwrGnrSimRes.setObjectNo(planInfo.getObjectNo());
|
||
pwrGnrSimRes.setPlanNo(planInfo.getPlanNo());
|
||
pwrGnrSimRes.setDrawingEstimateCreateDate(planInfo.getDrawingEstimateCreateDate());
|
||
pwrGnrSimRes.setCapacity(String.valueOf(dSpecification));
|
||
pwrGnrSimRes.setAnlFrcsGnrt(pwrGnrSimRes.getFrcPwrGnrList()[12]);
|
||
pwrGnrSimRes.setPrefName(planInfo.getPrefName());
|
||
pwrGnrSimRes.setAreaName(planInfo.getAreaName());
|
||
pwrGnrSimRes.setSnowfall(planInfo.getSnowfall());
|
||
pwrGnrSimRes.setStandardWindSpeedId(planInfo.getStandardWindSpeedId());
|
||
pwrGnrSimRes.setRoofModuleList(moduleList);
|
||
pwrGnrSimRes.setPcsList(pcsList);
|
||
|
||
return pwrGnrSimRes;
|
||
}
|
||
|
||
public PwrGnrSimResponse calcResults(PwrGnrSimRequest pwrGnrSimReq, int roofLength)
|
||
throws Exception {
|
||
|
||
int[] days = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 각 월별 일수 선언
|
||
|
||
double module_system_loss = 0;
|
||
double ondo_keisu = 0;
|
||
for (int i = 0; i < module_data.length; i++) { // 모듈 시스템 손실과 모듈의 온도 계수 가져오기
|
||
if (pwrGnrSimReq.getModule1().equals(module_data[i][0])) {
|
||
module_system_loss = Double.valueOf(module_data[i][6]);
|
||
ondo_keisu = Double.valueOf(module_data[i][7]);
|
||
}
|
||
}
|
||
|
||
// 각 월별 온도 손실 계산
|
||
double[] ondo_sonshitsu = new double[12];
|
||
ondo_sonshitsu[0] = 1 - 0.1 * ondo_keisu / 0.5;
|
||
ondo_sonshitsu[1] = 1 - 0.1 * ondo_keisu / 0.5;
|
||
ondo_sonshitsu[2] = 1 - 0.1 * ondo_keisu / 0.5;
|
||
ondo_sonshitsu[3] = 1 - 0.15 * ondo_keisu / 0.5;
|
||
ondo_sonshitsu[4] = 1 - 0.15 * ondo_keisu / 0.5;
|
||
ondo_sonshitsu[5] = 1 - 0.2 * ondo_keisu / 0.5;
|
||
ondo_sonshitsu[6] = 1 - 0.2 * ondo_keisu / 0.5;
|
||
ondo_sonshitsu[7] = 1 - 0.2 * ondo_keisu / 0.5;
|
||
ondo_sonshitsu[8] = 1 - 0.2 * ondo_keisu / 0.5;
|
||
ondo_sonshitsu[9] = 1 - 0.15 * ondo_keisu / 0.5;
|
||
ondo_sonshitsu[10] = 1 - 0.15 * ondo_keisu / 0.5;
|
||
ondo_sonshitsu[11] = 1 - 0.1 * ondo_keisu / 0.5;
|
||
|
||
// PCS1 선택 정보
|
||
double pcs1_henkankouritsu = 0;
|
||
double pcs1_youryou = 0;
|
||
// double pcs1_daidenryu = 0;
|
||
// double pcs1_sekisairitsu = 0;
|
||
|
||
// PCS2 선택 정보
|
||
double pcs2_henkankouritsu = 1;
|
||
double pcs2_youryou = 0;
|
||
|
||
// PCS3 선택 정보
|
||
double pcs3_henkankouritsu = 1;
|
||
double pcs3_youryou = 0;
|
||
|
||
for (int i = 0; i < pcs_data.length; i++) { // pcs1 변환 효율과 용량, 대전류 대응, 허용 적재율 가져오기
|
||
if (pwrGnrSimReq.getPcs1().equals(pcs_data[i][0])) {
|
||
pcs1_henkankouritsu = Double.parseDouble(pcs_data[i][3]) / 100;
|
||
pcs1_youryou = Double.parseDouble(pcs_data[i][4]);
|
||
// pcs1_daidenryu = Double.parseDouble(pcs_data[i][9]);
|
||
// pcs1_sekisairitsu = Double.parseDouble(pcs_data[i][5]);
|
||
break;
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < pcs_data.length; i++) { // pcs2 변환 효율과 용량 가져오기
|
||
if (pwrGnrSimReq.getPcs2() != null && pwrGnrSimReq.getPcs2().equals(pcs_data[i][0])) {
|
||
pcs2_henkankouritsu = Double.parseDouble(pcs_data[i][3]) / 100;
|
||
pcs2_youryou = Double.parseDouble(pcs_data[i][4]);
|
||
break;
|
||
} else {
|
||
pcs2_henkankouritsu = 1;
|
||
pcs2_youryou = 0;
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < pcs_data.length; i++) { // pcs3 변환 효율과 용량 가져오기
|
||
if (pwrGnrSimReq.getPcs3() != null && pwrGnrSimReq.getPcs3().equals(pcs_data[i][0])) {
|
||
pcs3_henkankouritsu = Double.parseDouble(pcs_data[i][3]) / 100;
|
||
pcs3_youryou = Double.parseDouble(pcs_data[i][4]);
|
||
break;
|
||
} else {
|
||
pcs3_henkankouritsu = 1;
|
||
pcs3_youryou = 0;
|
||
}
|
||
}
|
||
|
||
// pcs1과 pcs2, pcs3의 실효 변환 효율 중 낮은 값을 사용
|
||
double pcs_henkankouritsu =
|
||
Math.min(pcs1_henkankouritsu, Math.min(pcs3_henkankouritsu, pcs2_henkankouritsu));
|
||
|
||
// pcs 총 용량 계산
|
||
double pcs_youryou_total =
|
||
(pcs1_youryou * Double.valueOf(pwrGnrSimReq.getPcsInput1()))
|
||
+ (pcs2_youryou * Double.valueOf(pwrGnrSimReq.getPcsInput2()))
|
||
+ (pcs3_youryou * Double.valueOf(pwrGnrSimReq.getPcsInput3()));
|
||
|
||
double module1_watt = 0, module1_watt_flash = 0 /*, module1_amp = 0*/;
|
||
double module2_watt = 0, module2_watt_flash = 0;
|
||
double module3_watt = 0, module3_watt_flash = 0;
|
||
|
||
// 모듈 1의 출력 획득
|
||
for (int i = 0; i < module_data.length; i++) {
|
||
if (pwrGnrSimReq.getModule1() != null
|
||
&& pwrGnrSimReq.getModule1().equals(module_data[i][0])) {
|
||
module1_watt = Double.parseDouble(module_data[i][2]);
|
||
module1_watt_flash = module1_watt + 2; // 모듈 1 출력 + 2w (플래시)
|
||
// module1_amp = Double.parseDouble(module_data[i][9]);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 모듈 2의 출력 획득
|
||
for (int i = 0; i < module_data.length; i++) {
|
||
if (pwrGnrSimReq.getModule2() != null
|
||
&& pwrGnrSimReq.getModule2().equals(module_data[i][0])) {
|
||
module2_watt = Double.parseDouble(module_data[i][2]);
|
||
module2_watt_flash = module2_watt + 2; // 모듈 2 출력 + 2w (플래시)
|
||
break;
|
||
} else {
|
||
module2_watt = 0;
|
||
module2_watt_flash = 0;
|
||
}
|
||
}
|
||
|
||
// 모듈 3의 출력 획득
|
||
for (int i = 0; i < module_data.length; i++) {
|
||
if (pwrGnrSimReq.getModule3() != null
|
||
&& pwrGnrSimReq.getModule3().equals(module_data[i][0])) {
|
||
module3_watt = Double.parseDouble(module_data[i][2]);
|
||
module3_watt_flash = module3_watt + 2; // 모듈 3 출력 + 2w (플래시)
|
||
break;
|
||
} else {
|
||
module3_watt = 0;
|
||
module3_watt_flash = 0;
|
||
}
|
||
}
|
||
|
||
int module1_number_total = 0;
|
||
int module2_number_total = 0;
|
||
int module3_number_total = 0;
|
||
|
||
for (int i = 0; i < roofLength; i++) {
|
||
if (!Double.isNaN(pwrGnrSimReq.getKoubai()[i]) && !Double.isNaN(pwrGnrSimReq.getHoui()[i])) {
|
||
// 설치면 기울기와 방위가 입력되어 있으면 모듈의 매수를 취득
|
||
module1_number_total += pwrGnrSimReq.getModuleInput1()[i];
|
||
module2_number_total += pwrGnrSimReq.getModuleInput2()[i];
|
||
module3_number_total += pwrGnrSimReq.getModuleInput3()[i];
|
||
}
|
||
}
|
||
|
||
// 모듈 1, 2, 3 총 용량 계산
|
||
double module1_youryou_total = module1_watt * module1_number_total;
|
||
double module2_youryou_total = module2_watt * module2_number_total;
|
||
double module3_youryou_total = module3_watt * module3_number_total;
|
||
|
||
// 모듈 총 용량 계산
|
||
double module_youryou_total =
|
||
module1_youryou_total + module2_youryou_total + module3_youryou_total;
|
||
|
||
// 적재율 [실수] 계산
|
||
double sekisairitsu = module_youryou_total / (pcs_youryou_total * 1000);
|
||
|
||
// 피크 컷 및 발전량 배열 선언
|
||
int[] hatsudenryou_all = new int[13]; // 피크 컷 없음 발전량(적설 고려 없음)
|
||
// double[] hatsudenryou_all_snow = new double[12]; // 피크 컷 없음 발전량(적설 고려 있음)
|
||
// double[] hatsudenryou_peakcut_all = new double[12]; // 피크 컷 있음 발전량(적설 고려 없음)
|
||
// double[] hatsudenryou_peakcut_all_snow = new double[12]; // 피크 컷 있음 발전량(적설 고려 있음)
|
||
|
||
if (sekisairitsu <= 3) {
|
||
|
||
// 설치면 피크 컷 및 발전량 배열 선언
|
||
double[][] hatsudenryou = new double[roofLength][12]; // 전체 설치면의 피크 컷 없음 발전량
|
||
double[][] hatsudenryou_peakcut = new double[roofLength][12]; // 전체 설치면의 피크 컷 있음 발전량
|
||
|
||
int simulationPointNumber = pwrGnrSimReq.getSimulationPointNumber();
|
||
// 우기일 사량 관측 지점의 경우, 계산을 하지 않는다 , 이리 오모테 섬, 이시가키 섬, 오하라, 하 테루 간]
|
||
if (simulationPointNumber != 361
|
||
|| simulationPointNumber != 362
|
||
|| simulationPointNumber != 363
|
||
|| simulationPointNumber != 364
|
||
|| simulationPointNumber != 365
|
||
|| simulationPointNumber != 827
|
||
|| simulationPointNumber != 828
|
||
|| simulationPointNumber != 829
|
||
|| simulationPointNumber != 830
|
||
|| simulationPointNumber != 831
|
||
|| simulationPointNumber != 832
|
||
|| simulationPointNumber != 833
|
||
|| simulationPointNumber != 834
|
||
|| simulationPointNumber != 835
|
||
|| simulationPointNumber != 836
|
||
|| simulationPointNumber != 837) {
|
||
|
||
for (int i = 0; i < roofLength; i++) {
|
||
// 설치면 발전량 계산
|
||
if (!Double.isNaN(pwrGnrSimReq.getKoubai()[i])
|
||
&& !Double.isNaN(pwrGnrSimReq.getHoui()[i])) {
|
||
// 설치면의 사면 일사량 계산
|
||
double[] shamen_nissha =
|
||
this.shamenNissharyou(
|
||
pwrGnrSimReq.getKoubai()[i], pwrGnrSimReq.getHoui()[i], simulationPointNumber);
|
||
double[] peakcut =
|
||
this.peakcut(
|
||
simulationPointNumber,
|
||
pwrGnrSimReq.getKoubai()[i],
|
||
pwrGnrSimReq.getHoui()[i],
|
||
sekisairitsu,
|
||
pwrGnrSimReq.getModule1(),
|
||
pwrGnrSimReq.getPcs1(),
|
||
pcs_henkankouritsu);
|
||
|
||
for (int j = 0; j < 12; j++) {
|
||
|
||
// 피크 컷 없음 발전량 계산
|
||
// shamen_nissha 조금 다름
|
||
hatsudenryou[i][j] =
|
||
shamen_nissha[j]
|
||
* days[j]
|
||
* ondo_sonshitsu[j]
|
||
* pcs_henkankouritsu
|
||
* ((module1_watt_flash * pwrGnrSimReq.getModuleInput1()[i])
|
||
+ (module2_watt_flash * pwrGnrSimReq.getModuleInput2()[i])
|
||
+ (module3_watt_flash * pwrGnrSimReq.getModuleInput3()[i]))
|
||
/ 1000
|
||
* module_system_loss
|
||
* peakcut[0];
|
||
|
||
// 피크 컷 있음 발전량 계산
|
||
hatsudenryou_peakcut[i][j] =
|
||
shamen_nissha[j]
|
||
* days[j]
|
||
* ondo_sonshitsu[j]
|
||
* pcs_henkankouritsu
|
||
* ((module1_watt_flash * pwrGnrSimReq.getModuleInput1()[i])
|
||
+ (module2_watt_flash * pwrGnrSimReq.getModuleInput2()[i])
|
||
+ (module3_watt_flash * pwrGnrSimReq.getModuleInput3()[i]))
|
||
/ 1000
|
||
* module_system_loss
|
||
* peakcut[1];
|
||
}
|
||
}
|
||
}
|
||
|
||
// 설치면 1~4의 합계 발전량
|
||
for (int j = 0; j < 12; j++) {
|
||
|
||
for (int i = 0; i < roofLength; i++) {
|
||
hatsudenryou_all[j] += (int) Math.round(hatsudenryou[i][j]);
|
||
// hatsudenryou_peakcut_all[j] += hatsudenryou_peakcut[i][j];
|
||
}
|
||
|
||
// hatsudenryou_all_snow[j] =
|
||
// hatsudenryou_all[j] * (1 - (snow[(simulationPointNumber * 12) + j]));
|
||
// hatsudenryou_peakcut_all_snow[j] =
|
||
// hatsudenryou_peakcut_all[j] * (1 - (snow[(simulationPointNumber * 12) +
|
||
// j]));
|
||
}
|
||
}
|
||
|
||
// 마지막에 총합계 추가
|
||
hatsudenryou_all[12] = Arrays.stream(hatsudenryou_all).sum();
|
||
}
|
||
|
||
// 결과 확인용
|
||
System.err.println("hatsudenryou_all 결과 ::: " + Arrays.toString(hatsudenryou_all));
|
||
// System.err.println("hatsudenryou_all_snow 결과 ::: " +
|
||
// Arrays.toString(hatsudenryou_all_snow));
|
||
// System.err.println(
|
||
// "hatsudenryou_peakcut_all 결과 ::: " + Arrays.toString(hatsudenryou_peakcut_all));
|
||
// System.err.println(
|
||
// "hatsudenryou_peakcut_all_snow 결과 ::: " +
|
||
// Arrays.toString(hatsudenryou_peakcut_all_snow));
|
||
|
||
PwrGnrSimResponse pwrGnrSimRes = new PwrGnrSimResponse();
|
||
// pwrGnrSimRes.setSekisairitsu(sekisairitsu);
|
||
pwrGnrSimRes.setFrcPwrGnrList(hatsudenryou_all);
|
||
// pwrGnrSimRes.setHatsudenryouAllSnow(hatsudenryou_all_snow);
|
||
// pwrGnrSimRes.setHatsudenryouPeakcutAll(hatsudenryou_peakcut_all);
|
||
// pwrGnrSimRes.setHatsudenryouPeakcutAllSnow(hatsudenryou_peakcut_all_snow);
|
||
|
||
return pwrGnrSimRes;
|
||
}
|
||
|
||
// 설치면의 사면 일사량 계산
|
||
public static double[] peakcut(
|
||
int simulationPointNumber,
|
||
double koubai,
|
||
double houi,
|
||
double sekisairitsu,
|
||
String moduleName,
|
||
String pcsName,
|
||
double pcsHenkankouritsu) {
|
||
|
||
// koubai[rad]을 [도]로 변환 _아래 계산식도 변경된 값으로 계산됨.
|
||
koubai = (koubai / (2 * Math.PI)) * 360;
|
||
|
||
// 방위각 0도 설치각에서의 전력 피크 컷
|
||
double wattPeakcutAboutDeg0 =
|
||
computePowerPeakCutDeg0(simulationPointNumber, sekisairitsu, koubai);
|
||
|
||
// 방위각 90도 설치각에서의 전력 피크 컷
|
||
double watt_peakcut_about_deg90 =
|
||
computePowerPeakCutDeg90(simulationPointNumber, sekisairitsu, koubai);
|
||
|
||
double matrix_multiply1 = (1 * watt_peakcut_about_deg90) + (-1 * watt_peakcut_about_deg90);
|
||
double matrix_multiply2 = (-1 * watt_peakcut_about_deg90) + (2 * watt_peakcut_about_deg90);
|
||
|
||
// houi[rad]을 [도]로 변환
|
||
houi = (houi / (2 * Math.PI)) * 360;
|
||
|
||
// 電力ピークカット
|
||
double watt_peakcut_result =
|
||
((matrix_multiply1 * (Math.cos(houi * Math.PI / 180) + 1) + matrix_multiply2) < 0.1)
|
||
? 0.1
|
||
: (matrix_multiply1 * (Math.cos(houi * Math.PI / 180) + 1) + matrix_multiply2);
|
||
|
||
////////////
|
||
// pcs1과 모듈 1의 전류차 구하기 용도
|
||
double module_amp = 0;
|
||
double pcs_amp = 0;
|
||
boolean pcsAmpFound = false;
|
||
|
||
for (int j = 0; j < module_data.length; j++) {
|
||
if (moduleName.equals(module_data[j][0])) {
|
||
module_amp = Double.valueOf(module_data[j][9]);
|
||
break;
|
||
}
|
||
}
|
||
|
||
for (int j = 0; j < pcs_data.length; j++) {
|
||
if (pcsName.equals(pcs_data[j][0])) {
|
||
pcs_amp = Double.valueOf(pcs_data[j][8]);
|
||
pcsAmpFound = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// pcs1과 모듈 1의 전류차(pcs의 입력 전류 데이터가 없는 경우는 0)
|
||
double amp_peakcut_result = 1; // 디폴트 : pcs1이 없거나 pcs1과모듈1dml wjsfbckrk 0 보다 작은경우
|
||
if (pcsAmpFound) {
|
||
|
||
// pcs1과 모듈 1의 전류차(pcs의 입력 전류 데이터가 없는 경우는 0)
|
||
double amp_delta = module_amp - pcs_amp;
|
||
if (amp_delta >= 0) {
|
||
|
||
double amp_peakcut_about_deg0 =
|
||
computeAmpPeakCutDeg0(
|
||
simulationPointNumber, sekisairitsu, koubai, pcsAmpFound, amp_delta);
|
||
|
||
double amp_peakcut_about_deg90 =
|
||
computeAmpPeakCutDeg90(simulationPointNumber, sekisairitsu, koubai, amp_delta);
|
||
|
||
double matrix_multiply3 = (1 * amp_peakcut_about_deg0) + (-1 * amp_peakcut_about_deg90);
|
||
double matrix_multiply4 = (-1 * amp_peakcut_about_deg0) + (2 * amp_peakcut_about_deg90);
|
||
|
||
// 전류 피크 컷
|
||
amp_peakcut_result =
|
||
1
|
||
- (((matrix_multiply3 * (Math.cos(houi * Math.PI / 180) + 1) + matrix_multiply4)
|
||
< 0.001)
|
||
? 0
|
||
: (matrix_multiply3 * (Math.cos(houi * Math.PI / 180) + 1) + matrix_multiply4));
|
||
}
|
||
}
|
||
|
||
//////////////
|
||
|
||
// 전력 피크 컷과 전류 피크 컷의 최소값 계산
|
||
if (sekisairitsu >= 1.25) {
|
||
sekisairitsu *= 100;
|
||
int sekisairitsuCeil = (int) Math.ceil(sekisairitsu / 5.0) * 5;
|
||
double pcsEfficiency =
|
||
(pcsHenkankouritsu * 1000 <= 985) ? Math.ceil(pcsHenkankouritsu * 1000 / 5) * 5 : 985;
|
||
|
||
int i = 1, j = 1;
|
||
while (i < peakcut_by_pcs_data.length && peakcut_by_pcs_data[i][0] != sekisairitsuCeil) i++;
|
||
while (j < peakcut_by_pcs_data[0].length && peakcut_by_pcs_data[0][j] != pcsEfficiency) j++;
|
||
|
||
double peakcutByPcs = peakcut_by_pcs_data[i][j];
|
||
double peakcutByPcsKijun = peakcut_by_pcs_data[i][10];
|
||
double peakcutByPcsResult = peakcutByPcsKijun - peakcutByPcs;
|
||
|
||
watt_peakcut_result =
|
||
((100 - wattPeakcutAboutDeg0 + peakcutByPcsResult) / 100 < 1)
|
||
? ((100 - wattPeakcutAboutDeg0 + peakcutByPcsResult) / 100)
|
||
: 1.0;
|
||
} else {
|
||
watt_peakcut_result = 1.0;
|
||
}
|
||
|
||
// 피크컷이 없는 경우, 발전량에 곱하는 피크컷 계수
|
||
double[] peakcutResult = new double[2];
|
||
peakcutResult[0] = amp_peakcut_result;
|
||
peakcutResult[1] = Math.min(watt_peakcut_result, amp_peakcut_result);
|
||
|
||
return peakcutResult;
|
||
}
|
||
|
||
/** 0도 방위 전력 피크 컷을 계산하는 방법 */
|
||
private static double computePowerPeakCutDeg0(
|
||
int simulationPointNumber, double sekisairitsu, double koubai) {
|
||
// 0度近似曲線係数
|
||
double[] x = {1, 5, 15, 20, 30, 40};
|
||
double[] y = new double[6];
|
||
double x1, x2, y1, y2;
|
||
int m1;
|
||
double[] z1 = new double[4];
|
||
|
||
// 적재율과 다조년 데이터(방위 0도)에서 y를 대입
|
||
for (int i = 0; i < 6; i++) {
|
||
int idx = i * 6;
|
||
y[i] =
|
||
tashounen_data_deg0[simulationPointNumber][idx] * Math.pow(sekisairitsu, 5)
|
||
+ tashounen_data_deg0[simulationPointNumber][idx + 1] * Math.pow(sekisairitsu, 4)
|
||
+ tashounen_data_deg0[simulationPointNumber][idx + 2] * Math.pow(sekisairitsu, 3)
|
||
+ tashounen_data_deg0[simulationPointNumber][idx + 3] * Math.pow(sekisairitsu, 2)
|
||
+ tashounen_data_deg0[simulationPointNumber][idx + 4] * sekisairitsu
|
||
+ tashounen_data_deg0[simulationPointNumber][idx + 5];
|
||
}
|
||
|
||
// 3차 최소 제곱법
|
||
double[][] w = new double[4][5];
|
||
double[][] A = new double[6][4];
|
||
|
||
for (int i = 0; i < 6; i++) {
|
||
A[i][2] = x[i];
|
||
A[i][3] = 1.0;
|
||
x1 = A[i][2];
|
||
x2 = x1;
|
||
for (int j = 1; j >= 0; j--) {
|
||
x2 *= x1;
|
||
A[i][j] = x2;
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < 4; i++) {
|
||
for (int j = 0; j < 4; j++) {
|
||
w[i][j] = 0.0;
|
||
for (int k = 0; k < 6; k++) {
|
||
w[i][j] += A[k][i] * A[k][j];
|
||
}
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < 4; i++) {
|
||
w[i][4] = 0.0;
|
||
for (int j = 0; j < 6; j++) {
|
||
w[i][4] += A[j][i] * y[j];
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < 4; i++) {
|
||
y1 = 0.0;
|
||
m1 = i + 1;
|
||
int m2Index = 0;
|
||
for (int j = i; j < 4; j++) {
|
||
y2 = Math.abs(w[j][i]);
|
||
if (y1 < y2) {
|
||
y1 = y2;
|
||
m2Index = j;
|
||
}
|
||
}
|
||
|
||
for (int j = i; j < 5; j++) {
|
||
double temp = w[i][j];
|
||
w[i][j] = w[m2Index][j];
|
||
w[m2Index][j] = temp;
|
||
}
|
||
|
||
y1 = 1.0 / w[i][i];
|
||
|
||
for (int j = m1; j < 5; j++) {
|
||
w[i][j] *= y1;
|
||
}
|
||
|
||
for (int j = 0; j < 4; j++) {
|
||
if (j != i) {
|
||
for (int k = m1; k < 5; k++) {
|
||
w[j][k] -= w[j][i] * w[i][k];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// z1에 3차, 2차, 1차 계수와 절편을 대입
|
||
for (int i = 0; i < 4; i++) {
|
||
z1[i] = w[i][4];
|
||
}
|
||
|
||
// 방위각 90도 설치각에서의 전력 피크 컷
|
||
return (z1[0] * Math.pow(koubai, 3)) + (z1[1] * Math.pow(koubai, 2)) + (z1[2] * koubai) + z1[3];
|
||
}
|
||
|
||
/** 전류 피크 컷(5차) 90도 근사 곡선 계수 */
|
||
private static double computeAmpPeakCutDeg90(
|
||
int simulationPointNumber, double sekisairitsu, double koubai, double amp_delta) {
|
||
|
||
// 초기 설정
|
||
double[] x = {5, 15, 20, 30, 40, 50};
|
||
double[] z2 = new double[6];
|
||
double[] y = new double[6];
|
||
|
||
// 데이터 초기화 - y 값 계산 (amp_delta의 다항식 계산)
|
||
for (int i = 0; i < 6; i++) {
|
||
int idx = i * 5;
|
||
y[i] =
|
||
amp_peakcut_data_deg90[simulationPointNumber][idx] * Math.pow(amp_delta, 4)
|
||
+ amp_peakcut_data_deg90[simulationPointNumber][idx + 1] * Math.pow(amp_delta, 3)
|
||
+ amp_peakcut_data_deg90[simulationPointNumber][idx + 2] * Math.pow(amp_delta, 2)
|
||
+ amp_peakcut_data_deg90[simulationPointNumber][idx + 3] * amp_delta
|
||
+ amp_peakcut_data_deg90[simulationPointNumber][idx + 4];
|
||
}
|
||
|
||
// 최소 제곱법을 위한 행렬 A와 w 초기화
|
||
double[][] w = new double[6][7];
|
||
double[][] A = new double[6][6];
|
||
|
||
// A 행렬 설정 (x 값을 사용하여 다항식 계수 생성)
|
||
for (int i1 = 0; i1 < 6; i1++) {
|
||
A[i1][4] = x[i1];
|
||
A[i1][5] = 1.0;
|
||
double x1 = A[i1][4];
|
||
double x2 = x1;
|
||
for (int i2 = 3; i2 >= 0; i2--) {
|
||
x2 *= x1;
|
||
A[i1][i2] = x2;
|
||
}
|
||
}
|
||
|
||
// w 행렬 초기화
|
||
for (int i1 = 0; i1 < 6; i1++) {
|
||
for (int i2 = 0; i2 < 6; i2++) {
|
||
w[i1][i2] = 0.0;
|
||
for (int i3 = 0; i3 < 6; i3++) {
|
||
w[i1][i2] += A[i3][i1] * A[i3][i2];
|
||
}
|
||
}
|
||
}
|
||
|
||
// w의 마지막 열 값 설정
|
||
for (int i1 = 0; i1 < 6; i1++) {
|
||
w[i1][6] = 0.0;
|
||
for (int i2 = 0; i2 < 6; i2++) {
|
||
w[i1][6] += A[i2][i1] * y[i2];
|
||
}
|
||
}
|
||
|
||
// 가우스 소거법을 통해 연립방정식 해 구하기
|
||
for (int i1 = 0; i1 < 6; i1++) {
|
||
double y1 = 0.0;
|
||
int m1 = i1 + 1;
|
||
int m2 = 0;
|
||
|
||
for (int i2 = i1; i2 < 6; i2++) {
|
||
double y2 = Math.abs(w[i2][i1]);
|
||
if (y1 < y2) {
|
||
y1 = y2;
|
||
m2 = i2;
|
||
}
|
||
}
|
||
|
||
for (int i2 = i1; i2 < 7; i2++) {
|
||
y1 = w[i1][i2];
|
||
w[i1][i2] = w[m2][i2];
|
||
w[m2][i2] = y1;
|
||
}
|
||
|
||
y1 = 1.0 / w[i1][i1];
|
||
for (int i2 = m1; i2 < 7; i2++) {
|
||
w[i1][i2] *= y1;
|
||
}
|
||
|
||
for (int i2 = 0; i2 < 6; i2++) {
|
||
if (i2 != i1) {
|
||
for (int i3 = m1; i3 < 7; i3++) {
|
||
w[i2][i3] -= w[i2][i1] * w[i1][i3];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// z2에 계수 저장
|
||
for (int i1 = 0; i1 < 6; i1++) {
|
||
z2[i1] = w[i1][6];
|
||
}
|
||
|
||
return z2[0] * Math.pow(koubai, 5)
|
||
+ z2[1] * Math.pow(koubai, 4)
|
||
+ z2[2] * Math.pow(koubai, 3)
|
||
+ z2[3] * Math.pow(koubai, 2)
|
||
+ z2[4] * koubai
|
||
+ z2[5];
|
||
}
|
||
|
||
/** 전류 피크 컷(5차) 0도 근사 곡선 계수 */
|
||
private static double computeAmpPeakCutDeg0(
|
||
int simulationPointNumber,
|
||
double sekisairitsu,
|
||
double koubai,
|
||
boolean pcsAmpFound,
|
||
double amp_delta) {
|
||
double[] x = {5, 15, 20, 30, 40, 50};
|
||
double[] z2 = new double[6];
|
||
|
||
double[] y = new double[6];
|
||
for (int i = 0; i < 6; i++) {
|
||
int idx = i * 5;
|
||
y[i] =
|
||
amp_peakcut_data_deg0[simulationPointNumber][idx] * Math.pow(amp_delta, 4)
|
||
+ amp_peakcut_data_deg0[simulationPointNumber][idx + 1] * Math.pow(amp_delta, 3)
|
||
+ amp_peakcut_data_deg0[simulationPointNumber][idx + 2] * Math.pow(amp_delta, 2)
|
||
+ amp_peakcut_data_deg0[simulationPointNumber][idx + 3] * amp_delta
|
||
+ amp_peakcut_data_deg0[simulationPointNumber][idx + 4];
|
||
}
|
||
|
||
double[][] w = new double[6][7];
|
||
double[][] A = new double[6][6];
|
||
|
||
for (int i1 = 0; i1 < 6; i1++) {
|
||
A[i1][4] = x[i1];
|
||
A[i1][5] = 1.0;
|
||
double x1 = A[i1][4];
|
||
double x2 = x1;
|
||
for (int i2 = 3; i2 >= 0; i2--) {
|
||
x2 *= x1;
|
||
A[i1][i2] = x2;
|
||
}
|
||
}
|
||
|
||
for (int i1 = 0; i1 < 6; i1++) {
|
||
for (int i2 = 0; i2 < 6; i2++) {
|
||
w[i1][i2] = 0.0;
|
||
for (int i3 = 0; i3 < 6; i3++) {
|
||
w[i1][i2] += A[i3][i1] * A[i3][i2];
|
||
}
|
||
}
|
||
}
|
||
|
||
for (int i1 = 0; i1 < 6; i1++) {
|
||
w[i1][6] = 0.0;
|
||
for (int i2 = 0; i2 < 6; i2++) {
|
||
w[i1][6] += A[i2][i1] * y[i2];
|
||
}
|
||
}
|
||
|
||
for (int i1 = 0; i1 < 6; i1++) {
|
||
double y1 = 0.0;
|
||
int m1 = i1 + 1;
|
||
int m2 = 0;
|
||
for (int i2 = i1; i2 < 6; i2++) {
|
||
double y2 = Math.abs(w[i2][i1]);
|
||
if (y1 < y2) {
|
||
y1 = y2;
|
||
m2 = i2;
|
||
}
|
||
}
|
||
|
||
for (int i2 = i1; i2 < 7; i2++) {
|
||
y1 = w[i1][i2];
|
||
w[i1][i2] = w[m2][i2];
|
||
w[m2][i2] = y1;
|
||
}
|
||
|
||
y1 = 1.0 / w[i1][i1];
|
||
for (int i2 = m1; i2 < 7; i2++) {
|
||
w[i1][i2] *= y1;
|
||
}
|
||
|
||
for (int i2 = 0; i2 < 6; i2++) {
|
||
if (i2 != i1) {
|
||
for (int i3 = m1; i3 < 7; i3++) {
|
||
w[i2][i3] -= w[i2][i1] * w[i1][i3];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
for (int i1 = 0; i1 < 6; i1++) {
|
||
z2[i1] = w[i1][6];
|
||
}
|
||
|
||
return z2[0] * Math.pow(koubai, 5)
|
||
+ z2[1] * Math.pow(koubai, 4)
|
||
+ z2[2] * Math.pow(koubai, 3)
|
||
+ z2[3] * Math.pow(koubai, 2)
|
||
+ z2[4] * koubai
|
||
+ z2[5];
|
||
}
|
||
|
||
/** 0도 방위 전력 피크 컷을 계산하는 방법 */
|
||
private static double computePowerPeakCutDeg90(
|
||
int simulationPointNumber, double sekisairitsu, double koubai) {
|
||
// 데이터 초기화
|
||
double[] x = {1, 5, 15, 20, 30, 40};
|
||
|
||
// y 배열 초기화 및 값 할당
|
||
double[] y = new double[6];
|
||
for (int i = 0; i < 6; i++) {
|
||
int idx = i * 6;
|
||
y[i] =
|
||
tashounen_data_deg90[simulationPointNumber][idx] * Math.pow(sekisairitsu, 5)
|
||
+ tashounen_data_deg90[simulationPointNumber][idx + 1] * Math.pow(sekisairitsu, 4)
|
||
+ tashounen_data_deg90[simulationPointNumber][idx + 2] * Math.pow(sekisairitsu, 3)
|
||
+ tashounen_data_deg90[simulationPointNumber][idx + 3] * Math.pow(sekisairitsu, 2)
|
||
+ tashounen_data_deg90[simulationPointNumber][idx + 4] * sekisairitsu
|
||
+ tashounen_data_deg90[simulationPointNumber][idx + 5];
|
||
}
|
||
|
||
// w 행렬 초기화
|
||
double[][] w = new double[4][5];
|
||
|
||
// A 행렬 초기화
|
||
double[][] A = new double[6][4];
|
||
for (int i = 0; i < 6; i++) {
|
||
A[i][2] = x[i];
|
||
A[i][3] = 1.0;
|
||
double x1 = A[i][2];
|
||
double x2 = x1;
|
||
for (int j = 1; j >= 0; j--) {
|
||
x2 *= x1;
|
||
A[i][j] = x2;
|
||
}
|
||
}
|
||
|
||
// w[i1][i2] 계산
|
||
for (int i1 = 0; i1 < 4; i1++) {
|
||
for (int i2 = 0; i2 < 4; i2++) {
|
||
w[i1][i2] = 0.0;
|
||
for (int i3 = 0; i3 < 6; i3++) {
|
||
w[i1][i2] += A[i3][i1] * A[i3][i2];
|
||
}
|
||
}
|
||
}
|
||
|
||
// w[i1][4] 계산
|
||
for (int i1 = 0; i1 < 4; i1++) {
|
||
w[i1][4] = 0.0;
|
||
for (int i2 = 0; i2 < 6; i2++) {
|
||
w[i1][4] += A[i2][i1] * y[i2];
|
||
}
|
||
}
|
||
|
||
// 가우스 소거법 적용
|
||
for (int i1 = 0; i1 < 4; i1++) {
|
||
double y1 = 0.0;
|
||
int m1 = i1 + 1;
|
||
int m2 = 0;
|
||
for (int i2 = i1; i2 < 4; i2++) {
|
||
double y2 = Math.abs(w[i2][i1]);
|
||
if (y1 < y2) {
|
||
y1 = y2;
|
||
m2 = i2;
|
||
}
|
||
}
|
||
|
||
for (int i2 = i1; i2 < 5; i2++) {
|
||
y1 = w[i1][i2];
|
||
w[i1][i2] = w[m2][i2];
|
||
w[m2][i2] = y1;
|
||
}
|
||
|
||
y1 = 1.0 / w[i1][i1];
|
||
for (int i2 = m1; i2 < 5; i2++) {
|
||
w[i1][i2] *= y1;
|
||
}
|
||
|
||
for (int i2 = 0; i2 < 4; i2++) {
|
||
if (i2 != i1) {
|
||
for (int i3 = m1; i3 < 5; i3++) {
|
||
w[i2][i3] -= w[i2][i1] * w[i1][i3];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 결과 z1 배열에 저장
|
||
double[] z1 = new double[4];
|
||
for (int i1 = 0; i1 < 4; i1++) {
|
||
z1[i1] = w[i1][4];
|
||
}
|
||
|
||
// 출력 결과 확인
|
||
// System.out.println("Fitted coefficients:");
|
||
// for (double coef : z1) {
|
||
// System.out.println(coef);
|
||
// }
|
||
|
||
return (z1[0] * Math.pow(koubai, 3)) + (z1[1] * Math.pow(koubai, 2)) + (z1[2] * koubai) + z1[3];
|
||
}
|
||
|
||
// 설치면의 사면 일사량 계산
|
||
public double[] shamenNissharyou(double koubai, double houi, int simulationPointNumber) {
|
||
|
||
double[] shamenNissha = new double[12]; // 경사면 일사량 대입을 위한 빈 배열 변수 생성
|
||
|
||
int i = simulationPointNumber * 12; // 일사량 관측 지점의 행 번호로부터 데이터베이스의 일치하는 행을 산출
|
||
// (例:波照間は836行目→データベースでは836×12=10032行目から10044行目までが1月~12月)
|
||
|
||
if (koubai == 0) { // 경사가 0이면 경사 일사량 = 수평면 일사
|
||
for (int m = 0; m < 12; m++) {
|
||
shamenNissha[m] = suiheimennissha[i];
|
||
i++;
|
||
}
|
||
} else { // 기울기가 0이 아니면 경사 일사량 계산
|
||
|
||
// 경사면 일사량 (직접, 산란, 지면 반사)의 도출을위한 빈 배열 변수 생성
|
||
double[][] cosTheta = new double[12][26];
|
||
double[][] rb = new double[12][26];
|
||
double[][] io = new double[12][26];
|
||
double[][] I = new double[12][26];
|
||
double[][] id = new double[12][26];
|
||
double[][] Isby = new double[12][26];
|
||
double[][] Ibby = new double[12][26];
|
||
double[][] Irby = new double[12][26];
|
||
|
||
// 시간
|
||
double[] timeParam = {
|
||
100, 100, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5,
|
||
15.5, 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5
|
||
};
|
||
|
||
double IsbyMath = 0;
|
||
double[] IsbyTotal = new double[12];
|
||
|
||
double IbbyMath = 0;
|
||
double[] IbbyTotal = new double[12];
|
||
|
||
double IrbyMath = 0;
|
||
double[] IrbyTotal = new double[12];
|
||
|
||
i = simulationPointNumber * 12; // Reset i
|
||
// System.err.println( " i >>>>>>>>> " + i);
|
||
|
||
// 매월 각 시간의 각 파라미터 계산
|
||
for (int m = 0; m < 12; m++) {
|
||
// System.err.println( " " );
|
||
// System.err.println( " m >>>>>>>>> " + m);
|
||
for (int j = 0; j < 26; j++) {
|
||
// System.err.println( " i >>>>>>>>> " + i);
|
||
// System.err.println( " j >>>>>>>>> " + j);
|
||
// System.err.println( " latlng[i] >>>>>>>>> " + latlng[i]);
|
||
// System.err.println( " latlng[i][0] >>>>>>>>> " + latlng[i][0]);
|
||
// System.err.println( " taiyousekii[i] >>>>>>>>> " + taiyousekii[i]);
|
||
// System.err.println( " jikakudo[i][j] >>>>>>>>> " + jikakudo[i][j]);
|
||
// System.err.println( " koubai>>>>>>>>> " + koubai);
|
||
// System.err.println( " houi>>>>>>>>> " + houi);
|
||
// System.err.println( " 결과>>>>>>>>> " + Math.max(0, (Math.sin(latlng[i][0]) *
|
||
// Math.cos(koubai) - Math.cos(latlng[i][0]) * Math.sin(koubai) * Math.cos(houi)) *
|
||
// Math.sin(taiyousekii[i])
|
||
// + (Math.cos(latlng[i][0]) * Math.cos(koubai) + Math.sin(latlng[i][0]) *
|
||
// Math.sin(koubai) * Math.cos(houi)) * Math.cos(taiyousekii[i]) *
|
||
// Math.cos(jikakudo[i][j])
|
||
// + Math.cos(taiyousekii[i]) * Math.sin(koubai) * Math.sin(houi) *
|
||
// Math.sin(jikakudo[i][j])));
|
||
// System.err.println( " " );
|
||
|
||
cosTheta[m][j] =
|
||
Math.max(
|
||
0,
|
||
(Math.sin(latlng[i][0]) * Math.cos(koubai)
|
||
- Math.cos(latlng[i][0]) * Math.sin(koubai) * Math.cos(houi))
|
||
* Math.sin(taiyousekii[i])
|
||
+ (Math.cos(latlng[i][0]) * Math.cos(koubai)
|
||
+ Math.sin(latlng[i][0]) * Math.sin(koubai) * Math.cos(houi))
|
||
* Math.cos(taiyousekii[i])
|
||
* Math.cos(jikakudo[i][j])
|
||
+ Math.cos(taiyousekii[i])
|
||
* Math.sin(koubai)
|
||
* Math.sin(houi)
|
||
* Math.sin(jikakudo[i][j]));
|
||
|
||
rb[m][j] = (cosThetaZ[i][j] == 0) ? 0 : cosTheta[m][j] / cosThetaZ[i][j];
|
||
io[m][j] = ion[i] * cosThetaZ[i][j];
|
||
I[m][j] = suiheimennissha[i] * rtx[i][j];
|
||
id[m][j] = Math.min(sanrannissha[i] * rdx[i][j], I[m][j]);
|
||
|
||
Isby[m][j] =
|
||
Math.max(
|
||
0,
|
||
(io[m][j] == 0)
|
||
? 0
|
||
: id[m][j]
|
||
* (((I[m][j] - id[m][j]) / io[m][j]) * rb[m][j]
|
||
+ (1 - (I[m][j] - id[m][j]) / io[m][j])
|
||
* (1 + Math.cos(koubai))
|
||
/ 2));
|
||
Ibby[m][j] = Math.max(0, (I[m][j] - id[m][j]) * rb[m][j]);
|
||
Irby[m][j] = I[m][j] * jimenhansharitsu[i] * ((1 - Math.cos(koubai)) / 2);
|
||
}
|
||
i++;
|
||
}
|
||
|
||
// Reset i
|
||
i = simulationPointNumber * 12;
|
||
|
||
// 직접, 산란 계산
|
||
for (int m = 0; m < 12; m++) {
|
||
for (int j = 0; j < 26; j++) {
|
||
if (timeParam[j] > (Math.ceil(hinode_nichibotsu[i][0] * 2.0) / 2)
|
||
&& timeParam[j] < (Math.floor(hinode_nichibotsu[i][1] * 2.0) / 2)) {
|
||
IsbyMath += Isby[m][j];
|
||
IbbyMath += Ibby[m][j];
|
||
}
|
||
}
|
||
|
||
IsbyMath +=
|
||
((((Math.ceil(hinode_nichibotsu[i][0]) - 0.5) < hinode_nichibotsu[i][0])
|
||
? 0
|
||
: Isby[m][0])
|
||
+ (((Math.floor(hinode_nichibotsu[i][1]) + 0.5) > hinode_nichibotsu[i][1])
|
||
? 0
|
||
: Isby[m][1]));
|
||
IsbyTotal[m] = IsbyMath;
|
||
|
||
IbbyMath +=
|
||
(((Math.ceil(hinode_nichibotsu[i][0]) - 0.5) < hinode_nichibotsu[i][0])
|
||
? 0
|
||
: Ibby[m][0])
|
||
+ (((Math.floor(hinode_nichibotsu[i][1]) + 0.5) > hinode_nichibotsu[i][1])
|
||
? 0
|
||
: Ibby[m][1]);
|
||
IbbyTotal[m] = IbbyMath;
|
||
|
||
IsbyMath = 0;
|
||
IbbyMath = 0;
|
||
i++;
|
||
}
|
||
|
||
// Reset i
|
||
i = simulationPointNumber * 12;
|
||
|
||
// 지면 반사 계산
|
||
for (int m = 0; m < 12; m++) {
|
||
for (int j = 0; j < 26; j++) {
|
||
// 배열 범위에 데이터가 없어서 추가했음.
|
||
if ((j + 2 < rd[i].length)
|
||
&& (rd[i][j + 2]
|
||
> ((jikakudo[i][0] >= 0)
|
||
? Math.ceil(jikakudo[i][0] * 2.0) / 2
|
||
: Math.floor(jikakudo[i][0] * 2.0) / 2))
|
||
&& (rd[i][j + 2]
|
||
< ((jikakudo[i][1] >= 0)
|
||
? Math.floor(jikakudo[i][1] * 2.0) / 2
|
||
: Math.ceil(jikakudo[i][1] * 2.0) / 2))) {
|
||
IrbyMath += Irby[m][j + 2];
|
||
}
|
||
}
|
||
|
||
IrbyMath +=
|
||
(((jikakudo[i][0] >= 0)
|
||
? Math.ceil(jikakudo[i][0]) - 0.5
|
||
: Math.floor(jikakudo[i][0]) - 0.5)
|
||
< jikakudo[i][0]
|
||
? 0
|
||
: Irby[m][0])
|
||
+ (((jikakudo[i][1] >= 0)
|
||
? Math.floor(jikakudo[i][1]) + 0.5
|
||
: Math.ceil(jikakudo[i][1]) + 0.5)
|
||
> jikakudo[i][1]
|
||
? 0
|
||
: Irby[m][1]);
|
||
IrbyTotal[m] = IrbyMath;
|
||
|
||
IrbyMath = 0;
|
||
i++;
|
||
}
|
||
|
||
log.error(Arrays.toString(IsbyTotal));
|
||
// 직달, 산란, 지면 반사를 매월 합계
|
||
for (int m = 0; m < 12; m++) {
|
||
shamenNissha[m] = IsbyTotal[m] + IbbyTotal[m] + IrbyTotal[m];
|
||
}
|
||
}
|
||
|
||
return shamenNissha;
|
||
}
|
||
|
||
public static double[] readValuesFromFile(String fileName) {
|
||
List<Double> values = new ArrayList<>();
|
||
|
||
try (InputStream inputStream =
|
||
PwrGnrSimService.class.getClassLoader().getResourceAsStream(fileName);
|
||
BufferedReader br =
|
||
new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
|
||
String line;
|
||
while ((line = br.readLine()) != null) {
|
||
// 줄을 , 기준으로 분리하여 배열에 추가
|
||
String[] parts = line.split(",");
|
||
for (String part : parts) {
|
||
values.add(Double.parseDouble(part.trim()));
|
||
}
|
||
}
|
||
} catch (IOException e) {
|
||
e.printStackTrace();
|
||
}
|
||
|
||
// List를 배열로 변환하여 반환
|
||
return values.stream().mapToDouble(Double::doubleValue).toArray();
|
||
}
|
||
|
||
public static String[][] readValuesFromFileString2(String fileName) {
|
||
|
||
List<String[]> values = new ArrayList<>();
|
||
StringBuilder sb = new StringBuilder();
|
||
|
||
try (InputStream inputStream =
|
||
PwrGnrSimService.class.getClassLoader().getResourceAsStream(fileName);
|
||
BufferedReader br =
|
||
new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
|
||
|
||
String line;
|
||
while ((line = br.readLine()) != null) {
|
||
sb.append(line);
|
||
}
|
||
} catch (IOException e) {
|
||
e.printStackTrace();
|
||
}
|
||
|
||
String content = sb.toString();
|
||
content = content.substring(1, content.length() - 1); // 시작과 끝의 중괄호 제거
|
||
String[] arrays = content.split("\\}\\s*,\\s*\\{");
|
||
|
||
for (String innerArray : arrays) {
|
||
innerArray = innerArray.replaceAll("[{}]", "").trim();
|
||
String[] elements = innerArray.split(",");
|
||
String[] row = new String[elements.length];
|
||
|
||
for (int i = 0; i < elements.length; i++) {
|
||
String trimmedValue = elements[i].trim();
|
||
trimmedValue = trimmedValue.replace("\"", "");
|
||
row[i] = trimmedValue.isEmpty() ? "" : trimmedValue;
|
||
}
|
||
values.add(row);
|
||
}
|
||
|
||
return values.toArray(new String[0][]);
|
||
}
|
||
|
||
public static double[][] readValuesFromFile2(String fileName) {
|
||
List<double[]> values = new ArrayList<>();
|
||
|
||
StringBuilder sb = new StringBuilder();
|
||
|
||
try (InputStream inputStream =
|
||
PwrGnrSimService.class.getClassLoader().getResourceAsStream(fileName);
|
||
BufferedReader br =
|
||
new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
|
||
String line;
|
||
while ((line = br.readLine()) != null) {
|
||
sb.append(line);
|
||
}
|
||
} catch (IOException e) {
|
||
e.printStackTrace();
|
||
}
|
||
|
||
String content = sb.toString();
|
||
content = content.substring(1, content.length() - 1); // 시작과 끝의 중괄호 제거
|
||
String[] arrays = content.split("\\},\\{");
|
||
|
||
for (String innerArray : arrays) {
|
||
|
||
innerArray = innerArray.replaceAll("[{}]", "").trim();
|
||
|
||
String[] elements = innerArray.split(","); // 각 요소를 쉼표로 분리
|
||
double[] row = new double[elements.length];
|
||
|
||
for (int i = 0; i < elements.length; i++) {
|
||
String trimmedValue = elements[i].trim();
|
||
if (trimmedValue.isEmpty()) {
|
||
row[i] = 0.0; // 빈 문자열인 경우 기본값 설정
|
||
} else {
|
||
row[i] = Double.parseDouble(trimmedValue);
|
||
}
|
||
}
|
||
values.add(row);
|
||
}
|
||
|
||
// List<double[]>을 double[][]로 변환하여 반환
|
||
return values.toArray(new double[0][]);
|
||
}
|
||
|
||
public PwrGnrSimGuideResponse selectPwrGnrSimulationGuideInfo() throws Exception {
|
||
PwrGnrSimGuideResponse guideResponse = new PwrGnrSimGuideResponse();
|
||
|
||
String strResponse =
|
||
interfaceQsp.callApi(HttpMethod.GET, qspUrl + qspSimulationGuideInfUrl, null);
|
||
|
||
if (!"".equals(strResponse)) {
|
||
ObjectMapper om =
|
||
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||
guideResponse = om.readValue(strResponse, PwrGnrSimGuideResponse.class);
|
||
}
|
||
|
||
return guideResponse;
|
||
}
|
||
|
||
public static Document pwrGnrSimPdfHtml(Document doc, PwrGnrSimResponse data)
|
||
throws IOException, QcastException {
|
||
Element elm;
|
||
|
||
// 상단 요약정보
|
||
elm = doc.getElementById("objectNo");
|
||
elm.text(
|
||
StringUtils.defaultString(data.getObjectNo() + " (Plan No : " + data.getPlanNo() + ")"));
|
||
|
||
elm = doc.getElementById("drawingEstimateCreateDate");
|
||
elm.text(StringUtils.defaultString(data.getDrawingEstimateCreateDate()));
|
||
|
||
elm = doc.getElementById("prefName");
|
||
|
||
elm.text(StringUtils.defaultString(data.getPrefName()));
|
||
|
||
elm = doc.getElementById("areaName");
|
||
elm.text(StringUtils.defaultString(data.getAreaName()));
|
||
|
||
elm = doc.getElementById("capacity");
|
||
elm.text(StringUtils.defaultString(data.getCapacity()));
|
||
|
||
elm = doc.getElementById("anlFrcsGnrt");
|
||
elm.text(StringUtils.defaultString(String.valueOf(data.getAnlFrcsGnrt())));
|
||
|
||
elm = doc.getElementById("snowfall");
|
||
elm.text(StringUtils.defaultString(data.getSnowfall()));
|
||
|
||
elm = doc.getElementById("standardWindSpeedId");
|
||
elm.text(StringUtils.defaultString(data.getStandardWindSpeedId()));
|
||
|
||
if (data.getFrcPwrGnrList() != null && data.getFrcPwrGnrList().length > 0) {
|
||
|
||
int[] onlyData =
|
||
Arrays.copyOfRange(data.getFrcPwrGnrList(), 0, data.getFrcPwrGnrList().length - 1);
|
||
|
||
int referenceValue = 300; // table 높이
|
||
int orgMaxValue =
|
||
Arrays.stream(onlyData)
|
||
.max()
|
||
.orElseThrow(() -> new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, "배열이 없음"));
|
||
int maxValue = roundUpAuto(orgMaxValue / 8);
|
||
|
||
// chart y축
|
||
StringBuilder sb = new StringBuilder();
|
||
for (int i = 8; i >= 1; i--) {
|
||
sb.append("<tr>");
|
||
sb.append("<td ")
|
||
.append(i == 8 ? "class='top'" : "")
|
||
.append(">")
|
||
.append(maxValue * i)
|
||
.append("</td>");
|
||
sb.append("</tr>");
|
||
}
|
||
sb.append("<tr>");
|
||
sb.append("<td class='zero'>0</td>");
|
||
elm = doc.getElementById("htmlY");
|
||
elm.append(sb.toString());
|
||
|
||
// chart 데이터
|
||
String[] color = {
|
||
"#B5D4F5", "#FFE899", "#FBC3AB", "#D1D1D1", "#FFE899", "#B5D0F0", "#C1E4B8", "#A3C9EE",
|
||
"#F7BBA2", "#BEBEBE", "#A8BBE0", "#B5D7A3"
|
||
};
|
||
StringBuilder sb2 = new StringBuilder();
|
||
for (int i = 0; i < onlyData.length; i++) {
|
||
double scaledValues = ((double) onlyData[i] / (maxValue * 8)) * referenceValue;
|
||
|
||
sb2.append("<td class='bar-cell'>");
|
||
|
||
sb2.append("<table class='chart-line' style='border-collapse: collapse;'>")
|
||
.append("<tbody>")
|
||
.append("<tr>")
|
||
.append("<td class='top' style='border-bottom: 1px solid #ddd;'></td>")
|
||
.append("</tr>")
|
||
.append("<tr>")
|
||
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
|
||
.append("</tr>")
|
||
.append("<tr>")
|
||
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
|
||
.append("</tr>")
|
||
.append("<tr>")
|
||
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
|
||
.append("</tr>")
|
||
.append("<tr>")
|
||
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
|
||
.append("</tr>")
|
||
.append("<tr>")
|
||
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
|
||
.append("</tr>")
|
||
.append("<tr>")
|
||
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
|
||
.append("</tr>")
|
||
.append("<tr>")
|
||
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
|
||
.append("</tr>")
|
||
.append("<tr>")
|
||
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
|
||
.append("</tr>")
|
||
.append("</tbody>")
|
||
.append("</table>");
|
||
|
||
sb2.append(
|
||
"<div class='bar' style='height: "
|
||
+ scaledValues
|
||
+ "px; background-color: "
|
||
+ color[i]
|
||
+ "'></div>");
|
||
sb2.append("</td>");
|
||
}
|
||
elm = doc.getElementById("htmlX");
|
||
elm.append(sb2.toString());
|
||
}
|
||
|
||
// 예측발전량
|
||
if (data.getFrcPwrGnrList() != null && data.getFrcPwrGnrList().length > 0) {
|
||
StringBuilder sb = new StringBuilder();
|
||
for (int i = 0; i < 13; i++) {
|
||
sb.append(
|
||
"<td>"
|
||
+ StringUtils.defaultString(String.valueOf(data.getFrcPwrGnrList()[i]))
|
||
+ "</td>");
|
||
}
|
||
|
||
elm = doc.getElementById("frcPwrGnrList_detail");
|
||
elm.append(sb.toString());
|
||
}
|
||
|
||
// 모듈 list
|
||
if (data.getRoofModuleList() != null && data.getRoofModuleList().size() > 0) {
|
||
StringBuilder sb = new StringBuilder();
|
||
|
||
for (int i = 0; i < data.getRoofModuleList().size(); i++) {
|
||
PwrGnrSimRoofResponse listItem = data.getRoofModuleList().get(i);
|
||
sb.append("<tr>");
|
||
sb.append("<td>" + StringUtils.defaultString(listItem.getRoofSurface()) + "</td>");
|
||
sb.append("<td>" + Optional.ofNullable(listItem.getSlopeAngle()).orElse("") + "</td>");
|
||
sb.append("<td>" + StringUtils.defaultString(listItem.getAzimuth()) + "</td>");
|
||
sb.append("<td>" + StringUtils.defaultString(listItem.getItemNo()) + "</td>");
|
||
sb.append("<td>" + StringUtils.defaultString(listItem.getAmount()) + "</td>");
|
||
sb.append("</tr>");
|
||
}
|
||
|
||
elm = doc.getElementById("roofModuleList_detail");
|
||
elm.append(sb.toString());
|
||
}
|
||
|
||
// pcs list
|
||
if (data.getPcsList() != null && data.getPcsList().size() > 0) {
|
||
StringBuilder sb = new StringBuilder();
|
||
|
||
for (int i = 0; i < data.getPcsList().size(); i++) {
|
||
PwrGnrSimRoofResponse listItem = data.getPcsList().get(i);
|
||
|
||
sb.append("<tr>");
|
||
sb.append("<td>" + StringUtils.defaultString(listItem.getItemNo()) + "</td>");
|
||
sb.append("<td>" + StringUtils.defaultString(listItem.getAmount()) + "</td>");
|
||
sb.append("</tr>");
|
||
}
|
||
|
||
elm = doc.getElementById("pcsList_detail");
|
||
elm.append(sb.toString());
|
||
}
|
||
|
||
return doc;
|
||
}
|
||
|
||
public static int roundUpAuto(double value) {
|
||
int length = (int) Math.log10(value) + 1; // 숫자의 자리수를 계산
|
||
int place = (int) Math.pow(10, length - 1); // 자리수에 따른 올림 기준(10, 100, 1000 등)
|
||
|
||
return (int) Math.ceil(value / place) * place;
|
||
}
|
||
}
|