package org.openlca.io.ecospold2.input; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.openlca.core.database.BaseDao; import org.openlca.core.database.IDatabase; import org.openlca.core.database.ParameterDao; import org.openlca.core.database.ProcessDao; import org.openlca.core.model.Category; import org.openlca.core.model.DQSystem; import org.openlca.core.model.Exchange; import org.openlca.core.model.Flow; import org.openlca.core.model.FlowProperty; import org.openlca.core.model.Parameter; import org.openlca.core.model.ParameterScope; import org.openlca.core.model.Process; import org.openlca.core.model.ProcessType; import org.openlca.core.model.Unit; import org.openlca.core.model.UnitGroup; import org.openlca.io.ecospold2.UncertaintyConverter; import org.openlca.util.DQSystems; import org.openlca.util.KeyGen; import org.openlca.util.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Joiner; import spold2.Activity; import spold2.Classification; import spold2.DataSet; import spold2.ElementaryExchange; import spold2.IntermediateExchange; import spold2.PedigreeMatrix; import spold2.RichText; import spold2.Spold2; class ProcessImport { private final Logger log = LoggerFactory.getLogger(getClass()); private final IDatabase db; private final RefDataIndex index; private final ProcessDao dao; private final PriceMapper prices; private final ImportConfig config; private final DQSystem dqSystem; /** Exchanges that wait for a default provider: provider-id -> exchanges. */ private final HashMap<String, List<Exchange>> linkQueue = new HashMap<>(); public ProcessImport(IDatabase db, RefDataIndex index, ImportConfig config) { this.db = db; this.index = index; this.config = config; dao = new ProcessDao(db); prices = new PriceMapper(db); dqSystem = DQSystems.ecoinvent(db); } public void importDataSet(DataSet dataSet) { try { if (dataSet == null) { log.warn("not an EcoSpold data set"); return; } checkImport(dataSet); } catch (Exception e) { log.error("Failed to import EcoSpold 2 process", e); } } private void checkImport(DataSet dataSet) { if (!valid(dataSet)) { log.warn("invalid data set -> not imported"); return; } Activity activity = Spold2.getActivity(dataSet); try { String refId = RefId.forProcess(dataSet); boolean contains = dao.contains(refId); if (contains) { log.trace("process {} is already in the database", activity.id); return; } log.trace("import process {}", activity.name); runImport(dataSet, refId); } catch (Exception e) { log.error("Failed to import process", e); } } private boolean valid(DataSet ds) { Activity activity = Spold2.getActivity(ds); if (activity.id == null || activity.name == null) return false; IntermediateExchange refFlow = null; for (IntermediateExchange techFlow : Spold2.getProducts(ds)) { if (techFlow.outputGroup == null) continue; if (techFlow.outputGroup != 0) continue; if (techFlow.amount == 0) continue; refFlow = techFlow; break; } return refFlow != null; } private void runImport(DataSet dataSet, String refId) { Activity activity = Spold2.getActivity(dataSet); Process process = new Process(); process.setRefId(refId); setMetaData(activity, process); setCategory(dataSet, process); if (config.withParameters) handleParameters(dataSet, process); createProductExchanges(dataSet, process); if (process.getQuantitativeReference() == null) log.warn("could not set a quantitative reference for process {}", refId); createElementaryExchanges(dataSet, process); process.exchangeDqSystem = dqSystem; new DocImportMapper(db).map(dataSet, process); db.createDao(Process.class).insert(process); index.putProcessId(refId, process.getId()); flushLinkQueue(process); } private void handleParameters(DataSet dataSet, Process process) { List<Parameter> list = Parameters.fetch(dataSet, config); List<Parameter> newGlobals = new ArrayList<>(); for (Parameter p : list) { if (p.getScope() == ParameterScope.PROCESS) process.getParameters().add(p); else if (p.getScope() == ParameterScope.GLOBAL) newGlobals.add(p); } ParameterDao dao = new ParameterDao(db); Map<String, Boolean> map = new HashMap<>(); for (Parameter p : dao.getGlobalParameters()) map.put(p.getName(), Boolean.TRUE); for (Parameter newGlobal : newGlobals) { Boolean exists = map.get(newGlobal.getName()); if (exists == null) { dao.insert(newGlobal); map.put(newGlobal.getName(), Boolean.TRUE); } } } private void setMetaData(Activity a, Process p) { p.setName(a.name); ProcessType type = a.type == 2 ? ProcessType.LCI_RESULT : ProcessType.UNIT_PROCESS; p.setProcessType(type); String d = Joiner.on(" ").skipNulls().join( RichText.join(a.generalComment), a.includedActivitiesStart, a.includedActivitiesEnd, RichText.join(a.allocationComment)); p.setDescription(d); } private void flushLinkQueue(Process process) { List<Exchange> exchanges = linkQueue.remove(process.getRefId()); if (exchanges == null || process.getId() == 0) return; try { BaseDao<Exchange> dao = db.createDao(Exchange.class); for (Exchange exchange : exchanges) { exchange.setDefaultProviderId(process.getId()); dao.update(exchange); } } catch (Exception e) { log.error("failed to update default provider", e); } } private void createElementaryExchanges(DataSet ds, Process process) { for (ElementaryExchange e : Spold2.getElemFlows(ds)) { if (e.amount == 0 && config.skipNullExchanges) continue; String refId = e.flowId; Flow flow = index.getFlow(refId); if (flow == null) { log.warn("could not create flow for {}", e.flowId); } createExchange(e, refId, flow, process); } } private void createProductExchanges(DataSet ds, Process process) { for (IntermediateExchange ie : Spold2.getProducts(ds)) { boolean isRefFlow = ie.outputGroup != null && ie.outputGroup == 0; if (ie.amount == 0 && config.skipNullExchanges) continue; String refId = ie.flowId; Flow flow = index.getFlow(refId); if (flow == null) { log.warn("could not get flow for {}", refId); continue; } Exchange e = createExchange(ie, refId, flow, process); if (e == null) continue; if (isAvoidedProduct(refId, e)) e.setAvoidedProduct(true); if (ie.activityLinkId != null) addActivityLink(ie, e); if (isRefFlow) process.setQuantitativeReference(e); prices.map(ie, e); } } private boolean isAvoidedProduct(String refId, Exchange exchange) { return false; // If the sign of an product/waste input is different from the sign of // the product/waste output of the linked activity it could be an // avoided product. Not sure, if this is true for ecoinvent 3 // boolean isNeg = exchange.getAmountValue() < 0; // return isNeg != index.isNegativeFlow(refId) && exchange.isInput(); } private Exchange createExchange(spold2.Exchange es2, String flowRefId, Flow flow, Process process) { if (flow == null || flow.getReferenceFlowProperty() == null) return null; Exchange e = new Exchange(); e.setFlow(flow); e.setFlowPropertyFactor(flow.getReferenceFactor()); e.description = es2.comment; Unit unit = getFlowUnit(es2, flowRefId, flow); if (unit == null) return null; e.setUnit(unit); e.setInput(es2.inputGroup != null); double amount = es2.amount; double f = 1; if (index.isMappedFlow(flowRefId)) { f = index.getMappedFlowFactor(flowRefId); } e.setAmountValue(amount * f); e.setUncertainty(UncertaintyConverter.toOpenLCA(es2.uncertainty, f)); if (config.withParameters && config.withParameterFormulas) mapFormula(es2, process, e, f); e.setDqEntry(getPedigreeMatrix(es2)); process.getExchanges().add(e); return e; } private String getPedigreeMatrix(spold2.Exchange es2) { if (es2 == null || es2.uncertainty == null) return null; PedigreeMatrix pm = es2.uncertainty.pedigreeMatrix; if (pm == null) return null; return dqSystem.toString(pm.reliability, pm.completeness, pm.temporalCorrelation, pm.geographicalCorrelation, pm.technologyCorrelation); } private Unit getFlowUnit(spold2.Exchange original, String flowRefId, Flow flow) { if (!index.isMappedFlow(flowRefId)) return index.getUnit(original.unitId); FlowProperty refProp = flow.getReferenceFlowProperty(); if (refProp == null) return null; UnitGroup ug = refProp.getUnitGroup(); if (ug == null) return null; return ug.getReferenceUnit(); } private void mapFormula(spold2.Exchange original, Process process, Exchange exchange, double factor) { String formula = null; String var = original.variableName; if (Strings.notEmpty(var) && Parameters.contains(var, process.getParameters())) { formula = var; } else if (Parameters.isValid(original.mathematicalRelation, config)) { formula = original.mathematicalRelation; } if (formula == null) return; formula = formula.trim(); if (factor == 1.0) exchange.setAmountFormula(formula); else exchange.setAmountFormula(factor + " * (" + formula + ")"); } private void addActivityLink(IntermediateExchange e, Exchange exchange) { String providerId = e.activityLinkId; String flowId = e.flowId; String refId = KeyGen.get(providerId, flowId); Long processId = index.getProcessId(refId); if (processId != null) { exchange.setDefaultProviderId(processId); return; } List<Exchange> exchanges = linkQueue.get(refId); if (exchanges == null) { exchanges = new ArrayList<>(); linkQueue.put(refId, exchanges); } exchanges.add(exchange); } private void setCategory(DataSet ds, Process process) { Category category = null; for (Classification clazz : Spold2.getClassifications(ds)) { category = index.getProcessCategory(clazz.id); if (category != null) break; } process.setCategory(category); } }