package org.openlca.io.ecospold2.input; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import org.openlca.core.database.IDatabase; import org.openlca.core.database.NativeSql; import org.openlca.core.database.NativeSql.QueryResultHandler; import org.openlca.core.database.ProcessDao; import org.openlca.core.model.Exchange; import org.openlca.core.model.Process; import org.openlca.core.model.descriptors.ProcessDescriptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MarketProcessCleanUp implements Runnable { private Logger log = LoggerFactory.getLogger(getClass()); private final ProcessDao dao; private final IDatabase database; public MarketProcessCleanUp(IDatabase database) { this.database = database; this.dao = new ProcessDao(database); } @Override public void run() { log.trace("run market process clean up"); try { List<ProcessDescriptor> marketProcesses = getMarketProcesses(); int i = 0; for (ProcessDescriptor descriptor : marketProcesses) { Process marketProcess = dao.getForId(descriptor.getId()); log.trace("replace market process {}", descriptor); marketProcess = fixSelfLoop(marketProcess); replaceMarket(marketProcess); i++; log.trace("finished {} of {}", i, marketProcesses.size()); } } catch (Exception e) { log.error("failed to remove market processes in supply chain", e); } } private List<ProcessDescriptor> getMarketProcesses() { List<ProcessDescriptor> descriptors = dao.getDescriptors(); List<ProcessDescriptor> marketProcesses = new ArrayList<>(); for (ProcessDescriptor descriptor : descriptors) { if (descriptor.getName().toLowerCase().startsWith("market for ")) marketProcesses.add(descriptor); } log.trace("{} market processes in {} processes found", marketProcesses.size(), descriptors.size()); return marketProcesses; } private Process fixSelfLoop(Process marketProcess) { Exchange qRef = marketProcess.getQuantitativeReference(); Exchange loopInput = null; for (Exchange input : marketProcess.getExchanges()) { if (!input.isInput()) continue; if (input.getDefaultProviderId() == marketProcess.getId()) { loopInput = input; break; } } if (loopInput == null) return marketProcess; qRef.setAmountValue(qRef.getAmountValue() - loopInput.getAmountValue()); marketProcess.getExchanges().remove(loopInput); log.trace("fixed self loop in {}", marketProcess); return dao.update(marketProcess); } private void replaceMarket(Process marketProcess) throws Exception { List<Long> usedIds = getWhereUsed(marketProcess); log.trace("replace {} in {} processes", marketProcess, usedIds.size()); for (long id : usedIds) { Process process = dao.getForId(id); log.trace("include market processes in {}", process); Exchange input = null; for (Exchange exchange : process.getExchanges()) { if (exchange.getDefaultProviderId() == marketProcess.getId()) { input = exchange; break; } } if (input == null) continue; List<Exchange> marketExchanges = getMarketExchanges(input, marketProcess); if (marketExchanges.isEmpty()) continue; process.getExchanges().addAll(marketExchanges); process.getExchanges().remove(input); mergeDuplicates(process); dao.update(process); } } private List<Long> getWhereUsed(Process marketProcess) throws Exception { String query = "select distinct f_owner from tbl_exchanges where " + "f_default_provider = " + marketProcess.getId(); final List<Long> list = new ArrayList<>(); NativeSql.on(database).query(query, new QueryResultHandler() { @Override public boolean nextResult(ResultSet result) throws SQLException { list.add(result.getLong(1)); return true; } }); return list; } private List<Exchange> getMarketExchanges(Exchange input, Process market) { if (!matches(market, input)) return Collections.emptyList(); Exchange marketRef = market.getQuantitativeReference(); double factor = input.getAmountValue() / marketRef.getAmountValue(); List<Exchange> exchanges = new ArrayList<>(); for (Exchange exchange : market.getExchanges()) { if (Objects.equals(exchange, marketRef)) continue; Exchange clone = exchange.clone(); clone.setAmountValue(exchange.getAmountValue() * factor); exchanges.add(clone); } return exchanges; } private boolean matches(Process market, Exchange input) { if (market == null || input == null || market.getQuantitativeReference() == null) return false; Exchange marketRef = market.getQuantitativeReference(); return input.isInput() && !marketRef.isInput() && input.getAmountValue() != 0 && marketRef.getAmountValue() != 0 && Objects.equals(marketRef.getFlow(), input.getFlow()) && Objects.equals(marketRef.getUnit(), input.getUnit()); } private void mergeDuplicates(Process process) { List<Exchange> exchanges = process.getExchanges(); List<Exchange> duplicates = new ArrayList<>(); for (int i = 0; i < exchanges.size(); i++) { Exchange first = process.getExchanges().get(i); if (Objects.equals(first, process.getQuantitativeReference())) continue; for (int j = i + 1; j < exchanges.size(); j++) { Exchange second = process.getExchanges().get(j); if (!isDuplicate(first, second)) continue; second.setAmountValue(first.getAmountValue() + second.getAmountValue()); second.setUncertainty(null); // TODO: combine values? duplicates.add(first); } } process.getExchanges().removeAll(duplicates); } private boolean isDuplicate(Exchange first, Exchange second) { return first.isInput() == second.isInput() && Objects.equals(first.getFlow(), second.getFlow()) && Objects.equals(first.getUnit(), second.getUnit()) && first.getDefaultProviderId() == second .getDefaultProviderId(); } }