1506 lines
54 KiB
Java
1506 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.util.InterfaceQsp;
|
||
import java.io.*;
|
||
import java.nio.charset.StandardCharsets;
|
||
import java.text.DecimalFormat;
|
||
import java.util.ArrayList;
|
||
import java.util.Arrays;
|
||
import java.util.Comparator;
|
||
import java.util.List;
|
||
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.Value;
|
||
import org.springframework.http.HttpMethod;
|
||
import org.springframework.stereotype.Service;
|
||
|
||
@Slf4j
|
||
@Service
|
||
@RequiredArgsConstructor
|
||
public class PwrGnrSimService {
|
||
private final InterfaceQsp interfaceQsp;
|
||
|
||
@Value("${qsp.url}")
|
||
private String qspUrl;
|
||
|
||
@Value("${qsp.simulation-guide-info-url}")
|
||
private String qspSimulationGuideInfUrl;
|
||
|
||
private final PwrGnrSimMapper pwrGnrSimMapper;
|
||
|
||
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");
|
||
|
||
// 위도 경도[rad]
|
||
double[][] latlng = readValuesFromFile2("template/pwrGnrSimulation/latlng.txt");
|
||
|
||
double[][] rd = readValuesFromFile2("template/pwrGnrSimulation/rd.txt");
|
||
|
||
double[][] rdx = readValuesFromFile2("template/pwrGnrSimulation/rdx.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) {
|
||
|
||
// 데이터가 없어서 오류가 발생한 경우, 빈값으로 리턴
|
||
PwrGnrSimResponse exceptionRes = new PwrGnrSimResponse();
|
||
double[] dExceptionData = new double[13];
|
||
String[] strExceptionData = formatAndPrintArray(dExceptionData);
|
||
exceptionRes.setHatsudenryouAll(strExceptionData);
|
||
exceptionRes.setHatsudenryouAllSnow(strExceptionData);
|
||
exceptionRes.setHatsudenryouPeakcutAll(strExceptionData);
|
||
exceptionRes.setHatsudenryouPeakcutAllSnow(strExceptionData);
|
||
|
||
// 견적서 정보를 조회한다.
|
||
PwrGnrSimPlanResponse planInfo = pwrGnrSimMapper.selectPlanInfo(pwrGnrSimRequest);
|
||
|
||
// 지역이 없으면 계산 불가능
|
||
if (planInfo == null || planInfo.getAreaId() == 0) {
|
||
log.error("지역값이 없음.");
|
||
return exceptionRes;
|
||
}
|
||
|
||
// 견적서의 지붕재 목록 조회
|
||
List<PwrGnrSimRoofResponse> roofList = pwrGnrSimMapper.selectRoofList(pwrGnrSimRequest);
|
||
int roofLength = roofList.size(); // Set의 크기 = 고유 roofNo 개수
|
||
|
||
// 지붕재 정보가 없음.
|
||
if (roofList == null || roofList.isEmpty()) {
|
||
log.error("지붕재 정보가 없음.");
|
||
return exceptionRes;
|
||
}
|
||
|
||
// 지붕재의 모든 모듈 Item 조회
|
||
pwrGnrSimRequest.setItemGroup("MODULE_");
|
||
List<PwrGnrSimRoofResponse> roofModuleList =
|
||
pwrGnrSimMapper.selectRoofItemList(pwrGnrSimRequest);
|
||
|
||
// 전체 모듈에서 모듈 그룹형태로 변경
|
||
List<PwrGnrSimRoofResponse> moduleGroupList =
|
||
roofModuleList.stream()
|
||
.collect(
|
||
Collectors.groupingBy(
|
||
PwrGnrSimRoofResponse::getItemId, // 첫 번째 그룹화 기준
|
||
Collectors.collectingAndThen(
|
||
Collectors.toList(),
|
||
list -> {
|
||
// 그룹화된 첫 번째 요소를 기반으로 새 객체 생성
|
||
ObjectMapper mapper = new ObjectMapper();
|
||
PwrGnrSimRoofResponse setDto =
|
||
mapper.convertValue(list.get(0), PwrGnrSimRoofResponse.class);
|
||
|
||
// specification 합산
|
||
double totSpecification =
|
||
list.stream()
|
||
.mapToDouble(response -> response.getTotSpecification())
|
||
.sum();
|
||
|
||
totSpecification = totSpecification / 1000;
|
||
|
||
// 합산 결과 설정
|
||
setDto.setTotSpecification(totSpecification);
|
||
|
||
return setDto;
|
||
})))
|
||
.values()
|
||
.stream()
|
||
.sorted(
|
||
Comparator.comparingDouble(PwrGnrSimRoofResponse::getTotSpecification)
|
||
.reversed()) // 내림차순 정렬
|
||
.collect(Collectors.toList());
|
||
|
||
// 모듈 아이템이 없음.
|
||
if (moduleGroupList.isEmpty()) {
|
||
log.error("모듈 아이템이 없음.");
|
||
return exceptionRes;
|
||
}
|
||
|
||
// 지붕재의 모든 PCS Item 조회
|
||
// pwrGnrSimRequest.setItemGroup("PC_");
|
||
// List<PwrGnrSimRoofResponse> roofPcsList =
|
||
// pwrGnrSimMapper.selectRoofItemList(pwrGnrSimRequest);
|
||
|
||
// GROUP PCS Group Item 조회
|
||
List<PwrGnrSimRoofResponse> pcsGroupList =
|
||
pwrGnrSimMapper.selectRoofPcsGroupList(pwrGnrSimRequest);
|
||
|
||
// pcs 아이템이 없음.
|
||
if (pcsGroupList.isEmpty()) {
|
||
log.error("PCS 아이템이 없음.");
|
||
return exceptionRes;
|
||
}
|
||
|
||
// 견적서 정보 기준으로 발전시뮬레이션 계산을 진행한다.
|
||
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 : pcsGroupList) {
|
||
if (k == 0) {
|
||
pwrGnrSimReq.setPcs1(p.getItemId());
|
||
pwrGnrSimReq.setPcsInput1(Integer.parseInt(p.getAmount()));
|
||
pwrGnrSimReq.setPcsCnvEff1(p.getCnvEff() == 0.0 ? 1 : p.getCnvEff()); // 디폴트 값 1
|
||
pwrGnrSimReq.setPcsAmp1(p.getAmp());
|
||
pwrGnrSimReq.setPcsSpecification1(p.getSpecification() / 1000);
|
||
} else if (k == 1) {
|
||
pwrGnrSimReq.setPcs2(p.getItemId());
|
||
pwrGnrSimReq.setPcsInput2(Integer.parseInt(p.getAmount()));
|
||
pwrGnrSimReq.setPcsCnvEff2(p.getCnvEff() == 0.0 ? 1 : p.getCnvEff());
|
||
pwrGnrSimReq.setPcsAmp2(p.getAmp());
|
||
pwrGnrSimReq.setPcsSpecification2(p.getSpecification() / 1000);
|
||
} else {
|
||
pwrGnrSimReq.setPcs3(p.getItemId());
|
||
pwrGnrSimReq.setPcsInput3(Integer.parseInt(p.getAmount()));
|
||
pwrGnrSimReq.setPcsCnvEff3(p.getCnvEff() == 0.0 ? 1 : p.getCnvEff());
|
||
pwrGnrSimReq.setPcsAmp3(p.getAmp());
|
||
pwrGnrSimReq.setPcsSpecification3(p.getSpecification() / 1000);
|
||
}
|
||
k++;
|
||
}
|
||
// module 정보
|
||
k = 0;
|
||
for (PwrGnrSimRoofResponse m : moduleGroupList) {
|
||
if (k == 0) {
|
||
pwrGnrSimReq.setModule1(m.getItemId());
|
||
pwrGnrSimReq.setModuleAmp1(m.getAmp());
|
||
pwrGnrSimReq.setModuleSpecification1(m.getSpecification());
|
||
pwrGnrSimReq.setModuleTempCoeff1(m.getTempCoeff());
|
||
pwrGnrSimReq.setModuleTempLoss1(m.getTempLoss());
|
||
} else if (k == 1) {
|
||
pwrGnrSimReq.setModule2(m.getItemId());
|
||
pwrGnrSimReq.setModuleAmp2(m.getAmp());
|
||
pwrGnrSimReq.setModuleSpecification2(m.getSpecification());
|
||
pwrGnrSimReq.setModuleTempCoeff2(m.getTempCoeff());
|
||
pwrGnrSimReq.setModuleTempLoss2(m.getTempLoss());
|
||
} else {
|
||
pwrGnrSimReq.setModule3(m.getItemId());
|
||
pwrGnrSimReq.setModuleAmp3(m.getAmp());
|
||
pwrGnrSimReq.setModuleSpecification3(m.getSpecification());
|
||
pwrGnrSimReq.setModuleTempCoeff3(m.getTempCoeff());
|
||
pwrGnrSimReq.setModuleTempLoss3(m.getTempLoss());
|
||
}
|
||
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 : roofModuleList) {
|
||
if (data.getRoofNo().equals(m.getRoofNo())) {
|
||
dSpecification += m.getTotSpecification();
|
||
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 = new PwrGnrSimResponse();
|
||
try {
|
||
pwrGnrSimRes = this.calcResults(pwrGnrSimReq, roofLength);
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
}
|
||
|
||
// DecimalFormat 객체 생성
|
||
DecimalFormat decimalFormat = new DecimalFormat("#,##0.000");
|
||
// 포맷 적용
|
||
String formatSpecification = decimalFormat.format(dSpecification / 1000);
|
||
|
||
pwrGnrSimRes.setObjectNo(planInfo.getObjectNo());
|
||
pwrGnrSimRes.setPlanNo(planInfo.getPlanNo());
|
||
pwrGnrSimRes.setDrawingEstimateCreateDate(planInfo.getDrawingEstimateCreateDate());
|
||
pwrGnrSimRes.setCapacity(formatSpecification);
|
||
pwrGnrSimRes.setPrefName(planInfo.getPrefName());
|
||
pwrGnrSimRes.setAreaName(planInfo.getAreaName());
|
||
pwrGnrSimRes.setSnowfall(planInfo.getSnowfall());
|
||
pwrGnrSimRes.setStandardWindSpeedId(planInfo.getStandardWindSpeedId());
|
||
pwrGnrSimRes.setRoofModuleList(roofModuleList);
|
||
pwrGnrSimRes.setPcsList(pcsGroupList);
|
||
|
||
return pwrGnrSimRes;
|
||
}
|
||
|
||
/**
|
||
* 실제 계산로직
|
||
*
|
||
* @param pwrGnrSimReq
|
||
* @param roofLength
|
||
* @return
|
||
* @throws Exception
|
||
*/
|
||
public PwrGnrSimResponse calcResults(PwrGnrSimRequest pwrGnrSimReq, int roofLength) {
|
||
|
||
int[] days = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 각 월별 일수 선언
|
||
|
||
// 모듈 1에 대한 데이터
|
||
double module_system_loss = pwrGnrSimReq.getModuleTempLoss1(); // 모듈 온도 손실
|
||
double ondo_keisu = pwrGnrSimReq.getModuleTempCoeff1(); // 모듈 온도 계수
|
||
|
||
// 각 월별 온도 손실 계산
|
||
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 = pwrGnrSimReq.getPcsCnvEff1() / 100; // 변환 효율
|
||
double pcs1_youryou = pwrGnrSimReq.getPcsSpecification1(); // 용량 (예시 4.4)
|
||
double pcs2_henkankouritsu = pwrGnrSimReq.getPcsCnvEff2() / 100;
|
||
double pcs2_youryou = pwrGnrSimReq.getPcsSpecification2();
|
||
double pcs3_henkankouritsu = pwrGnrSimReq.getPcsCnvEff3() / 100;
|
||
double pcs3_youryou = pwrGnrSimReq.getPcsSpecification3();
|
||
|
||
// pcs1과 pcs2, pcs3의 실효 변환 효율 중 낮은 값을 사용
|
||
double[] values = {pcs1_henkankouritsu, pcs2_henkankouritsu, pcs3_henkankouritsu};
|
||
double pcs_henkankouritsu =
|
||
Arrays.stream(values)
|
||
.filter(value -> value > 0.0) // 0.0 제외
|
||
.min()
|
||
.orElse(0.0); // 모두 0.0인 경우 기본값 반환
|
||
|
||
// pcs 총 용량 계산
|
||
double pcs_youryou_total =
|
||
(pcs1_youryou * pwrGnrSimReq.getPcsInput1())
|
||
+ (pcs2_youryou * pwrGnrSimReq.getPcsInput2())
|
||
+ (pcs3_youryou * pwrGnrSimReq.getPcsInput3());
|
||
|
||
double module1_watt = pwrGnrSimReq.getModuleSpecification1(),
|
||
module1_watt_flash = module1_watt + 2;
|
||
double module2_watt = pwrGnrSimReq.getModuleSpecification2(),
|
||
module2_watt_flash = module2_watt + 2; // 모듈 1 출력 + 2w (플래시)
|
||
double module3_watt = pwrGnrSimReq.getModuleSpecification3(),
|
||
module3_watt_flash = module3_watt + 2;
|
||
|
||
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);
|
||
|
||
// 피크 컷 및 발전량 배열 선언
|
||
double[] hatsudenryou_all = new double[13]; // 피크 컷 없음 발전량(적설 고려 없음)
|
||
double[] hatsudenryou_all_snow = new double[13]; // 피크 컷 없음 발전량(적설 고려 있음)
|
||
double[] hatsudenryou_peakcut_all = new double[13]; // 피크 컷 있음 발전량(적설 고려 없음)
|
||
double[] hatsudenryou_peakcut_all_snow = new double[13]; // 피크 컷 있음 발전량(적설 고려 있음)
|
||
|
||
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.getModuleAmp1(),
|
||
pwrGnrSimReq.getPcsAmp1(),
|
||
pcs_henkankouritsu);
|
||
|
||
for (int j = 0; j < 12; j++) {
|
||
|
||
// 피크 컷 없음 발전량 계산
|
||
// shamen_nissha 조금 다름
|
||
hatsudenryou[i][j] =
|
||
shamen_nissha[j]
|
||
* days[j]
|
||
* ondo_sonshitsu[j] // 0.931
|
||
* pcs_henkankouritsu // 0.97
|
||
* ((module1_watt_flash * pwrGnrSimReq.getModuleInput1()[i])
|
||
+ (module2_watt_flash * pwrGnrSimReq.getModuleInput2()[i])
|
||
+ (module3_watt_flash * pwrGnrSimReq.getModuleInput3()[i]))
|
||
/ 1000
|
||
* module_system_loss // 0.99
|
||
* 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] += hatsudenryou[i][j]; // hatsudenryou[i][j]);
|
||
hatsudenryou_peakcut_all[j] += hatsudenryou_peakcut[i][j];
|
||
}
|
||
|
||
hatsudenryou_all_snow[j] =
|
||
(int)
|
||
Math.round(hatsudenryou_all[j] * (1 - (snow[(simulationPointNumber * 12) + j])));
|
||
hatsudenryou_peakcut_all_snow[j] =
|
||
(int)
|
||
Math.round(
|
||
hatsudenryou_peakcut_all[j] * (1 - (snow[(simulationPointNumber * 12) + j])));
|
||
|
||
// 계산 후 반올림 처리
|
||
hatsudenryou_all[j] = (int) Math.round(hatsudenryou_all[j]);
|
||
hatsudenryou_peakcut_all[j] = (int) Math.round(hatsudenryou_peakcut_all[j]);
|
||
}
|
||
}
|
||
|
||
// 마지막에 총합계 추가
|
||
hatsudenryou_all[12] = Arrays.stream(hatsudenryou_all).sum();
|
||
hatsudenryou_all_snow[12] = Arrays.stream(hatsudenryou_all_snow).sum();
|
||
hatsudenryou_peakcut_all[12] = Arrays.stream(hatsudenryou_peakcut_all).sum();
|
||
hatsudenryou_peakcut_all_snow[12] = Arrays.stream(hatsudenryou_peakcut_all_snow).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.setHatsudenryouAll(formatAndPrintArray(hatsudenryou_all));
|
||
pwrGnrSimRes.setHatsudenryouAllSnow(formatAndPrintArray(hatsudenryou_all_snow));
|
||
pwrGnrSimRes.setHatsudenryouPeakcutAll(formatAndPrintArray(hatsudenryou_peakcut_all));
|
||
pwrGnrSimRes.setHatsudenryouPeakcutAllSnow(formatAndPrintArray(hatsudenryou_peakcut_all_snow));
|
||
return pwrGnrSimRes;
|
||
}
|
||
|
||
// 배열 변환 및 포맷 처리 메서드
|
||
private static String[] formatAndPrintArray(double[] doubleArray) {
|
||
return Arrays.stream(doubleArray)
|
||
.mapToInt(d -> (int) d) // double -> int 변환
|
||
.mapToObj(i -> String.format("%,d", i))
|
||
.toArray(String[]::new); // String[]로 변환
|
||
}
|
||
|
||
// 설치면의 사면 일사량 계산
|
||
public static double[] peakcut(
|
||
int simulationPointNumber,
|
||
double koubai,
|
||
double houi,
|
||
double sekisairitsu,
|
||
double moduleAmp1,
|
||
double pcsAmp1,
|
||
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의 전류차 구하기 용도
|
||
boolean pcsAmpFound = true; // pcs_ampr 값은 항상있을거라 true
|
||
|
||
// pcs1과 모듈 1의 전류차(pcs의 입력 전류 데이터가 없는 경우는 0)
|
||
double amp_peakcut_result = 1; // 디폴트 : pcs1이 없거나 pcs1과모듈1dml wjsfbckrk 0 보다 작은경우
|
||
if (pcsAmpFound) {
|
||
|
||
// pcs1과 모듈 1의 전류차(pcs의 입력 전류 데이터가 없는 경우는 0)
|
||
double amp_delta = moduleAmp1 - pcsAmp1;
|
||
if (amp_delta >= 0) {
|
||
|
||
double amp_peakcut_about_deg0 =
|
||
computeAmpPeakCutDeg0(simulationPointNumber, koubai, amp_delta);
|
||
|
||
double amp_peakcut_about_deg90 =
|
||
computeAmpPeakCutDeg90(simulationPointNumber, 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 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 koubai, 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 {
|
||
|
||
String[] pwrGnrSimList = data.getFrcPwrGnrList();
|
||
|
||
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()) + " kW");
|
||
|
||
elm = doc.getElementById("anlFrcsGnrt");
|
||
elm.text(StringUtils.defaultString(String.valueOf(pwrGnrSimList[12])));
|
||
|
||
elm = doc.getElementById("snowfall");
|
||
elm.text(StringUtils.defaultString(data.getSnowfall()) + " cm");
|
||
|
||
elm = doc.getElementById("standardWindSpeedId");
|
||
elm.text(StringUtils.defaultString(data.getStandardWindSpeedId()));
|
||
|
||
boolean isUnchanged = Arrays.stream(pwrGnrSimList).allMatch(value -> value == "00");
|
||
if (!isUnchanged) { // 변경된값 확인
|
||
|
||
int[] onlyData =
|
||
Arrays.stream(Arrays.copyOfRange(pwrGnrSimList, 0, pwrGnrSimList.length - 1))
|
||
.map(s -> s.replace(",", "")) // 쉼표 제거
|
||
.mapToInt(Integer::parseInt) // 정수로 변환
|
||
.toArray();
|
||
|
||
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());
|
||
}
|
||
|
||
// 예측발전량
|
||
StringBuilder sb = new StringBuilder();
|
||
for (int i = 0; i < 13; i++) {
|
||
sb.append("<td>" + StringUtils.defaultString(pwrGnrSimList[i]) + "</td>");
|
||
}
|
||
|
||
elm = doc.getElementById("frcPwrGnrList_detail");
|
||
elm.append(sb.toString());
|
||
|
||
// 모듈 list
|
||
if (data.getRoofModuleList() != null && data.getRoofModuleList().size() > 0) {
|
||
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>" + StringUtils.defaultString(listItem.getSlopeAngleTxt()) + "</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) {
|
||
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());
|
||
}
|
||
|
||
elm = doc.getElementById("guideInfo");
|
||
elm.append(data.getGuideInfo());
|
||
|
||
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;
|
||
}
|
||
}
|