package org.openlca.core.matrix;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
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;
import gnu.trove.impl.Constants;
import gnu.trove.map.hash.TLongDoubleHashMap;
class AllocationIndex {
private MatrixCache cache;
private TechIndex productIndex;
private AllocationMethod method;
/**
* Used for physical and economic allocation: directly stores the the
* allocation factors for the given process-products.
*/
private HashMap<LongPair, Double> productFactors;
/**
* Used for causal allocation: stores the relation process-product ->
* exchange -> allocation factor.
*/
private HashMap<LongPair, TLongDoubleHashMap> exchangeFactors;
public static AllocationIndex create(TechIndex productIndex,
AllocationMethod method, MatrixCache cache) {
return new AllocationIndex(productIndex, method, cache);
}
private AllocationIndex(TechIndex productIndex, AllocationMethod method,
MatrixCache cache) {
this.method = method;
this.productIndex = productIndex;
this.cache = cache;
List<CalcAllocationFactor> factors = loadFactors();
for (CalcAllocationFactor factor : factors)
index(factor);
}
private List<CalcAllocationFactor> loadFactors() {
try {
List<CalcAllocationFactor> factors = new ArrayList<>();
Map<Long, List<CalcAllocationFactor>> factorMap = cache
.getAllocationCache().getAll(productIndex.getProcessIds());
for (List<CalcAllocationFactor> list : factorMap.values())
factors.addAll(list);
return factors;
} catch (Exception e) {
Logger log = LoggerFactory.getLogger(getClass());
log.error("failed to load allocation factors from cache", e);
return Collections.emptyList();
}
}
private void index(CalcAllocationFactor factor) {
LongPair processProduct = new LongPair(factor.getProcessId(),
factor.getProductId());
AllocationMethod _method = this.method;
if (this.method == AllocationMethod.USE_DEFAULT)
_method = cache.getProcessTable().getDefaultAllocationMethod(
factor.getProcessId());
if (_method == null)
return;
switch (_method) {
case CAUSAL:
tryIndexCausal(processProduct, factor);
break;
case ECONOMIC:
tryIndexForProduct(processProduct, factor, _method);
break;
case PHYSICAL:
tryIndexForProduct(processProduct, factor, _method);
break;
default:
break;
}
}
private void tryIndexCausal(LongPair processProduct,
CalcAllocationFactor factor) {
if (factor.getMethod() != AllocationMethod.CAUSAL
|| factor.getExchangeId() == null)
return;
if (exchangeFactors == null)
exchangeFactors = new HashMap<>();
TLongDoubleHashMap map = exchangeFactors.get(processProduct);
if (map == null) {
// 1.0 is the default value -> means no allocation
map = new TLongDoubleHashMap(Constants.DEFAULT_CAPACITY,
Constants.DEFAULT_LOAD_FACTOR,
Constants.DEFAULT_LONG_NO_ENTRY_VALUE, 1d);
exchangeFactors.put(processProduct, map);
}
map.put(factor.getExchangeId(), factor.getValue());
}
private void tryIndexForProduct(LongPair processProduct,
CalcAllocationFactor factor, AllocationMethod method) {
if (factor.getMethod() != method)
return;
if (method != AllocationMethod.ECONOMIC
&& method != AllocationMethod.PHYSICAL)
return;
if (productFactors == null)
productFactors = new HashMap<>();
productFactors.put(processProduct, factor.getValue());
}
public double getFactor(LongPair processProduct,
CalcExchange calcExchange) {
if (!calcExchange.input
&& calcExchange.flowType == FlowType.PRODUCT_FLOW)
return 1d; // TODO: this changes when we allow input-modelling
// of
// waste-flows
AllocationMethod _method = this.method;
if (this.method == AllocationMethod.USE_DEFAULT)
_method = cache.getProcessTable().getDefaultAllocationMethod(
processProduct.getFirst());
if (_method == null)
return 1d;
switch (_method) {
case CAUSAL:
return fetchCausal(processProduct, calcExchange);
case ECONOMIC:
return fetchForProduct(processProduct);
case PHYSICAL:
return fetchForProduct(processProduct);
default:
return 1d;
}
}
private double fetchForProduct(LongPair processProduct) {
if (productFactors == null)
return 1d;
Double factor = productFactors.get(processProduct);
if (factor == null)
return 1d;
else
return factor;
}
private double fetchCausal(LongPair processProduct,
CalcExchange calcExchange) {
if (exchangeFactors == null)
return 1d;
TLongDoubleHashMap map = exchangeFactors.get(processProduct);
if (map == null)
return 1d;
return map.get(calcExchange.exchangeId); // default is 1.0
}
}