package org.openlca.io.ilcd.input; import java.util.List; import java.util.Map; import org.openlca.core.model.Exchange; import org.openlca.core.model.FlowType; import org.openlca.core.model.Process; import org.openlca.ilcd.util.ProcessBag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Maps the reference flow of an ILCD process to an openLCA process. There can * be multiple-issues with the ILCD process that may needs to be solved to * create a valid openLCA process: * <ul> * <li>no reference flow provided: try to find a product on the output site, if * there is none, try to find a negative input product, change the sign and * direction and set it as reference flow. * <li>the reference flow is on the input site: move it to the output site * <li>there are multiple-reference flows: try to find the product with the * highest amount on the output site and take this as reference flow. * </ul> */ public class ProcessRefFlowMapper { private Logger log = LoggerFactory.getLogger(getClass()); private ProcessBag ilcdProcess; private Process olcaProcess; private Map<Integer, Exchange> map; /** * Initialise the mapper: idMap is the mapping of ILCD data set internal IDs * of exchanges to the respective openLCA exchanges in the process. */ public ProcessRefFlowMapper(ProcessBag ilcdProcess, Process olcaProcess, Map<Integer, Exchange> map) { this.ilcdProcess = ilcdProcess; this.olcaProcess = olcaProcess; this.map = map; } public void setReferenceFlow() { if (ilcdProcess == null || olcaProcess == null || map == null || map.isEmpty()) { log.warn("Null arguments or empty exchanges for process {} " + "in reference flow mapper, no reference flow set", olcaProcess); return; } List<Integer> refFlowIds = ilcdProcess.getReferenceFlowIds(); if (refFlowIds == null || refFlowIds.isEmpty()) handleNoReferenceFlow(); else setReferenceFlow(refFlowIds); } private void setReferenceFlow(List<Integer> refFlowIds) { Exchange candidate = null; for (Integer flowId : refFlowIds) { Exchange refExchange = map.get(flowId); if (refExchange == null) { log.warn("Reference flow ID {} in ILCD process {} does " + "not link to an exchange", candidate, ilcdProcess.getId()); continue; } if (betterMatch(refExchange, candidate)) candidate = refExchange; } handleCandidate(candidate); } private void handleCandidate(Exchange candidate) { if (candidate == null || candidate.getFlow() .getFlowType() == FlowType.ELEMENTARY_FLOW) handleNoReferenceFlow(); else { if (candidate.isInput()) { log.warn("Input found as reference flow in ILCD process {};" + " changed it to output", ilcdProcess.getId()); candidate.setInput(false); if (candidate.getAmountValue() < 0) switchSign(candidate); } olcaProcess.setQuantitativeReference(candidate); } } private void handleNoReferenceFlow() { String processId = ilcdProcess.getId(); log.warn("No reference flow found in ILCD process {}", processId); Exchange found = findBestOutput(); if (found == null) { found = findNegativeInputProduct(); if (found == null) { log.warn("No ref. flow found in ILCD process {}", processId); } else { switchSign(found); found.setInput(false); olcaProcess.setQuantitativeReference(found); } } else { log.info("Take best tech. output as ref. flow for ILCD process {}", processId); olcaProcess.setQuantitativeReference(found); } } private void switchSign(Exchange found) { log.info("Set a negative input product as quant. ref., " + "change sign, in process {}", ilcdProcess.getId()); double val = found.getAmountValue(); found.setAmountValue(Math.abs(val)); found.setAmountFormula(null); } private Exchange findBestOutput() { Exchange candidate = null; for (Exchange exchange : olcaProcess.getExchanges()) { if (exchange.isInput() || exchange.getFlow() .getFlowType() == FlowType.ELEMENTARY_FLOW) continue; if (betterMatch(exchange, candidate)) candidate = exchange; } return candidate; } private Exchange findNegativeInputProduct() { for (Exchange exchange : olcaProcess.getExchanges()) { if (exchange.isInput() && exchange.getFlow().getFlowType() == FlowType.PRODUCT_FLOW && exchange.getAmountValue() < 0) return exchange; } return null; } private boolean betterMatch(Exchange newCandidate, Exchange oldCandidate) { if (newCandidate == null) return false; if (oldCandidate == null) return true; if (!newCandidate.isInput() && oldCandidate.isInput()) return true; if (newCandidate.getFlow().getFlowType() == FlowType.PRODUCT_FLOW && oldCandidate.getFlow() .getFlowType() != FlowType.PRODUCT_FLOW) return true; if (newCandidate.getFlow().getFlowType() == FlowType.WASTE_FLOW && oldCandidate.getFlow() .getFlowType() != FlowType.ELEMENTARY_FLOW) return true; if (newCandidate.getAmountValue() > oldCandidate.getAmountValue()) return true; return false; } }