Skip to content

Instantly share code, notes, and snippets.

@cyriux
Last active May 17, 2024 08:26
Show Gist options
  • Save cyriux/a263efb9c483bcefe72e49c3343ff24e to your computer and use it in GitHub Desktop.
Save cyriux/a263efb9c483bcefe72e49c3343ff24e to your computer and use it in GitHub Desktop.
CaseStudyEnvironmentalImpact
package com.cyrillemartraire.monoids;
import static com.cyrillemartraire.monoids.EnvironmentalImpactTest.CertifiedAmount.certified;
import static com.cyrillemartraire.monoids.EnvironmentalImpactTest.CertifiedAmount.uncertified;
import static com.cyrillemartraire.monoids.EnvironmentalImpactTest.EnvironmentalImpact.singleSupplier;
import static java.lang.Double.doubleToLongBits;
import static java.lang.Math.abs;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class EnvironmentalImpactTest {
@Test
public void pizzaImpact() {
EnvironmentalImpact cooking = singleSupplier(certified(1, "kWh", 0.3), certified(1, "T", 0.25));
EnvironmentalImpact dough = singleSupplier(uncertified(5, "kWh", 5.), uncertified(0.5, "T", 1.));
EnvironmentalImpact tomatoSauce = singleSupplier(uncertified(3, "kWh", 1.), certified(0.2, "T", 0.1));
EnvironmentalImpact expectedPizza = new EnvironmentalImpact(3,
new CertifiedAmount(new Amount(6.9, "kWh", 5.6), 1., 2.3),
new CertifiedAmount(new Amount(1.56, "T", 1.28), 1.3, 2.3));
EnvironmentalImpact pizza = cooking.add(dough).add(tomatoSauce.times(0.3));
assertEquals(expectedPizza, pizza);
System.out.println(cooking);
System.out.println(dough);
System.out.println(tomatoSauce);
System.out.println(pizza);
}
/**
* The energy and carbon impacts across a supply chain
*/
public static class EnvironmentalImpact {
private final int supplierCount;
private final CertifiedAmount energyConsumption;
private final CertifiedAmount carbonEmission;
public static final EnvironmentalImpact neutral() {
return new EnvironmentalImpact(0, CertifiedAmount.neutral("kWh"), CertifiedAmount.neutral("T"));
}
public static final EnvironmentalImpact singleSupplier(CertifiedAmount energyConsumption,
CertifiedAmount carbonEmission) {
return new EnvironmentalImpact(1, energyConsumption, carbonEmission);
}
public EnvironmentalImpact(int supplierCount, CertifiedAmount energyConsumption,
CertifiedAmount carbonEmission) {
this.supplierCount = supplierCount;
this.energyConsumption = energyConsumption;
this.carbonEmission = carbonEmission;
}
public EnvironmentalImpact add(EnvironmentalImpact other) {
return new EnvironmentalImpact(supplierCount + other.supplierCount,
energyConsumption.add(other.energyConsumption), carbonEmission.add(other.carbonEmission));
}
public EnvironmentalImpact add(double coefficient, EnvironmentalImpact other) {
return add(other.times(coefficient));
}
public EnvironmentalImpact times(double coefficient) {
return new EnvironmentalImpact(supplierCount, energyConsumption.times(coefficient),
carbonEmission.times(coefficient));
}
@Override
public int hashCode() {
return 31 ^ carbonEmission.hashCode() + energyConsumption.hashCode() ^ supplierCount;
}
@Override
public boolean equals(Object obj) {
EnvironmentalImpact other = (EnvironmentalImpact) obj;
return supplierCount == other.supplierCount && energyConsumption.equals(other.energyConsumption)
&& carbonEmission.equals(other.carbonEmission);
}
@Override
public String toString() {
return "EnvironmentalImpact(" + supplierCount + " supplier" + (supplierCount == 1 ? "" : "s") + ", energy: "
+ energyConsumption + ", carbon: " + carbonEmission + ")";
}
}
/**
* An amount that keeps track of its percentage of certification
*/
public static class CertifiedAmount {
private final Amount amount;
private final double score;// the total certification score
private final double weight; // the total weight of the certified thing
public static final CertifiedAmount certified(Amount amount) {
return new CertifiedAmount(amount, 1., 1.);
}
public static CertifiedAmount neutral(String unit) {
return new CertifiedAmount(Amount.neutral(unit), 0., 0.);
}
public static final CertifiedAmount certified(double value, String unit, double errorMargin) {
return new CertifiedAmount(new Amount(value, unit, errorMargin), 1., 1.);
}
public static final CertifiedAmount uncertified(double value, String unit, double errorMargin) {
return new CertifiedAmount(new Amount(value, unit, errorMargin), 0., 1.);
}
public CertifiedAmount(Amount amount, double score, double weight) {
this.amount = amount;
this.score = score;
this.weight = weight;
}
public CertifiedAmount add(CertifiedAmount other) {
return new CertifiedAmount(amount.add(other.amount), score + other.score, weight + other.weight);
}
public CertifiedAmount times(double coefficient) {
return new CertifiedAmount(amount.times(coefficient), coefficient * score, coefficient);
}
@Override
public int hashCode() {
return (int) (amount.hashCode() ^ doubleToLongBits(score) ^ doubleToLongBits(weight));
}
@Override
public boolean equals(Object obj) {
CertifiedAmount other = (CertifiedAmount) obj;
return amount.equals(other.amount) && abs(score - other.score) <= 0.01
&& abs(weight - other.weight) <= 0.01;
}
@Override
public String toString() {
return amount + " (" + ((int) (score * 100. / weight)) + "% certified)";
}
}
/**
* An amount of a physical quantity, with its unit, margin of error and
* percentage of certification
*/
public static class Amount {
private final double value;
private final String unit;
private final double errorMargin;
public static final Amount neutral(String unit) {
return new Amount(0., unit, 0.);
}
public Amount(double value, String unit, double errorMargin) {
this.value = value;
this.unit = unit;
this.errorMargin = errorMargin;
}
public Amount add(double coefficient, Amount other) {
return this.add(other.times(coefficient));
}
public Amount add(Amount other) {
if (!unit.equals(other.unit))
throw new IllegalArgumentException(
"Cannot add amounts of different units: " + unit + " <> " + other.unit);
return new Amount(value + other.value, unit, errorMargin + other.errorMargin);
}
public Amount times(double coefficient) {
return new Amount(coefficient * value, unit, coefficient * errorMargin);
}
@Override
public int hashCode() {
return (int) (31 ^ doubleToLongBits(errorMargin) + unit.hashCode() ^ doubleToLongBits(value));
}
@Override
public boolean equals(Object obj) {
Amount other = (Amount) obj;
return abs(value - other.value) <= 0.01 && unit.equals(other.unit)
&& abs(errorMargin - other.errorMargin) <= 0.1;
}
@Override
public String toString() {
return value + (errorMargin == 0. ? "" : "+/-" + errorMargin) + " " + unit;
}
}
}
@cyriux
Copy link
Author

cyriux commented Jul 19, 2018

When displayed into the console, we get:

EnvironmentalImpact(1 supplier, 
  energy: 1.0+/-0.3 kWh (100% certified), 
  carbon: 1.0+/-0.25 T (100% certified))
EnvironmentalImpact(1 supplier, 
  energy: 5.0+/-5.0 kWh (0% certified), 
  carbon: 0.5+/-1.0 T (0% certified))
EnvironmentalImpact(1 supplier, 
  energy: 3.0+/-1.0 kWh (0% certified), 
  carbon: 0.2+/-0.1 T (100% certified))

From that we can calculate the full impact of the restaurant by chaining each impact:

EnvironmentalImpact pizza = cooking
  .add(dough)
  .add(tomatoSauce.times(0.3));

If we print the resulting impact into the console, we get:

EnvironmentalImpact(3 suppliers, 
  energy: 6.9+/-5.6 kWh (43% certified), 
  carbon: 1.56+/-1.28 T (56% certified))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment