package org.openlca.io.ecospold2.input;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.openlca.core.database.BaseEntityDao;
import org.openlca.core.database.CategoryDao;
import org.openlca.core.database.FlowDao;
import org.openlca.core.database.FlowPropertyDao;
import org.openlca.core.database.IDatabase;
import org.openlca.core.model.Category;
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.Location;
import org.openlca.core.model.ModelType;
import org.openlca.core.model.Unit;
import org.openlca.io.Categories;
import org.openlca.io.maps.FlowMap;
import org.openlca.io.maps.FlowMapEntry;
import org.openlca.io.maps.MapType;
import org.openlca.util.KeyGen;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spold2.Classification;
import spold2.Compartment;
import spold2.DataSet;
import spold2.ElementaryExchange;
import spold2.Exchange;
import spold2.Geography;
import spold2.IntermediateExchange;
import spold2.Spold2;
/**
* Imports the reference data from a set of EcoSpold 02 files. During the import
* it creates an index that then can be used in a real process import.
*/
class RefDataImport {
private Logger log = LoggerFactory.getLogger(getClass());
private final ImportConfig config;
private IDatabase database;
private CategoryDao categoryDao;
private FlowDao flowDao;
private BaseEntityDao<Location> locationDao;
private RefDataIndex index;
private FlowMap flowMap;
public RefDataImport(IDatabase database, ImportConfig config) {
this.config = config;
this.database = database;
this.index = new RefDataIndex();
this.categoryDao = new CategoryDao(database);
this.locationDao = new BaseEntityDao<>(Location.class, database);
this.flowDao = new FlowDao(database);
this.flowMap = new FlowMap(MapType.ES2_FLOW);
try {
loadUnitMaps(database);
} catch (Exception e) {
log.error("failed to load unit map", e);
}
}
public RefDataIndex getIndex() {
return index;
}
private void loadUnitMaps(IDatabase database) throws Exception {
InputStream is = getClass().getResourceAsStream("ei3_unit_map.csv");
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = null;
while ((line = reader.readLine()) != null) {
String[] args = line.split(",");
String eiUnitKey = args[0];
BaseEntityDao<Unit> unitDao = new BaseEntityDao<>(Unit.class,
database);
Unit unit = unitDao.getForRefId(args[1]);
FlowPropertyDao propDao = new FlowPropertyDao(database);
FlowProperty prop = propDao.getForRefId(args[2]);
if (unit == null || prop == null)
log.warn("no unit or property found for {} in database, "
+ "no reference data?", eiUnitKey);
else {
index.putUnit(eiUnitKey, unit);
index.putFlowProperty(eiUnitKey, prop);
}
}
}
public void importDataSet(DataSet ds) {
if (ds == null)
return;
try {
classification(ds);
geography(ds);
for (IntermediateExchange e : Spold2.getProducts(ds)) {
if (e.amount == 0 && config.skipNullExchanges)
continue;
productFlow(ds, e);
}
for (ElementaryExchange e : Spold2.getElemFlows(ds)) {
elementaryFlow(e);
}
} catch (Exception e) {
log.error("failed to import reference data from data set", e);
}
}
private void classification(DataSet dataSet) {
Classification classification = findClassification(dataSet);
if (classification == null || classification.id == null)
return;
String refId = classification.id;
Category category = index.getProcessCategory(refId);
if (category != null)
return;
category = categoryDao.getForRefId(refId);
if (category == null) {
category = new Category();
category.setDescription(classification.system);
category.setModelType(ModelType.PROCESS);
category.setName(classification.value);
category.setRefId(refId);
category = categoryDao.insert(category);
}
index.putProcessCategory(refId, category);
}
private Classification findClassification(DataSet ds) {
for (Classification c : Spold2.getClassifications(ds)) {
if (c.system == null)
continue;
if (c.system.startsWith("ISIC"))
return c;
}
return null;
}
private void geography(DataSet ds) {
Geography geography = Spold2.getGeography(ds);
if (geography == null || geography.id == null
|| geography.shortName == null)
return;
String refId = geography.id;
Location location = index.getLocation(refId);
if (location != null)
return;
String genKey = KeyGen.get(geography.shortName);
location = locationDao.getForRefId(genKey);
if (location == null) {
location = new Location();
location.setCode(geography.shortName);
location.setName(geography.shortName);
location.setDescription("imported via EcoSpold 02 import");
location.setRefId(genKey);
location = locationDao.insert(location);
}
index.putLocation(refId, location);
}
private void compartment(Compartment compartment) {
if (compartment == null || compartment.id == null
|| compartment.subCompartment == null
|| compartment.compartment == null)
return;
String refId = compartment.id;
Category category = index.getCompartment(refId);
if (category != null)
return;
category = categoryDao.getForRefId(refId);
if (category == null) {
Category parent = Categories.findOrCreateRoot(database,
ModelType.FLOW, compartment.compartment);
category = Categories.findOrAddChild(database, parent,
compartment.subCompartment);
}
index.putCompartment(refId, category);
}
private void productFlow(DataSet dataSet, IntermediateExchange exchange) {
String refId = exchange.flowId;
Flow flow = index.getFlow(refId);
if (flow == null) {
flow = flowDao.getForRefId(refId);
if (flow != null)
index.putFlow(refId, flow);
}
if (flow == null)
flow = createNewProduct(exchange, refId);
Integer og = exchange.outputGroup;
boolean isRef = og != null && og == 0;
if (!isRef)
return;
index.putNegativeFlow(refId, exchange.amount < 0);
Category category = getProductCategory(dataSet, exchange);
flow.setCategory(category);
flow = flowDao.update(flow);
index.putFlow(refId, flow);
}
private Flow createNewProduct(IntermediateExchange exchange, String refId) {
Flow flow;
flow = new Flow();
flow.setRefId(refId);
flow.setDescription("EcoSpold 2 intermediate exchange, ID = "
+ exchange.flowId);
// in ecoinvent 3 negative values indicate waste flows
// see also the exchange handling in the process input
// to be on the save side, we declare all intermediate flows as
// products
// FlowType type = exchange.getAmount() < 0 ? FlowType.WASTE_FLOW
// : FlowType.PRODUCT_FLOW;
flow.setFlowType(FlowType.PRODUCT_FLOW);
createFlow(exchange, flow);
return flow;
}
private void elementaryFlow(ElementaryExchange exchange) {
String refId = exchange.flowId;
Flow flow = index.getFlow(refId);
if (flow != null)
return;
flow = loadElemDBFlow(exchange);
if (flow != null) {
index.putFlow(refId, flow);
return;
}
Category category = null;
if (exchange.compartment != null) {
compartment(exchange.compartment);
category = index.getCompartment(exchange.compartment.id);
}
flow = new Flow();
flow.setRefId(refId);
flow.setCategory(category);
flow.setDescription("EcoSpold 2 elementary exchange, ID = "
+ exchange.flowId);
flow.setFlowType(FlowType.ELEMENTARY_FLOW);
createFlow(exchange, flow);
}
/**
* Tries to load an elementary flow from the database, which could also be a
* mapped flow.
*/
private Flow loadElemDBFlow(ElementaryExchange exchange) {
String extId = exchange.flowId;
Flow flow = flowDao.getForRefId(extId);
if (flow != null)
return flow;
FlowMapEntry entry = flowMap.getEntry(extId);
if (entry == null)
return null;
flow = flowDao.getForRefId(entry.getOpenlcaFlowKey());
if (flow == null)
return null;
index.putMappedFlow(extId, entry.getConversionFactor());
return flow;
}
private void createFlow(Exchange exchange, Flow flow) {
flow.setName(exchange.name);
FlowProperty prop = index.getFlowProperty(exchange.unitId);
if (prop == null) {
log.warn("unknown unit {}", exchange.unitId);
return;
}
FlowPropertyFactor fac = new FlowPropertyFactor();
fac.setFlowProperty(prop);
fac.setConversionFactor(1.0);
flow.getFlowPropertyFactors().add(fac);
flow.setReferenceFlowProperty(prop);
try {
flow = flowDao.insert(flow);
index.putFlow(flow.getRefId(), flow);
} catch (Exception e) {
log.error("Failed to store flow", e);
}
}
/**
* Returns only a value if the given exchange is the reference product of
* the data set.
*/
private Category getProductCategory(DataSet dataSet, IntermediateExchange e) {
Integer og = e.outputGroup;
if (og == null || og != 0)
return null;
Classification clazz = findClassification(dataSet);
if (clazz == null || clazz.value == null)
return null;
Category cat = Categories.findOrCreateRoot(database, ModelType.FLOW,
clazz.value);
return cat;
}
}