package org.openlca.core.matrix.matlib;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.openlca.core.database.FlowDao;
import org.openlca.core.database.IDatabase;
import org.openlca.core.database.LocationDao;
import org.openlca.core.database.NativeSql;
import org.openlca.core.database.ProcessDao;
import org.openlca.core.database.RootEntityDao;
import org.openlca.core.math.IMatrix;
import org.openlca.core.math.IMatrixFactory;
import org.openlca.core.math.IMatrixSolver;
import org.openlca.core.matrix.LongIndex;
import org.openlca.core.matrix.LongPair;
import org.openlca.core.matrix.TechIndex;
import org.openlca.core.matrix.cache.FlowTypeTable;
import org.openlca.core.matrix.cache.ProcessTable;
import org.openlca.core.model.descriptors.BaseDescriptor;
import org.openlca.core.model.descriptors.FlowDescriptor;
import org.openlca.core.model.descriptors.LocationDescriptor;
import org.openlca.core.model.descriptors.ProcessDescriptor;
/**
* Exports a matrix into the openLCA matrix-library (=matlib) format.The matlib
* format is still experimental so this may change in future.
*
* TODO: unit conversion; allocation factors
*/
public class MatlibExport implements Runnable {
private final IDatabase db;
private final File dir;
private final IMatrixSolver solver;
public boolean withResults = false;
private ProcessTable processTable;
private TechIndex techIndex;
private LongIndex flowIndex;
private IMatrix techMatrix;
private IMatrix enviMatrix;
public MatlibExport(IDatabase db, IMatrixSolver solver, File dir) {
this.db = db;
this.dir = dir;
this.solver = solver;
}
@Override
public void run() {
try {
if (!dir.exists())
dir.mkdirs();
initMatrices();
fillMatrices();
writeMetaData();
writeMatrices();
} catch (Exception e) {
throw new RuntimeException("MatlibExport failed", e);
}
}
private void fillMatrices() throws SQLException {
String query = "select f_owner, f_flow, is_input, "
+ "resulting_amount_value from tbl_exchanges";
NativeSql.on(db).query(query, r -> {
long processId = r.getLong(1);
long flowId = r.getLong(2);
boolean isInput = r.getBoolean(3);
double amount = r.getDouble(4);
if (flowIndex.contains(flowId))
setEnvi(processId, flowId, amount);
else
setTech(processId, flowId, isInput, amount);
return true;
});
}
private void setTech(long processId, long flowId, boolean isInput, double amount) {
if (!isInput) {
LongPair p = LongPair.of(processId, flowId);
int idx = techIndex.getIndex(p);
if (idx < 0)
return;
techMatrix.set(idx, idx, amount);
return;
}
long[] providers = processTable.getProductProviders(flowId);
for (long provider : providers) {
LongPair prov = LongPair.of(provider, flowId);
int row = techIndex.getIndex(prov);
if (row < 0)
continue;
for (LongPair recipient : techIndex.getProviders(processId)) {
int col = techIndex.getIndex(recipient);
if (col < 0)
continue;
techMatrix.set(row, col, -amount);
}
break;
}
}
private void setEnvi(long processId, long flowId, double amount) {
for (LongPair p : techIndex.getProviders(processId)) {
int row = flowIndex.getIndex(flowId);
int col = techIndex.getIndex(p);
if (row < 0 || col < 0)
continue;
enviMatrix.set(row, col, amount);
}
}
private void initMatrices() {
FlowTypeTable flowTypes = FlowTypeTable.create(db);
processTable = ProcessTable.create(db, flowTypes);
List<LongPair> products = processTable.getProcessProducts();
techIndex = new TechIndex(products.get(0));
flowIndex = new LongIndex();
Set<Long> productIds = new HashSet<>();
for (int i = 0; i < products.size(); i++) {
LongPair product = products.get(i);
techIndex.put(products.get(i));
productIds.add(product.getSecond());
}
for (long flowId : flowTypes.getFlowIds()) {
if (productIds.contains(flowId))
continue;
flowIndex.put(flowId);
}
IMatrixFactory<?> f = solver.getMatrixFactory();
int n = techIndex.size();
int k = flowIndex.size();
techMatrix = f.create(n, n);
enviMatrix = f.create(k, n);
}
private void writeMatrices() throws Exception {
writeMatrix(techMatrix, new File(dir, "A.bin"));
writeMatrix(enviMatrix, new File(dir, "B.bin"));
if (withResults) {
IMatrix invA = solver.invert(techMatrix);
writeMatrix(invA, new File(dir, "Ainv.bin"));
IMatrix m = solver.multiply(enviMatrix, invA);
writeMatrix(m, new File(dir, "M.bin"));
}
}
private void writeMatrix(IMatrix m, File file) throws Exception {
try (FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream buffer = new BufferedOutputStream(fos)) {
// byte buffers for int and double
ByteBuffer i32 = ByteBuffer.allocate(4);
ByteBuffer f64 = ByteBuffer.allocate(8);
i32.order(ByteOrder.LITTLE_ENDIAN);
f64.order(ByteOrder.LITTLE_ENDIAN);
// rows + columns
i32.putInt(m.rows());
buffer.write(i32.array());
i32.clear();
i32.putInt(m.columns());
buffer.write(i32.array());
// values
for (int col = 0; col < m.columns(); col++) {
for (int row = 0; row < m.rows(); row++) {
f64.putDouble(m.get(row, col));
buffer.write(f64.array());
f64.clear();
}
}
}
}
private void writeMetaData() throws Exception {
Map<Long, FlowDescriptor> flows = getDescriptors(new FlowDao(db));
writeTechIndex(flows);
List<String> indexB = new ArrayList<>(flowIndex.size() + 1);
indexB.add("index;flow");
for (int i = 0; i < flowIndex.size(); i++) {
StringBuilder builder = new StringBuilder();
builder.append(i).append(";\"");
FlowDescriptor flow = flows.get(flowIndex.getKeyAt(i));
if (flow != null)
builder.append(flow.getName());
builder.append("\"");
indexB.add(builder.toString());
}
writeIndex(indexB, new File(dir, "index_B.csv"));
}
private void writeTechIndex(Map<Long, FlowDescriptor> flows) throws Exception {
Map<Long, ProcessDescriptor> procs = getDescriptors(new ProcessDao(db));
Map<Long, LocationDescriptor> locs = getDescriptors(new LocationDao(db));
List<String> indexA = new ArrayList<>(techIndex.size() + 1);
indexA.add("index;process;location;flow");
for (int i = 0; i < techIndex.size(); i++) {
StringBuilder builder = new StringBuilder();
builder.append(i).append(";\"");
LongPair product = techIndex.getProviderAt(i);
ProcessDescriptor proc = procs.get(product.getFirst());
if (proc != null)
builder.append(proc.getName());
builder.append("\";\"");
if (proc != null && proc.getLocation() != null) {
LocationDescriptor loc = locs.get(proc.getLocation());
if (loc != null)
builder.append(loc.getName());
}
builder.append("\";\"");
FlowDescriptor flow = flows.get(product.getSecond());
if (flow != null)
builder.append(flow.getName());
builder.append("\"");
indexA.add(builder.toString());
}
writeIndex(indexA, new File(dir, "index_A.csv"));
}
private void writeIndex(List<String> lines, File file) throws Exception {
try (FileOutputStream fos = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(fos, "utf-8");
BufferedWriter buffer = new BufferedWriter(writer)) {
for (String line : lines) {
buffer.write(line);
buffer.newLine();
}
}
}
private <T extends BaseDescriptor> Map<Long, T> getDescriptors(
RootEntityDao<?, T> dao) {
Map<Long, T> map = new HashMap<>();
for (T d : dao.getDescriptors()) {
map.put(d.getId(), d);
}
return map;
}
}