package org.openlca.io.xls.results; import java.io.File; import java.io.FileOutputStream; import java.util.List; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.streaming.SXSSFSheet; import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.openlca.core.database.EntityCache; import org.openlca.core.math.CalculationSetup; import org.openlca.core.matrix.FlowIndex; import org.openlca.core.model.descriptors.FlowDescriptor; import org.openlca.core.model.descriptors.ImpactCategoryDescriptor; import org.openlca.core.results.SimulationResultProvider; import org.openlca.core.results.SimulationStatistics; import org.openlca.io.xls.Excel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** Exports a simulation result to Excel. */ public class SimulationResultExport { private static final String[] FLOW_HEADER = { "Flow UUID", "Flow", "Category", "Sub-category", "Unit" }; private static final String[] IMPACT_HEADER = { "Impact category UUID", "Impact category", "Reference unit" }; private Logger log = LoggerFactory.getLogger(getClass()); private CalculationSetup setup; private SimulationResultProvider<?> result; private EntityCache cache; private int row = 0; private CellWriter writer; private boolean useStreaming = false; public SimulationResultExport(CalculationSetup setup, SimulationResultProvider<?> result) { this.setup = setup; this.result = result; this.cache = result.cache; } /** * Runs the result export. The given file should be an xlsx file. */ public void run(File file) throws Exception { Workbook workbook = createWorkbook(); writer = new CellWriter(cache, workbook); InfoSheet.write(workbook, writer, setup, null, "Simulation result"); writeInventorySheet(workbook); if (result.hasImpactResults()) writeImpactSheet(workbook); try (FileOutputStream fos = new FileOutputStream(file)) { workbook.write(fos); } log.trace("result written to file {}", file); } private Workbook createWorkbook() { useStreaming = result.getNumberOfRuns() > 150; log.trace("create workbook, using streaming: {}", useStreaming); if (useStreaming) return new SXSSFWorkbook(-1); else return new XSSFWorkbook(); } private void writeImpactSheet(Workbook workbook) { Sheet sheet = workbook.createSheet("Impact Assessment"); row = 0; writerImpactHeader(sheet); List<ImpactCategoryDescriptor> impacts = Sort.impacts(result.getImpactDescriptors()); for (ImpactCategoryDescriptor impact : impacts) { writer.impactRow(sheet, row, 1, impact); List<Double> values = result.getImpactResults(impact); writeValues(sheet, row, IMPACT_HEADER.length + 1, values); row++; } for (int i = 0; i < IMPACT_HEADER.length + 7; i++) sheet.autoSizeColumn(i); } private void writeInventorySheet(Workbook workbook) { Sheet sheet = workbook.createSheet("Inventory"); row = 0; List<FlowDescriptor> flows = Sort.flows(result.getFlowDescriptors(), cache); writeInventorySection(flows, true, sheet); writeInventorySection(flows, false, sheet); if (!useStreaming) { for (int i = 0; i < FLOW_HEADER.length + 7; i++) sheet.autoSizeColumn(i); } flushSheet(sheet); } private void flushSheet(Sheet sheet) { if (!useStreaming) return; if (!(sheet instanceof SXSSFSheet)) return; SXSSFSheet s = (SXSSFSheet) sheet; try { log.trace("flush rows of sheet {}", sheet.getSheetName()); s.flushRows(); } catch (Exception e) { log.error("failed to flush rows of streamed sheet", e); } } private void writeInventorySection(List<FlowDescriptor> flows, boolean forInputs, Sheet sheet) { writeInventoryHeader(sheet, forInputs); FlowIndex idx = result.result.flowIndex; for (FlowDescriptor flow : flows) { if (idx.isInput(flow.getId()) != forInputs) continue; writer.flowRow(sheet, row, 1, flow); List<Double> values = result.getFlowResults(flow); writeValues(sheet, row, FLOW_HEADER.length + 1, values); row++; } } private void writeInventoryHeader(Sheet sheet, boolean inputs) { row++; String section = inputs ? "Inputs" : "Outputs"; writer.cell(sheet, row, 1, section, true); row++; writer.headerRow(sheet, row, 1, FLOW_HEADER); int nextCol = FLOW_HEADER.length + 1; writeValueHeaders(sheet, row, nextCol); row++; } private void writerImpactHeader(Sheet sheet) { row++; writer.headerRow(sheet, row, 1, IMPACT_HEADER); int nextCol = IMPACT_HEADER.length + 1; writeValueHeaders(sheet, row, nextCol); row++; } private void writeValueHeaders(Sheet sheet, int row, int startCol) { String[] vals = { "Mean", "Standard deviation", "Minimum", "Maximum", "Median", "5% Percentile", "95% Percentile" }; for (int i = 0; i < vals.length; i++) writer.cell(sheet, row, startCol + i, vals[i], true); int nextCol = startCol + vals.length; for (int i = 0; i < result.getNumberOfRuns(); i++) writer.cell(sheet, row, nextCol++, "Run " + (i + 1), true); } private void writeValues(Sheet sheet, int row, int startCol, List<Double> values) { if (values == null) return; int col = startCol; SimulationStatistics stat = new SimulationStatistics(values, 100); Excel.cell(sheet, row, col++, stat.getMean()); Excel.cell(sheet, row, col++, stat.getStandardDeviation()); Excel.cell(sheet, row, col++, stat.getMinimum()); Excel.cell(sheet, row, col++, stat.getMaximum()); Excel.cell(sheet, row, col++, stat.getMedian()); Excel.cell(sheet, row, col++, stat.getPercentileValue(5)); Excel.cell(sheet, row, col++, stat.getPercentileValue(95)); for (int i = 0; i < values.size(); i++) Excel.cell(sheet, row, col++, values.get(i).doubleValue()); } }