All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openlca.io.openepd.EpdConverter Maven / Gradle / Ivy

There is a newer version: 2.2.1
Show newest version
package org.openlca.io.openepd;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Objects;
import java.util.OptionalDouble;
import java.util.function.BiPredicate;
import java.util.function.Function;

import org.openlca.core.model.Actor;
import org.openlca.core.model.Epd;
import org.openlca.core.model.EpdProduct;
import org.openlca.core.model.FlowProperty;
import org.openlca.core.model.RefEntity;
import org.openlca.core.model.Source;
import org.openlca.core.model.Unit;
import org.openlca.util.Categories;
import org.openlca.util.Pair;
import org.openlca.util.Strings;

/**
 * A utility class for converting openLCA EPDs into the openEPD format.
 */
public final class EpdConverter {

	private EpdConverter() {
	}

	/**
	 * Checks if the given EPD model can be converted into on openEPD document.
	 */
	public static Validation validate(Epd epd) {
		if (epd == null)
			return Validation.error("The EPD is empty.");
		if (epd.product == null || epd.product.flow == null)
			return Validation.error("The EPD has no product.");
		if (epd.product.unit == null)
			return Validation.error("The EPD has no declared unit");
		if (epd.product.amount == 0)
			return Validation.error("The product amount is 0.");
		return Validation.ok();
	}

	public static EpdDoc toEpdDoc(Epd epd) {
		var doc = new EpdDoc();
		doc.isPrivate = true;
		doc.version = 1;
		doc.productName = epd.name;
		if (epd.product != null && epd.product.flow != null) {
			doc.productDescription = epd.product.flow.description;
		}
		doc.lcaDiscussion = epd.description;

		// declared unit
		if (epd.product != null && epd.product.unit != null) {
			doc.declaredUnit = new EpdQuantity(
				epd.product.amount, epd.product.unit.name);
		}
		var mass = massInKgOf(epd.product);
		if (mass.isPresent()) {
			doc.kgPerDeclaredUnit = new EpdQuantity(
				mass.getAsDouble(), "kg");
		}

		// category
		if (epd.category != null) {
			var path = Categories.path(epd.category);
			if (!path.isEmpty()) {
				doc.productClasses.add(
					Pair.of("io.cqd.ec3", String.join(" >> ", path)));
			}
		}

		doc.manufacturer = toOrg(epd.manufacturer);
		doc.verifier = toOrg(epd.verifier);
		doc.programOperator = toOrg(epd.programOperator);
		doc.pcr = toPcr(epd.pcr);

		doc.impactResults.addAll(resultsOf(epd));
		return doc;
	}

	public static EpdOrg toOrg(Actor actor) {
		if (actor == null)
			return null;
		var org = new EpdOrg();
		org.name = actor.name;
		org.webDomain = actor.website;
		return org;
	}

	public static EpdPcr toPcr(Source source) {
		if (source == null)
			return null;
		var pcr = new EpdPcr();
		pcr.id = source.refId;
		pcr.name = source.name;
		return pcr;
	}

	public static Collection resultsOf(Epd epd) {
		var map = new HashMap();
		for (var mod : epd.modules) {
			var result = mod.result;
			if (result == null
				|| result.impactMethod == null
				|| Strings.nullOrEmpty(result.impactMethod.code))
				continue;
			var docResult = map.computeIfAbsent(
				result.impactMethod.code,
				code -> new EpdImpactResult(code, new ArrayList<>()));

			for (var impact : result.impactResults) {
				if (impact.indicator == null
					|| Strings.nullOrEmpty(impact.indicator.code))
					continue;
				var code = impact.indicator.code;
				EpdIndicatorResult docImpact = null;
				for (var i : docResult.results()) {
					if (Objects.equals(code, i.indicator())) {
						docImpact = i;
						break;
					}
				}
				if (docImpact == null) {
					docImpact = new EpdIndicatorResult(code, new ArrayList<>());
					docResult.results().add(docImpact);
				}
				var value = EpdMeasurement.of(
					mod.multiplier * impact.amount,
					impact.indicator.referenceUnit);
				docImpact.values().add(new EpdScopeValue(mod.name, value));
			}
		}
		return map.values();
	}

	/**
	 * Tries to infer the mass in kilograms per declared unit.
	 */
	public static OptionalDouble massInKgOf(EpdProduct product) {
		if (product == null
			|| product.flow == null
			|| product.property == null
			|| product.unit == null)
			return OptionalDouble.empty();

		BiPredicate matches = (name, entity) -> {
			if (entity == null || entity.name == null)
				return false;
			var eName = entity.name.trim();
			return eName.equalsIgnoreCase(name);
		};

		Function getKg = prop -> {
			if (prop.unitGroup == null)
				return null;
			for (var unit : prop.unitGroup.units) {
				if (matches.test("kg", unit))
					return unit;
			}
			return null;
		};

		// the property of the product is mass
		if (matches.test("Mass", product.property)) {
			if (matches.test("kg", product.unit))
				return OptionalDouble.of(product.amount);

			var kg = getKg.apply(product.property);
			if (kg == null)
				return OptionalDouble.empty();

			var amount = product.amount * product.unit.conversionFactor
				/ kg.conversionFactor;
			return OptionalDouble.of(amount);
		}

		// search for Mass and kg
		var massFac = product.flow.flowPropertyFactors.stream()
			.filter(f -> matches.test("Mass", f.flowProperty))
			.findAny()
			.orElse(null);
		if (massFac == null || massFac.flowProperty == null)
			return OptionalDouble.empty();
		var kg = getKg.apply(massFac.flowProperty);
		var propFac = product.flow.getFactor(product.property);
		if (kg == null || propFac == null)
			return OptionalDouble.empty();

		var amount = product.amount * product.unit.conversionFactor
			* massFac.conversionFactor
			/ (kg.conversionFactor * propFac.conversionFactor);
		return OptionalDouble.of(amount);
	}

	public record Validation(String error) {

		static Validation ok() {
			return new Validation(null);
		}

		static Validation error(String message) {
			return new Validation(message);
		}

		public boolean hasError() {
			return error != null;
		}

		public boolean isOk() {
			return error == null;
		}

	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy