package org.openlca.io.simapro.csv.input;
import org.openlca.core.database.IDatabase;
import org.openlca.core.database.ProcessDao;
import org.openlca.core.model.AllocationFactor;
import org.openlca.core.model.AllocationMethod;
import org.openlca.core.model.Category;
import org.openlca.core.model.Exchange;
import org.openlca.core.model.Flow;
import org.openlca.core.model.FlowProperty;
import org.openlca.core.model.FlowPropertyFactor;
import org.openlca.core.model.FlowType;
import org.openlca.core.model.ModelType;
import org.openlca.core.model.Process;
import org.openlca.core.model.ProcessDocumentation;
import org.openlca.core.model.ProcessType;
import org.openlca.core.model.Uncertainty;
import org.openlca.core.model.UnitGroup;
import org.openlca.io.Categories;
import org.openlca.io.UnitMappingEntry;
import org.openlca.io.maps.MapFactor;
import org.openlca.simapro.csv.model.AbstractExchangeRow;
import org.openlca.simapro.csv.model.annotations.BlockHandler;
import org.openlca.simapro.csv.model.enums.ElementaryFlowType;
import org.openlca.simapro.csv.model.enums.ProductType;
import org.openlca.simapro.csv.model.process.ElementaryExchangeRow;
import org.openlca.simapro.csv.model.process.ProcessBlock;
import org.openlca.simapro.csv.model.process.ProductExchangeRow;
import org.openlca.simapro.csv.model.process.ProductOutputRow;
import org.openlca.simapro.csv.model.process.RefProductRow;
import org.openlca.util.KeyGen;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class ProcessHandler {
private Logger log = LoggerFactory.getLogger(getClass());
private IDatabase database;
private RefData refData;
private ProcessDao dao;
// currently mapped process and process block
private Process process;
private ProcessBlock block;
private ProcessParameterMapper parameterMapper;
public ProcessHandler(IDatabase database, RefData refData) {
this.database = database;
this.refData = refData;
this.dao = new ProcessDao(database);
}
@BlockHandler
public void handleProcess(ProcessBlock block) {
String refId = KeyGen.get(block.getIdentifier());
Process process = dao.getForRefId(refId);
if (process != null) {
log.warn("a process with the identifier {} is already in the "
+ "database and was not imported", refId);
}
log.trace("import process {}", refId);
process = new Process();
process.setRefId(refId);
process.setDefaultAllocationMethod(AllocationMethod.PHYSICAL);
process.setDocumentation(new ProcessDocumentation());
this.process = process;
this.block = block;
mapData();
try {
dao.insert(process);
} catch (Exception e) {
log.error("failed to insert process " + refId, e);
}
this.process = null;
}
private void mapData() {
mapName();
mapLocation();
mapCategory();
mapType();
new ProcessDocMapper(database, refData).map(block, process);
parameterMapper = new ProcessParameterMapper(database);
long scope = parameterMapper.map(block, process);
mapProductOutputs(scope);
mapProductInputs(scope);
mapElementaryFlows(scope);
mapAllocation();
}
private void mapName() {
if (block.getName() != null) {
process.setName(block.getName());
return;
}
Flow refFlow = getRefFlow();
if (refFlow != null) {
process.setName(refFlow.getName());
return;
}
process.setName(block.getIdentifier());
}
private void mapLocation() {
Flow refFlow = getRefFlow();
if (refFlow == null)
return;
process.setLocation(refFlow.getLocation());
}
private void mapAllocation() {
for (ProductOutputRow output : block.getProducts()) {
double value = output.getAllocation() / 100d;
long productId = refData.getProduct(output.getName()).getId();
addFactor(AllocationMethod.PHYSICAL, productId, value);
addFactor(AllocationMethod.ECONOMIC, productId, value);
for (Exchange e : process.getExchanges()) {
if (!isOutputProduct(e)) {
addCausalFactor(productId, e, value);
}
}
}
}
private boolean isOutputProduct(Exchange e) {
return e != null && e.getFlow() != null
&& !e.isInput() && !e.isAvoidedProduct()
&& e.getFlow().getFlowType() == FlowType.PRODUCT_FLOW;
}
private void addFactor(AllocationMethod method, long productId, double value) {
AllocationFactor f = new AllocationFactor();
f.setAllocationType(method);
f.setValue(value);
f.setProductId(productId);
process.getAllocationFactors().add(f);
}
private void addCausalFactor(long productId, Exchange e, double value) {
AllocationFactor f = new AllocationFactor();
f.setAllocationType(AllocationMethod.CAUSAL);
f.setValue(value);
f.setProductId(productId);
f.setExchange(e);
process.getAllocationFactors().add(f);
}
private Flow getRefFlow() {
if (!block.getProducts().isEmpty()) {
ProductOutputRow refRow = block.getProducts().get(0);
Flow flow = refData.getProduct(refRow.getName());
if (flow != null)
return flow;
}
if (block.getWasteTreatment() != null)
return refData.getProduct(block.getWasteTreatment().getName());
return null;
}
private void mapProductOutputs(long scope) {
boolean first = true;
for (ProductOutputRow row : block.getProducts()) {
Exchange e = createProductOutput(row, scope);
if (first && e != null) {
process.setQuantitativeReference(e);
first = false;
}
}
if (block.getWasteTreatment() != null) {
Exchange e = createProductOutput(block.getWasteTreatment(),
scope);
process.setQuantitativeReference(e);
}
}
private Exchange createProductOutput(RefProductRow row, long scope) {
Flow flow = refData.getProduct(row.getName());
Exchange e = initExchange(row, scope, flow);
if (e == null)
return null;
e.setInput(false);
setUnit(e, row.getUnit());
process.getExchanges().add(e);
return e;
}
private void mapProductInputs(long scope) {
for (ProductType type : ProductType.values()) {
for (ProductExchangeRow row : block.getProductExchanges(type)) {
Flow flow = refData.getProduct(row.getName());
Exchange e = initExchange(row, scope, flow);
if (e == null)
continue;
e.setInput(true);
e.setAvoidedProduct(type == ProductType.AVOIDED_PRODUCTS);
setUnit(e, row.getUnit());
process.getExchanges().add(e);
}
}
}
private void mapElementaryFlows(long scope) {
for (ElementaryFlowType type : ElementaryFlowType.values()) {
boolean isInput = type == ElementaryFlowType.RESOURCES;
for (ElementaryExchangeRow row : block
.getElementaryExchangeRows(type)) {
String key = KeyGen.get(row.getName(),
type.getExchangeHeader(), row.getSubCompartment(),
row.getUnit());
MapFactor<Flow> factor = refData.getMappedFlow(key);
Exchange e;
if (factor != null) {
e = initMappedExchange(factor, row, scope);
setRefUnit(e);
} else {
Flow flow = refData.getElemFlow(key);
e = initExchange(row, scope, flow);
setUnit(e, row.getUnit());
}
if (e == null)
continue;
e.setInput(isInput);
process.getExchanges().add(e);
}
}
}
private Exchange initMappedExchange(MapFactor<Flow> mappedFlow,
ElementaryExchangeRow row, long scope) {
Flow flow = mappedFlow.getEntity();
Exchange e = initExchange(row, scope, flow);
if (e == null)
return null;
double f = mappedFlow.getFactor();
e.setAmountValue(f * e.getAmountValue());
if (e.getAmountFormula() != null) {
String formula = f + " * ( " + e.getAmountFormula() + " )";
e.setAmountFormula(formula);
}
if (e.getUncertainty() != null) {
e.getUncertainty().scale(f);
}
return e;
}
private Exchange initExchange(AbstractExchangeRow row, long scopeId,
Flow flow) {
if (flow == null) {
log.error("could not create exchange as there was now flow found "
+ "for {}", row);
return null;
}
Exchange e = new Exchange();
e.setFlow(flow);
e.description = row.getComment();
setAmount(e, row.getAmount(), scopeId);
Uncertainty uncertainty = Uncertainties.get(e.getAmountValue(),
row.getUncertaintyDistribution());
e.setUncertainty(uncertainty);
return e;
}
/** Sets the exchange unit and flow property for the given SimaPro unit. */
private void setUnit(Exchange e, String unit) {
if (e == null || e.getFlow() == null)
return;
UnitMappingEntry entry = refData.getUnitMapping().getEntry(unit);
if (entry == null) {
log.error("unknown unit {}; could not set exchange unit", unit);
return;
}
Flow flow = e.getFlow();
e.setUnit(entry.unit);
FlowPropertyFactor factor = flow.getFactor(entry.flowProperty);
e.setFlowPropertyFactor(factor);
}
/**
* Sets the exchange unit and flow property from the flow reference data
* (used for mapped reference flows).
*/
private void setRefUnit(Exchange e) {
if (e == null || e.getFlow() == null)
return;
Flow f = e.getFlow();
FlowPropertyFactor fac = f.getReferenceFactor();
if (fac == null || fac.getFlowProperty() == null)
return;
e.setFlowPropertyFactor(fac);
FlowProperty prop = fac.getFlowProperty();
UnitGroup group = prop.getUnitGroup();
if (group == null || group.getReferenceUnit() == null)
return;
e.setUnit(group.getReferenceUnit());
}
private void setAmount(Exchange e, String amountText, long scope) {
try {
double val = Double.parseDouble(amountText);
e.setAmountValue(val);
} catch (Exception ex) {
double val = parameterMapper.eval(amountText, scope);
e.setAmountValue(val);
e.setAmountFormula(amountText);
}
}
private void mapCategory() {
String categoryPath = null;
if (!block.getProducts().isEmpty()) {
ProductOutputRow row = block.getProducts().get(0);
categoryPath = row.getCategory();
} else if (block.getWasteTreatment() != null)
categoryPath = block.getWasteTreatment().getCategory();
if (categoryPath == null)
return;
String[] path = categoryPath.split("\\\\");
Category category = Categories.findOrAdd(database, ModelType.PROCESS,
path);
process.setCategory(category);
}
private void mapType() {
org.openlca.simapro.csv.model.enums.ProcessType type = block
.getProcessType();
if (type == org.openlca.simapro.csv.model.enums.ProcessType.SYSTEM)
process.setProcessType(ProcessType.LCI_RESULT);
else
process.setProcessType(ProcessType.UNIT_PROCESS);
}
}