package org.openlca.core.matrix; import java.util.List; import java.util.Map; import org.openlca.core.matrix.cache.MatrixCache; import org.openlca.core.model.AllocationMethod; import org.openlca.core.model.FlowType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class InventoryBuilder { private final MatrixCache cache; private final TechIndex techIndex; private final AllocationMethod allocationMethod; private FlowIndex flowIndex; private AllocationIndex allocationTable; private ExchangeMatrix technologyMatrix; private ExchangeMatrix interventionMatrix; InventoryBuilder(MatrixCache matrixCache, TechIndex productIndex, AllocationMethod allocationMethod) { this.cache = matrixCache; this.techIndex = productIndex; this.allocationMethod = allocationMethod; } Inventory build() { if (allocationMethod != null && allocationMethod != AllocationMethod.NONE) allocationTable = AllocationIndex.create(techIndex, allocationMethod, cache); flowIndex = FlowIndex.build(cache, techIndex, allocationMethod); technologyMatrix = new ExchangeMatrix(techIndex.size(), techIndex.size()); interventionMatrix = new ExchangeMatrix(flowIndex.size(), techIndex.size()); return createInventory(); } private Inventory createInventory() { Inventory inventory = new Inventory(); inventory.allocationMethod = allocationMethod; inventory.flowIndex = flowIndex; inventory.interventionMatrix = interventionMatrix; inventory.productIndex = techIndex; inventory.technologyMatrix = technologyMatrix; fillMatrices(); return inventory; } private void fillMatrices() { try { Map<Long, List<CalcExchange>> map = cache.getExchangeCache() .getAll(techIndex.getProcessIds()); for (Long processId : techIndex.getProcessIds()) { List<CalcExchange> exchanges = map.get(processId); List<LongPair> processProducts = techIndex .getProviders(processId); for (LongPair processProduct : processProducts) { for (CalcExchange exchange : exchanges) { putExchangeValue(processProduct, exchange); } } } } catch (Exception e) { Logger log = LoggerFactory.getLogger(getClass()); log.error("failed to load exchanges from cache", e); } } private void putExchangeValue(LongPair processProduct, CalcExchange e) { if (!e.input && processProduct.equals(e.processId, e.flowId)) { // the reference product int idx = techIndex.getIndex(processProduct); add(idx, processProduct, technologyMatrix, e); } else if (e.flowType == FlowType.ELEMENTARY_FLOW) { // elementary exchanges addIntervention(processProduct, e); } else if (e.input) { if (techIndex.isLinked(LongPair.of(e.processId, e.exchangeId))) { // linked product inputs addProcessLink(processProduct, e); } else { // an unlinked product input addIntervention(processProduct, e); } } else if (allocationMethod == null || allocationMethod == AllocationMethod.NONE) { // non allocated output products addIntervention(processProduct, e); } } private void addProcessLink(LongPair processProduct, CalcExchange e) { LongPair exchange = LongPair.of(e.processId, e.exchangeId); LongPair provider = techIndex.getLinkedProvider(exchange); int row = techIndex.getIndex(provider); add(row, processProduct, technologyMatrix, e); } private void addIntervention(LongPair processProduct, CalcExchange e) { int row = flowIndex.getIndex(e.flowId); add(row, processProduct, interventionMatrix, e); } private void add(int row, LongPair processProduct, ExchangeMatrix matrix, CalcExchange exchange) { int col = techIndex.getIndex(processProduct); if (row < 0 || col < 0) return; ExchangeCell existingCell = matrix.getEntry(row, col); if (existingCell != null) { // self loops or double entries exchange = mergeExchanges(existingCell, exchange); } ExchangeCell cell = new ExchangeCell(exchange); if (allocationTable != null) { // note that the allocation table assures that the factor is 1.0 for // reference products double factor = allocationTable.getFactor(processProduct, exchange); cell.allocationFactor = factor; } matrix.setEntry(row, col, cell); } private CalcExchange mergeExchanges(ExchangeCell existingCell, CalcExchange addExchange) { // a possible allocation factor is handled outside of this function CalcExchange exExchange = existingCell.exchange; double existingVal = getMergeValue(exExchange); double addVal = getMergeValue(addExchange); double val = existingVal + addVal; CalcExchange newExchange = new CalcExchange(); newExchange.input = val < 0; newExchange.conversionFactor = 1; newExchange.flowId = addExchange.flowId; newExchange.flowType = addExchange.flowType; newExchange.processId = addExchange.processId; newExchange.amount = Math.abs(val); if (exExchange.amountFormula != null && addExchange.amountFormula != null) { newExchange.amountFormula = "abs( " + getMergeFormula(exExchange) + " + " + getMergeFormula(addExchange) + ")"; } newExchange.costValue = getMergeCosts(exExchange, addExchange); // TODO: adding up uncertainty information (with formulas!) is not yet // handled return newExchange; } private double getMergeValue(CalcExchange e) { double v = e.amount * e.conversionFactor; if (e.input && !e.avoidedProduct) return -v; else return v; } private String getMergeFormula(CalcExchange e) { String f; if (e.amountFormula == null) f = Double.toString(e.amount); else f = "(" + e.amountFormula + ")"; if (e.conversionFactor != 1) f += " * " + e.conversionFactor; if (e.input && !e.avoidedProduct) f = "( -1 * (" + f + "))"; return f; } private double getMergeCosts(CalcExchange e1, CalcExchange e2) { if (e1.costValue == 0) return e2.costValue; if (e2.costValue == 0) return e1.costValue; // TODO: this would be rarely the case but if the same flow in a single // process is given in different currencies with different conversion // the following would be not correct. double v1 = e1.input ? e1.costValue : -e1.costValue; double v2 = e2.input ? e2.costValue : -e2.costValue; // TODO: cost formulas return Math.abs(v1 + v2); } }