package org.openlca.app.editors.systems; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Queue; import org.openlca.core.database.EntityCache; import org.openlca.core.matrix.LongPair; import org.openlca.core.model.ProcessLink; import org.openlca.core.model.ProductSystem; import org.openlca.core.model.descriptors.Descriptors; import org.openlca.core.model.descriptors.ProcessDescriptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.gson.Gson; class Statistics { @SuppressWarnings("unused") private int processCount; @SuppressWarnings("unused") private int linkCount; @SuppressWarnings("unused") private int techMatrixSize; @SuppressWarnings("unused") private boolean connectedGraph; @SuppressWarnings("unused") private ProcessDescriptor refProcess; @SuppressWarnings("unused") private List<LinkValue> topInDegrees; @SuppressWarnings("unused") private List<LinkValue> topOutDegrees; private Statistics() { } public static Statistics calculate(ProductSystem system, EntityCache cache) { Statistics statistics = new Statistics(); try { statistics.doCalc(system, cache); } catch (Exception e) { Logger log = LoggerFactory.getLogger(Statistics.class); log.error("failed to calculate product system statistics for " + system, e); } return statistics; } private void doCalc(ProductSystem system, EntityCache cache) { processCount = system.getProcesses().size(); linkCount = system.getProcessLinks().size(); refProcess = Descriptors.toDescriptor(system.getReferenceProcess()); HashSet<LongPair> processProducts = new HashSet<>(); Multimap<Long, Long> inEdges = HashMultimap.create(); Multimap<Long, Long> outEdges = HashMultimap.create(); for (ProcessLink link : system.getProcessLinks()) { processProducts.add(LongPair.of(link.providerId, link.flowId)); inEdges.put(link.processId, link.providerId); outEdges.put(link.providerId, link.processId); } techMatrixSize = processProducts.size(); connectedGraph = isConnectedGraph(system, inEdges); topInDegrees = calculateMostLinked(inEdges, 5, cache); topOutDegrees = calculateMostLinked(outEdges, 5, cache); } /** * The product system graph is connected if we can visit every process in * the product system traversing the graph starting from the reference * process and following the incoming process links. */ private static boolean isConnectedGraph(ProductSystem system, Multimap<Long, Long> inEdges) { if (system.getReferenceProcess() == null) return false; HashMap<Long, Boolean> visited = new HashMap<>(); Queue<Long> queue = new ArrayDeque<>(); queue.add(system.getReferenceProcess().getId()); while (!queue.isEmpty()) { Long recipient = queue.poll(); visited.put(recipient, Boolean.TRUE); for (Long provider : inEdges.get(recipient)) { Boolean state = visited.get(provider); if (!Objects.equals(state, Boolean.TRUE) && !queue.contains(provider)) queue.add(provider); } } for (Long processId : system.getProcesses()) { Boolean val = visited.get(processId); if (!Objects.equals(val, Boolean.TRUE)) return false; } return true; } private static List<LinkValue> calculateMostLinked( Multimap<Long, Long> edges, int maxSize, EntityCache cache) { Long[] keys = new Long[maxSize]; int[] degrees = new int[maxSize]; for (Long id : edges.keySet()) { int degree = edges.get(id).size(); if (degree == 0) continue; Long key = id; for (int i = 0; i < maxSize; i++) { if (degree <= degrees[i]) continue; if (keys[i] == null) { keys[i] = key; degrees[i] = degree; break; } else { Long swapKey = keys[i]; int swapDegree = degrees[i]; keys[i] = key; degrees[i] = degree; key = swapKey; degree = swapDegree; } } } return createLinkValues(keys, degrees, cache); } private static List<LinkValue> createLinkValues(Long[] keys, int[] degrees, EntityCache cache) { List<LinkValue> linkValues = new ArrayList<>(); for (int i = 0; i < keys.length; i++) { Long key = keys[i]; if (key == null) break; ProcessDescriptor process = cache.get(ProcessDescriptor.class, key); LinkValue value = new LinkValue(); value.process = process; value.degree = degrees[i]; linkValues.add(value); } return linkValues; } public String toJson() { return new Gson().toJson(this); } private static class LinkValue { @SuppressWarnings("unused") int degree; @SuppressWarnings("unused") ProcessDescriptor process; } }