All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openlca.io.xls.systems.SystemExport Maven / Gradle / Ivy

The newest version!
package org.openlca.io.xls.systems;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openlca.core.matrix.MatrixData;
import org.openlca.core.matrix.format.DenseMatrix;
import org.openlca.core.matrix.format.Matrix;
import org.openlca.core.matrix.format.MatrixReader;
import org.openlca.core.matrix.index.EnviIndex;
import org.openlca.core.matrix.index.ImpactIndex;
import org.openlca.core.matrix.index.TechIndex;
import org.openlca.core.model.AllocationMethod;
import org.openlca.core.model.CalculationSetup;
import org.openlca.core.model.descriptors.ImpactDescriptor;
import org.openlca.io.xls.Excel;
import org.openlca.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class SystemExport {

	private final Logger log = LoggerFactory.getLogger(getClass());

	private MatrixData data;
	private final SystemExportConfig conf;

	public SystemExport(SystemExportConfig config) {
		this.conf = config;
	}

	public void exportTo(File dir) throws IOException {
		var setup = CalculationSetup.of(conf.system)
			// TODO: .withParameters(conf.system.parameterRedefs)
			.withAllocation(conf.allocationMethod);
		// setup.impactMethod = conf.impactMethod;
		var techIndex = TechIndex.of(conf.database,  setup);
		data = MatrixData.of(conf.database, techIndex)
				.withSetup(setup)
				.build();

		File subDir = new File(dir, conf.system.name.trim());
		if (!subDir.exists())
			subDir.mkdirs();
		createElementaryWorkbook(subDir);
		createProductWorkbook(subDir);
		if (data.impactMatrix != null) {
			createImpactWorkbook(subDir);
		}
	}

	private void createElementaryWorkbook(File subDir) throws IOException {
		log.trace("create workbook with elementary flows");
		Workbook elementaryWorkbook = new XSSFWorkbook();
		createElementaryCoverSheet(elementaryWorkbook,
			conf.allocationMethod);
		createElementarySheet(elementaryWorkbook);
		writeToFile(elementaryWorkbook,
			new File(subDir, FILE_NAMES.ELEMENTARY));
	}

	private void createProductWorkbook(File subDir) throws IOException {
		log.trace("create workbook with product flows");
		Workbook productWorkbook = new XSSFWorkbook();
		createProductCoverSheet(productWorkbook, conf.allocationMethod);
		createProductSheet(productWorkbook);
		writeToFile(productWorkbook, new File(subDir, FILE_NAMES.PRODUCT));
	}

	private void createImpactWorkbook(File subDir) throws IOException {
		log.trace("create workbook with impact assessment factors");
		Workbook impactMethodWorkbook = new XSSFWorkbook();
		createImpactMethodCoverSheet(impactMethodWorkbook);
		createImpactMethodSheet(impactMethodWorkbook);
		writeToFile(impactMethodWorkbook, new File(subDir,
			FILE_NAMES.IMPACT_FACTORS));
	}

	private void createElementaryCoverSheet(Workbook workbook,
		AllocationMethod allocationMethod) {
		Sheet sheet = workbook.createSheet("General information");
		Excel.trackSize(sheet, 0, 1);
		boolean allocated = allocationMethod != null;
		String subTitle = allocated
			? TITLES.ELEMENTARY_ALLOCATED
			: TITLES.ELEMENTARY;
		int row = 0;
		row = writeHeaderInformation(sheet, row++, subTitle);
		row = writeSoftwareInformation(sheet, row++);

		String name = conf.system.name;
		int processes = conf.system.processes.size();
		int products = data.techIndex.size();
		int flows = data.enviIndex.size();
		String dimensions = flows + "x" + products;

		row = line(sheet, row, "Product system:", name);
		if (allocated)
			row = line(sheet, row, "Allocation method:",
				getMethodLabel(allocationMethod));
		row = line(sheet, row, "No. of processes:", processes);
		row = line(sheet, row, "No. of products:", products);
		row = line(sheet, row, "No. of elementary flows:", flows);
		row = line(sheet, row, "Matrix dimensions:", dimensions);
		Excel.autoSize(sheet, 0, 1);
	}

	private String getMethodLabel(AllocationMethod method) {
		if (method == null)
			return "None";
		switch (method) {
			case CAUSAL:
				return "Causal";
			case ECONOMIC:
				return "Economic";
			case NONE:
				return "None";
			case PHYSICAL:
				return "Physical";
			case USE_DEFAULT:
				return "As defined in processes";
			default:
				return "Unknown";
		}
	}

	private void createProductCoverSheet(Workbook workbook,
		AllocationMethod allocationMethod) {
		Sheet sheet = workbook.createSheet("General information");
		Excel.trackSize(sheet, 0, 1);
		boolean allocated = allocationMethod != null;
		String subTitle = allocated
			? TITLES.PRODUCT_ALLOCATED
			: TITLES.PRODUCT;

		int row = 0;
		row = writeHeaderInformation(sheet, row++, subTitle);
		row = writeSoftwareInformation(sheet, row++);

		String name = conf.system.name;
		int processes = conf.system.processes.size();
		int products = data.techIndex.size();
		String dimensions = products + "x" + products;

		row = line(sheet, row, "Product system:", name);
		if (allocated)
			row = line(sheet, row, "Allocation method:",
				getMethodLabel(allocationMethod));
		row = line(sheet, row, "No. of processes:", processes);
		row = line(sheet, row, "No. of products:", products);
		row = line(sheet, row, "Matrix dimensions:", dimensions);

		Excel.autoSize(sheet, 0, 1);
	}

	private void createImpactMethodCoverSheet(Workbook workbook) {
		Sheet sheet = workbook.createSheet("General information");
		Excel.trackSize(sheet, 0, 1);
		int row = 0;
		row = writeHeaderInformation(sheet, row, TITLES.IMPACT_FACTORS);
		row++;
		row = writeSoftwareInformation(sheet, row);
		row++;

		String name = conf.system.name;
		String method = conf.impactMethod.name;
		int categories = data.impactIndex.size();
		int factors = data.enviIndex.size();
		String dimensions = factors + "x" + categories;

		row = line(sheet, row, "Product system:", name);
		row = line(sheet, row, "Impact method:", method);
		row = line(sheet, row, "No. of impact categories:", categories);
		row = line(sheet, row, "No. of impact factors:", factors);
		row = line(sheet, row, "Matrix dimensions:", dimensions);

		Excel.autoSize(sheet, 0, 1);
	}

	private int writeHeaderInformation(Sheet sheet, int row, String title) {
		String date = DateFormat.getDateInstance().format(
			GregorianCalendar.getInstance().getTime());
		Excel.cell(sheet, row, 0, TITLES.MAIN_TITLE);
		row++;
		Excel.cell(sheet, row, 0, title);
		row++;
		row++;
		Excel.cell(sheet, row, 0, date);
		row++;
		return row;
	}

	private int writeSoftwareInformation(Sheet sheet, int row) {
		row = line(sheet, row, "Software:", "openLCA");
		row = line(sheet, row, "Version:", conf.olcaVersion);
		row = line(sheet, row, "Database:", conf.database.getName());
		return row;
	}

	private int line(Sheet sheet, int row, String label, String value) {
		Excel.cell(sheet, row, 0, label);
		Excel.cell(sheet, row, 1, value);
		return row + 1;
	}

	private int line(Sheet sheet, int row, String label, double value) {
		Excel.cell(sheet, row, 0, label);
		Excel.cell(sheet, row, 1, value);
		return row + 1;
	}

	private ExcelHeader createFlowHeader(EnviIndex index) {
		ExcelHeader header = new ExcelHeader();
		header.setHeaders(HEADERS.FLOW.VALUES);
		List entries = new ArrayList<>();
		List sortedFlows = mapFlowIndices(header, index);
		for (FlowInfo info : sortedFlows) {
			entries.add(new FlowHeaderEntry(info));
		}
		header.setEntries(entries.toArray(new IExcelHeaderEntry[0]));
		return header;
	}

	private ExcelHeader createProductHeader(TechIndex index) {
		ExcelHeader header = new ExcelHeader();
		header.setHeaders(HEADERS.PRODUCT.VALUES);
		List headerEntries = new ArrayList<>();
		List sortedProducts = mapProductIndices(header,
			index);
		for (ProductInfo product : sortedProducts) {
			headerEntries.add(new ProductHeaderEntry(product));
		}
		header.setEntries(headerEntries
			.toArray(new IExcelHeaderEntry[headerEntries.size()]));
		return header;
	}

	private ExcelHeader createImpactCategoryHeader(ImpactIndex impactIndex) {
		ExcelHeader header = new ExcelHeader();
		header.setHeaders(HEADERS.IMPACT_CATEGORY.VALUES);
		List headerEntries = new ArrayList<>();
		List sortedCategories = mapImpactCategoryIndices(
			header, impactIndex);
		for (ImpactDescriptor category : sortedCategories) {
			headerEntries.add(new ImpactCategoryHeaderEntry(
				conf.impactMethod.name, category));
		}
		header.setEntries(headerEntries
			.toArray(new IExcelHeaderEntry[headerEntries.size()]));
		return header;
	}

	private void createElementarySheet(Workbook workbook) {
		ExcelHeader columnHeader = createProductHeader(data.techIndex);
		ExcelHeader rowHeader = createFlowHeader(data.enviIndex);
		MatrixExcelExport export = new MatrixExcelExport();
		export.setColumnHeader(columnHeader);
		export.setRowHeader(rowHeader);
		export.setMatrix(data.enviMatrix);
		export.writeTo(workbook);
	}

	private void createProductSheet(Workbook workbook) {
		ExcelHeader columnHeader = createProductHeader(data.techIndex);
		ExcelHeader rowHeader = createProductHeader(data.techIndex);
		MatrixExcelExport export = new MatrixExcelExport();
		export.setColumnHeader(columnHeader);
		export.setRowHeader(rowHeader);
		export.setMatrix(data.techMatrix);
		Sheet sheet = export.writeTo(workbook);
		int columnOffSet = rowHeader.getHeaderSize() + 1;
		for (int i = 0; i < columnHeader.getHeaderSize(); i++) {
			Excel.bold(workbook, sheet, i, columnOffSet);
		}
	}

	private void createImpactMethodSheet(Workbook workbook) {
		ExcelHeader columnHeader = createImpactCategoryHeader(
			data.impactIndex);
		ExcelHeader rowHeader = createFlowHeader(data.enviIndex);
		MatrixExcelExport export = new MatrixExcelExport();
		export.setColumnHeader(columnHeader);
		export.setRowHeader(rowHeader);
		export.setMatrix(transpose(data.impactMatrix));
		export.writeTo(workbook);
	}

	private List mapFlowIndices(ExcelHeader header,
		EnviIndex flowIndex) {
		List sortedFlows = FlowInfo.getAll(conf, flowIndex);
		Collections.sort(sortedFlows);
		int counter = 0;
		for (FlowInfo flow : sortedFlows) {
			header.putIndexMapping(counter, flowIndex.of(flow.realId));
			counter++;
		}
		return sortedFlows;
	}

	private List mapProductIndices(ExcelHeader header,
		TechIndex index) {
		List products = ProductInfo.getAll(conf, index);
		Collections.sort(products);
		int i = 0;
		for (ProductInfo product : products) {
			header.putIndexMapping(i, index.of(product.provider));
			i++;
		}
		return products;
	}

	private List mapImpactCategoryIndices(
		ExcelHeader header, ImpactIndex impactIndex) {
		var sortedCategories = impactIndex.content()
			.stream()
			.sorted((i1, i2) -> Strings.compare(i1.name, i2.name))
			.collect(Collectors.toList());
		int counter = 0;
		for (ImpactDescriptor category : sortedCategories) {
			header.putIndexMapping(counter, impactIndex.of(category));
			counter++;
		}
		return sortedCategories;
	}

	private Matrix transpose(MatrixReader matrix) {
		var result = new DenseMatrix(matrix.columns(), matrix.rows());
		for (int row = 0; row < matrix.rows(); row++) {
			for (int column = 0; column < matrix.columns(); column++) {
				double value = matrix.get(row, column);
				result.set(column, row, value);
			}
		}
		return result;
	}

	private void writeToFile(Workbook workbook, File file) throws IOException {
		int i = 1;
		File actFile = new File(file.getAbsolutePath());
		while (actFile.exists()) {
			String tmp = file.getAbsolutePath();
			tmp = tmp.substring(0, tmp.lastIndexOf('.')) + "(" + i + ")"
				+ tmp.substring(tmp.lastIndexOf('.'));
			actFile = new File(tmp);
			i++;
		}
		actFile.createNewFile();
		log.trace("write file {}", actFile.getAbsolutePath());
		try (FileOutputStream fos = new FileOutputStream(actFile)) {
			workbook.write(fos);
		}
	}

	private interface TITLES {

		String MAIN_TITLE = "OpenLCA Life Cycle Assessment Matrix Export";

		String ELEMENTARY = "Elementary Flows Associated with Processes/Activities, no allocation applied";
		String ELEMENTARY_ALLOCATED = "Elementary Flows Associated with Processes/Activities, after allocation";
		String PRODUCT = "Use of products/services by processes/activities without allocation or co-product/avoided production credits";
		String PRODUCT_ALLOCATED = "Use of Products/Services by Processes/Activities with user-specified allocation or co-product/avoided production applied";
		String IMPACT_FACTORS = "Life Cycle Impact Assessment, Characterization Factors";

	}

	private interface FILE_NAMES {

		String ELEMENTARY = "ElementaryFlows.xlsx";
		String PRODUCT = "ProductFlows.xlsx";
		String IMPACT_FACTORS = "ImpactFactors.xlsx";

	}

	private interface HEADERS {

		interface FLOW {

			String CATEGORY = "Category";
			String LOCATION = "Elementary flow location";
			String NAME = "Elementary flowname";
			String SUB_CATEGORY = "Sub category";
			String UNIT = "Unit";
			String UUID = "UUID";

			String[] VALUES = new String[]{UUID, CATEGORY, SUB_CATEGORY,
				NAME, LOCATION, UNIT};

		}

		interface PRODUCT {

			String INFRASTRUCTURE_PRODUCT = "Infrastructure product";
			String MULTI_OUTPUT = "Multi-Output process";
			String PROCESS_CATEGORY = "Process category";
			String PROCESS_LOCATION = "Process location";
			String PROCESS_NAME = "Process name";
			String PROCESS_SUB_CATEGORY = "Process sub category";
			String PRODUCT_NAME = "Product name";
			String PRODUCT_UNIT = "Product/Service unit";
			String UUID = "UUID";

			String[] VALUES = new String[]{PROCESS_NAME, PRODUCT_NAME,
				MULTI_OUTPUT, UUID, INFRASTRUCTURE_PRODUCT,
				PROCESS_LOCATION, PROCESS_CATEGORY, PROCESS_SUB_CATEGORY,
				PRODUCT_UNIT};

		}

		interface IMPACT_CATEGORY {

			String CATEGORY = "Sub category";
			String METHOD = "Category";
			String UNIT = "Unit";
			String UUID = "UUID";

			String[] VALUES = new String[]{UUID, CATEGORY, METHOD, UNIT};

		}

	}

	private class FlowHeaderEntry implements IExcelHeaderEntry {

		private FlowInfo flowInfo;

		private FlowHeaderEntry(FlowInfo flowInfo) {
			this.flowInfo = flowInfo;
		}

		@Override
		public String getValue(int count) {
			if (count > HEADERS.FLOW.VALUES.length)
				return null;
			String header = HEADERS.FLOW.VALUES[count];
			return getValue(header);
		}

		private String getValue(String header) {
			switch (header) {
				case HEADERS.FLOW.NAME:
					return flowInfo.name;
				case HEADERS.FLOW.UUID:
					return flowInfo.id;
				case HEADERS.FLOW.LOCATION:
					return flowInfo.location;
				case HEADERS.FLOW.CATEGORY:
					return flowInfo.category;
				case HEADERS.FLOW.SUB_CATEGORY:
					return flowInfo.subCategory;
				case HEADERS.FLOW.UNIT:
					return flowInfo.unit;
			}
			return null;
		}

	}

	private class ProductHeaderEntry implements IExcelHeaderEntry {

		private ProductInfo productInfo;

		private ProductHeaderEntry(ProductInfo productInfo) {
			this.productInfo = productInfo;
		}

		@Override
		public String getValue(int count) {
			if (count > HEADERS.PRODUCT.VALUES.length)
				return null;
			String header = HEADERS.PRODUCT.VALUES[count];
			return getValue(header);
		}

		private String getValue(String header) {
			switch (header) {
				case HEADERS.PRODUCT.PROCESS_NAME:
					return productInfo.process;
				case HEADERS.PRODUCT.PRODUCT_NAME:
					return productInfo.product;
				case HEADERS.PRODUCT.MULTI_OUTPUT:
					return Boolean.toString(productInfo.fromMultiOutputProcess);
				case HEADERS.PRODUCT.UUID:
					return productInfo.productId;
				case HEADERS.PRODUCT.INFRASTRUCTURE_PRODUCT:
					return Boolean.toString(productInfo.fromInfrastructureProcess);
				case HEADERS.PRODUCT.PROCESS_LOCATION:
					return productInfo.processLocation;
				case HEADERS.PRODUCT.PROCESS_CATEGORY:
					return productInfo.processCategory;
				case HEADERS.PRODUCT.PROCESS_SUB_CATEGORY:
					return productInfo.processSubCategory;
				case HEADERS.PRODUCT.PRODUCT_UNIT:
					return productInfo.productUnit;
			}
			return null;
		}

	}

	private class ImpactCategoryHeaderEntry implements IExcelHeaderEntry {

		private ImpactDescriptor impactCategory;
		private String methodName;

		private ImpactCategoryHeaderEntry(String methodName,
			ImpactDescriptor impactCategory) {
			this.methodName = methodName;
			this.impactCategory = impactCategory;
		}

		@Override
		public String getValue(int count) {
			if (count > HEADERS.IMPACT_CATEGORY.VALUES.length)
				return null;
			String header = HEADERS.IMPACT_CATEGORY.VALUES[count];
			return getValue(header);
		}

		private String getValue(String header) {
			switch (header) {
				case HEADERS.IMPACT_CATEGORY.CATEGORY:
					return impactCategory.name;
				case HEADERS.IMPACT_CATEGORY.UUID:
					return impactCategory.refId;
				case HEADERS.IMPACT_CATEGORY.METHOD:
					return methodName;
				case HEADERS.IMPACT_CATEGORY.UNIT:
					return impactCategory.referenceUnit;
			}
			return null;
		}

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy