package org.openlca.core.matrix;
import gnu.trove.iterator.TLongObjectIterator;
import gnu.trove.map.hash.TLongObjectHashMap;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.openlca.core.database.IDatabase;
import org.openlca.core.math.NumberGenerator;
import org.openlca.core.model.ParameterRedef;
import org.openlca.core.model.ParameterScope;
import org.openlca.core.model.Uncertainty;
import org.openlca.core.model.UncertaintyType;
import org.openlca.expressions.FormulaInterpreter;
import org.openlca.expressions.InterpreterException;
import org.openlca.expressions.Scope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Contains the parameters of a set of processes or impact assessment methods
* that can be bound to a formula interpreter which then can be used in
* calculations.
*/
public class ParameterTable {
/**
* Maps: parameter scope -> parameter name -> parameter cell. The global
* scope is indicated by an owner value of 0.
*/
private TLongObjectHashMap<Map<String, ParameterCell>> entries;
/**
* Builds a new parameter table. The table contains all global parameters
* from the given database and the parameters of the processes and LCIA
* methods with the given IDs.
*/
public static ParameterTable build(IDatabase database, Set<Long> contexts) {
return new ParameterTableBuilder().build(database, contexts);
}
ParameterTable() {
entries = new TLongObjectHashMap<>();
}
/**
* Creates a new formula interpreter for the parameter values in this table.
*/
public FormulaInterpreter createInterpreter() {
FormulaInterpreter interpreter = new FormulaInterpreter();
TLongObjectIterator<Map<String, ParameterCell>> it = entries.iterator();
while (it.hasNext()) {
it.advance();
Map<String, ParameterCell> map = it.value();
for (ParameterCell cell : map.values())
cell.bindTo(interpreter);
}
return interpreter;
}
/**
* Calculates new random values for the parameters in this table that have
* an uncertainty distribution assigned. The method creates a formula
* interpreter that is used for the evaluation of the uncertainty parameters
* and returned with the new values bound. Thus, the returned interpreter
* can be used in calculations.
*/
public FormulaInterpreter simulate() {
FormulaInterpreter interpreter = createInterpreter();
TLongObjectIterator<Map<String, ParameterCell>> it = entries.iterator();
while (it.hasNext()) {
it.advance();
Map<String, ParameterCell> map = it.value();
for (ParameterCell cell : map.values()) {
cell.eval(interpreter);
cell.simulate();
cell.bindTo(interpreter);
}
}
return interpreter;
}
/**
* Applies the given parameter redefinitions to this table. The respective
* parameter values in this table are overwritten by these redefinitions.
*/
public void apply(Collection<ParameterRedef> redefs) {
for (ParameterRedef redef : redefs) {
Long owner = redef.getContextId();
long scope = owner == null ? 0 : owner;
Map<String, ParameterCell> map = entries.get(scope);
if (map == null)
continue;
ParameterCell cell = map.get(redef.getName());
if (cell == null)
continue;
redefine(cell, redef);
}
}
private void redefine(ParameterCell cell, ParameterRedef redef) {
cell.generator = null;
CalcParameter param = cell.param;
param.setValue(redef.getValue());
param.setFormula(null); // it is important to delete the formula!
if (redef.getUncertainty() == null) {
param.setUncertaintyType(null);
param.setParameter1(0);
param.setParameter2(0);
param.setParameter3(0);
param.setParameter1Formula(null);
param.setParameter2Formula(null);
param.setParameter3Formula(null);
} else {
Uncertainty uncertainty = redef.getUncertainty();
param.setUncertaintyType(uncertainty.getDistributionType());
param.setParameter1(val(uncertainty.getParameter1Value()));
param.setParameter2(val(uncertainty.getParameter2Value()));
param.setParameter3(val(uncertainty.getParameter3Value()));
param.setParameter1Formula(uncertainty.getParameter1Formula());
param.setParameter2Formula(uncertainty.getParameter2Formula());
param.setParameter3Formula(uncertainty.getParameter3Formula());
}
}
private double val(Double d) {
return d == null ? 0 : d;
}
void put(CalcParameter param) {
Map<String, ParameterCell> map = entries.get(param.getOwner());
if (map == null) {
map = new HashMap<>();
entries.put(param.getOwner(), map);
}
ParameterCell cell = new ParameterCell(param);
map.put(param.getName(), cell);
}
private class ParameterCell {
private CalcParameter param;
private NumberGenerator generator;
ParameterCell(CalcParameter param) {
this.param = param;
}
private String getInterpreterValue() {
if (param.isInputParameter())
return Double.toString(param.getValue());
if (param.getFormula() != null && !param.getFormula().isEmpty())
return param.getFormula();
else
return Double.toString(param.getValue());
}
private void simulate() {
UncertaintyType type = param.getUncertaintyType();
if (type == null || type == UncertaintyType.NONE)
return;
if (generator == null)
generator = createGenerator(type);
param.setValue(generator.next());
}
private NumberGenerator createGenerator(UncertaintyType type) {
final CalcParameter p = param;
switch (type) {
case LOG_NORMAL:
return NumberGenerator.logNormal(p.getParameter1(),
p.getParameter2());
case NORMAL:
return NumberGenerator.normal(p.getParameter1(),
p.getParameter2());
case TRIANGLE:
return NumberGenerator.triangular(p.getParameter1(),
p.getParameter2(), p.getParameter3());
case UNIFORM:
return NumberGenerator.uniform(p.getParameter1(),
p.getParameter2());
default:
return NumberGenerator.discrete(p.getValue());
}
}
private void bindTo(FormulaInterpreter interpreter) {
Scope scope = findScope(interpreter);
scope.bind(param.getName(), getInterpreterValue());
}
private Scope findScope(FormulaInterpreter interpreter) {
if (param.getScope() == ParameterScope.GLOBAL)
return interpreter.getGlobalScope();
Scope scope = interpreter.getScope(param.getOwner());
if (scope == null)
scope = interpreter.createScope(param.getOwner());
return scope;
}
void eval(FormulaInterpreter interpreter) {
if (interpreter == null)
return;
try {
Scope scope = findScope(interpreter);
tryEval(scope);
} catch (Exception e) {
Logger log = LoggerFactory.getLogger(getClass());
log.error(
"Formula evaluation failed; parameter: "
+ param.getName(), e);
}
}
private void tryEval(Scope scope) throws InterpreterException {
if (param.getParameter1Formula() != null) {
double v = scope.eval(param.getParameter1Formula());
param.setParameter1(v);
}
if (param.getParameter2Formula() != null) {
double v = scope.eval(param.getParameter2Formula());
param.setParameter2(v);
}
if (param.getParameter3Formula() != null) {
double v = scope.eval(param.getParameter3Formula());
param.setParameter3(v);
}
}
}
}