Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improved handling if not earliest forested value if provided #126

Merged
merged 3 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@
import de.envite.greenbpm.carbonreductor.core.domain.model.CarbonReductorConfiguration;
import de.envite.greenbpm.carbonreductor.core.domain.model.ExceptionHandlingEnum;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.Milestone;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.Threshold;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.ProcessDuration;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.Threshold;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.location.Location;
import io.github.domainprimitives.type.ValueObject;
import org.springframework.stereotype.Component;

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;


@Component
Expand Down Expand Up @@ -54,12 +56,15 @@ private Threshold mapIfEnabled(boolean enabled, String thresholdValue) {
}

public Map<String, Object> mapFromDomain(CarbonReduction output, Map<String, Object> allVariables) {
final Double carbonWithoutOptimization = Optional.ofNullable(output.getCarbonWithoutOptimization())
.map(ValueObject::getValue)
.orElse(null);
Map<String, Object> variables = new HashMap<>();
variables.put("executionDelayed", output.getDelay().isExecutionDelayed());
variables.put("carbonWithoutOptimization", output.getCarbonWithoutOptimization().getValue());
variables.put("carbonWithoutOptimization", carbonWithoutOptimization);
variables.put("optimalForecastedCarbon", output.getOptimalForecastedCarbon().getValue());
variables.put("savedCarbonPercentage", output.getSavedCarbonPercentage().getValue());
variables.put("reducedCarbon", output.calculateReduction().getValue());
variables.put("savedCarbonPercentage", Optional.ofNullable(output.getSavedCarbonPercentage()).map(ValueObject::getValue).orElse(null));
variables.put("reducedCarbon", Optional.ofNullable(output.calculateReduction()).map(ValueObject::getValue).orElse(null));
variables.put("delayedBy", output.getDelay().getDelayedBy());
// Override milestone variable because joda time is not a primitive object ..
variables.put("milestone", getMilestone(allVariables).format(DateTimeFormatter.ISO_DATE_TIME));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,29 @@ void should_map_all_fields_to_map() {
softAssertions.assertThat(result.get("milestone")).isEqualTo(mileStoneDateString);
softAssertions.assertAll();
}

@Test
void should_map_mandatory_fields_to_map() {
final String mileStoneDateString = "2023-09-08T14:13:40.764+02:00";
CarbonReduction carbonReduction = new CarbonReduction(
new Delay(true, 3),
null,
new Carbon(2.0),
null
);
Map<String, Object> variables = Map.of("milestone", mileStoneDateString);

Map<String, Object> result = classUnderTest.mapFromDomain(carbonReduction, variables);

SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(result.get("executionDelayed")).isEqualTo(carbonReduction.getDelay().isExecutionDelayed());
softAssertions.assertThat(result.get("carbonWithoutOptimization")).isNull();
softAssertions.assertThat(result.get("optimalForecastedCarbon")).isEqualTo(carbonReduction.getOptimalForecastedCarbon().getValue());
softAssertions.assertThat(result.get("savedCarbonPercentage")).isNull();
softAssertions.assertThat(result.get("reducedCarbon")).isNull();
softAssertions.assertThat(result.get("delayedBy")).isEqualTo(carbonReduction.getDelay().getDelayedBy());
softAssertions.assertThat(result.get("milestone")).isEqualTo(mileStoneDateString);
softAssertions.assertAll();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
public class CarbonReductorOutputVariable {

private boolean executionDelayed;
private double carbonWithoutOptimization;
private Double carbonWithoutOptimization;
private double optimalForecastedCarbon;
private double savedCarbonPercentage;
private double carbonReduction;
private Double savedCarbonPercentage;
private Double carbonReduction;
private long delayedBy;

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import de.envite.greenbpm.carbonreductor.core.domain.model.CarbonReductorConfiguration;
import de.envite.greenbpm.carbonreductor.core.domain.model.ExceptionHandlingEnum;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.Milestone;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.Threshold;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.ProcessDuration;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.Threshold;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.location.Location;
import io.github.domainprimitives.type.ValueObject;
import org.springframework.stereotype.Component;

import java.time.OffsetDateTime;
import java.time.format.DateTimeParseException;
import java.util.Optional;

import static java.lang.String.format;
import static java.time.format.DateTimeFormatter.ofPattern;
Expand Down Expand Up @@ -55,13 +57,16 @@ private ProcessDuration mapIfNotNull(String input) {
}

public CarbonReductorOutputVariable mapFromDomain(CarbonReduction output) {
final Double carbonWithoutOptimization = Optional.ofNullable(output.getCarbonWithoutOptimization())
.map(ValueObject::getValue)
.orElse(null);
CarbonReductorOutputVariable outputVariable = new CarbonReductorOutputVariable();
outputVariable.setExecutionDelayed(output.getDelay().isExecutionDelayed());
outputVariable.setDelayedBy(output.getDelay().getDelayedBy());
outputVariable.setOptimalForecastedCarbon(output.getOptimalForecastedCarbon().getValue());
outputVariable.setCarbonWithoutOptimization(output.getCarbonWithoutOptimization().getValue());
outputVariable.setSavedCarbonPercentage(output.getSavedCarbonPercentage().getValue());
outputVariable.setCarbonReduction(output.calculateReduction().getValue());
outputVariable.setCarbonWithoutOptimization(carbonWithoutOptimization);
outputVariable.setSavedCarbonPercentage(Optional.ofNullable(output.getSavedCarbonPercentage()).map(ValueObject::getValue).orElse(null));
outputVariable.setCarbonReduction(Optional.ofNullable(output.calculateReduction()).map(ValueObject::getValue).orElse(null));
return outputVariable;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ private CarbonReductorOutputVariable createDefaultOutput() {
defaultCarbonReductorOutput.setCarbonWithoutOptimization(0.0);
defaultCarbonReductorOutput.setOptimalForecastedCarbon(0.0);
defaultCarbonReductorOutput.setSavedCarbonPercentage(0.0);
defaultCarbonReductorOutput.setCarbonReduction(0.0);
defaultCarbonReductorOutput.setDelayedBy(0);
return defaultCarbonReductorOutput;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import de.envite.greenbpm.carbonreductor.core.domain.model.CarbonReduction;
import de.envite.greenbpm.carbonreductor.core.domain.model.CarbonReductorConfiguration;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.Milestone;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.Threshold;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.ProcessDuration;
import de.envite.greenbpm.carbonreductor.core.domain.model.input.Threshold;
import de.envite.greenbpm.carbonreductor.core.domain.model.output.Carbon;
import de.envite.greenbpm.carbonreductor.core.domain.model.output.Delay;
import de.envite.greenbpm.carbonreductor.core.domain.model.output.Percentage;
Expand Down Expand Up @@ -34,6 +34,15 @@ public static CarbonReduction createCarbonReductorOutput() {
);
}

public static CarbonReduction createCarbonReductorOutputWithoutCurrentValue() {
return new CarbonReduction(
new Delay(true, 3),
null,
new Carbon(2.0),
null
);
}

public static CarbonReductorConfiguration createSLABasedCarbonReductorInput(OffsetDateTime timestamp) {
return new CarbonReductorConfiguration(
NORWAY_EAST.asLocation(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@

import java.time.*;

import static de.envite.greenbpm.carbonreductorconnector.adapter.in.zeebe.test.utils.TestDataGenerator.createCarbonReductorOutput;
import static de.envite.greenbpm.carbonreductorconnector.adapter.in.zeebe.test.utils.TestDataGenerator.createInputVariables;
import static de.envite.greenbpm.carbonreductorconnector.adapter.in.zeebe.test.utils.TestDataGenerator.*;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;

class CarbonReductorVariableMapperTest {
Expand Down Expand Up @@ -54,19 +53,39 @@ void should_throw_on_invalid_date() {
}
}

@Test
void should_map_all_fields_from_domain() {
CarbonReduction outputDDD = createCarbonReductorOutput();
@Nested
class MapFromDomain {

@Test
void should_map_all_fields_from_domain() {
CarbonReduction outputDDD = createCarbonReductorOutput();

CarbonReductorOutputVariable result = classUnderTest.mapFromDomain(outputDDD);

CarbonReductorOutputVariable result = classUnderTest.mapFromDomain(outputDDD);
SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(result.isExecutionDelayed()).isEqualTo(outputDDD.getDelay().isExecutionDelayed());
softAssertions.assertThat(result.getDelayedBy()).isEqualTo(outputDDD.getDelay().getDelayedBy());
softAssertions.assertThat(result.getOptimalForecastedCarbon()).isEqualTo(outputDDD.getOptimalForecastedCarbon().getValue());
softAssertions.assertThat(result.getCarbonWithoutOptimization()).isEqualTo(outputDDD.getCarbonWithoutOptimization().getValue());
softAssertions.assertThat(result.getSavedCarbonPercentage()).isEqualTo(outputDDD.getSavedCarbonPercentage().getValue());
softAssertions.assertThat(result.getCarbonReduction()).isEqualTo(outputDDD.calculateReduction().getValue());
softAssertions.assertAll();
}

SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(result.isExecutionDelayed()).isEqualTo(outputDDD.getDelay().isExecutionDelayed());
softAssertions.assertThat(result.getDelayedBy()).isEqualTo(outputDDD.getDelay().getDelayedBy());
softAssertions.assertThat(result.getOptimalForecastedCarbon()).isEqualTo(outputDDD.getOptimalForecastedCarbon().getValue());
softAssertions.assertThat(result.getCarbonWithoutOptimization()).isEqualTo(outputDDD.getCarbonWithoutOptimization().getValue());
softAssertions.assertThat(result.getSavedCarbonPercentage()).isEqualTo(outputDDD.getSavedCarbonPercentage().getValue());
softAssertions.assertThat(result.getCarbonReduction()).isEqualTo(outputDDD.calculateReduction().getValue());
softAssertions.assertAll();
@Test
void should_map_mandatory_fields_from_domain() {
CarbonReduction outputDDD = createCarbonReductorOutputWithoutCurrentValue();

CarbonReductorOutputVariable result = classUnderTest.mapFromDomain(outputDDD);

SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(result.isExecutionDelayed()).isEqualTo(outputDDD.getDelay().isExecutionDelayed());
softAssertions.assertThat(result.getDelayedBy()).isEqualTo(outputDDD.getDelay().getDelayedBy());
softAssertions.assertThat(result.getOptimalForecastedCarbon()).isEqualTo(outputDDD.getOptimalForecastedCarbon().getValue());
softAssertions.assertThat(result.getCarbonWithoutOptimization()).isNull();
softAssertions.assertThat(result.getSavedCarbonPercentage()).isNull();
softAssertions.assertThat(result.getCarbonReduction()).isNull();
softAssertions.assertAll();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,15 @@

import de.envite.greenbpm.api.carbonawarecomputing.model.EmissionsData;
import de.envite.greenbpm.carbonreductor.core.domain.model.EmissionTimeframe;
import de.envite.greenbpm.carbonreductor.core.domain.model.emissionframe.EarliestForecastedValue;
import de.envite.greenbpm.carbonreductor.core.domain.model.emissionframe.ForecastedValue;
import de.envite.greenbpm.carbonreductor.core.domain.model.emissionframe.OptimalTime;

import java.time.OffsetDateTime;
import java.time.ZoneOffset;

public class CarbonAwareComputingMapper {

public EmissionTimeframe mapToDomain(EmissionsData emissionsData) {
if (emissionsData.getTimestamp() != null && emissionsData.getTimestamp().isAfter(OffsetDateTime.now(ZoneOffset.UTC))) {
return new EmissionTimeframe(
new OptimalTime(emissionsData.getTimestamp()),
new EarliestForecastedValue(0.0),
new ForecastedValue(emissionsData.getValue())
);
}

return new EmissionTimeframe(
new OptimalTime(emissionsData.getTimestamp()),
new EarliestForecastedValue(emissionsData.getValue()),
null,
new ForecastedValue(emissionsData.getValue())
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ public class CarbonReduction extends Aggregate {

private final Delay delay;

/**
* CO2eq if executed immediately.
* Could be null because some data provider only serve the {@link CarbonReduction#optimalForecastedCarbon}.
*/
private final Carbon carbonWithoutOptimization;
private final Carbon optimalForecastedCarbon;
private final Percentage savedCarbonPercentage;
Expand All @@ -26,13 +30,18 @@ public CarbonReduction(Delay delay, Carbon carbonWithoutOptimization, Carbon opt
@Override
protected void validate() {
validateNotNull(delay, "Delay");
validateNotNull(carbonWithoutOptimization, "original Carbon");
validateNotNull(optimalForecastedCarbon, "actual Carbon");
validateNotNull(savedCarbonPercentage, "saved Carbon");
evaluateValidations();
}

/**
* Calculates the Carbon Reduction due to the time shifting
* @return Carbon Reduction. If no calculation is possible it returns null
*/
public Carbon calculateReduction() {
if (carbonWithoutOptimization == null) {
return null;
}
return new Carbon(carbonWithoutOptimization.getValue() - optimalForecastedCarbon.getValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,22 @@
@EqualsAndHashCode(callSuper = false)
public class EmissionTimeframe extends Aggregate {

/**
* Optimal execution time with lowest CO2eq for the specific timeframe.
* Value is represented by {@link EmissionTimeframe#optimalValue}.
*/
private final OptimalTime optimalTime;

/**
* Earliest forecasted value (CO2eq). Could be null because some data provider only serve the {@link EmissionTimeframe#optimalValue}.
* Usefully for comparing the savings.
*/
private final EarliestForecastedValue earliestForecastedValue;

/**
* Optimal forecasted value for the execution with lowest CO2eq for the specific timeframe.
* Execution time when this value could be reached is represented by {@link EmissionTimeframe#optimalTime}.
*/
private final ForecastedValue optimalValue;

public EmissionTimeframe(OptimalTime optimalTime, EarliestForecastedValue earliestForecastedValue, ForecastedValue optimalValue) {
Expand All @@ -25,21 +39,29 @@ public EmissionTimeframe(OptimalTime optimalTime, EarliestForecastedValue earlie
@Override
protected void validate() {
validateNotNull(optimalTime, "Optimal Time");
validateNotNull(earliestForecastedValue, "Rating");
validateNotNull(optimalValue, "ForecastedValue");
evaluateValidations();
}

public boolean isCleanerEnergyInFuture() {
if (earliestForecastedValue == null) {
return optimalTime.isInFuture();
}
final boolean emissionReduction = earliestForecastedValue.getValue() > optimalValue.getValue();
return emissionReduction && optimalTime.isInFuture();
}

public double calculateSavedCarbonDelta() {
public Double calculateSavedCarbonDelta() {
if (earliestForecastedValue == null) {
return null;
}
return earliestForecastedValue.getValue() - optimalValue.getValue();
}

public double calculateSavedCarbonPercentage() {
public Double calculateSavedCarbonPercentage() {
if (earliestForecastedValue == null) {
return null;
}
double difference = earliestForecastedValue.getValue() - optimalValue.getValue();
return (difference / earliestForecastedValue.getValue()) * 100;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import de.envite.greenbpm.carbonreductor.core.domain.model.output.Percentage;
import de.envite.greenbpm.carbonreductor.core.usecase.in.DelayCalculator;
import de.envite.greenbpm.carbonreductor.core.usecase.out.CarbonEmissionQuery;
import io.github.domainprimitives.type.ValueObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
Expand All @@ -18,6 +19,7 @@
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.Optional;

import static de.envite.greenbpm.carbonreductor.core.domain.model.ExceptionHandlingEnum.CONTINUE_ON_EXCEPTION;

Expand Down Expand Up @@ -46,26 +48,31 @@ public CarbonReduction calculateDelay(CarbonReductorConfiguration input) throws
}

boolean isDelayNecessary = input.isDelayStillRelevant() && emissionTimeframe.isCleanerEnergyInFuture();
boolean isGreaterThanMinimumThreshold = input.getThreshold().isGreaterThanMinimumThreshold(
emissionTimeframe.calculateSavedCarbonDelta()
);
boolean isGreaterThanMinimumThreshold = Optional.ofNullable(emissionTimeframe.calculateSavedCarbonDelta())
.map(savedCarbonDelta -> input.getThreshold().isGreaterThanMinimumThreshold(savedCarbonDelta))
.orElse(false);

final Carbon carbonWithoutOptimization = Optional.ofNullable(emissionTimeframe.getEarliestForecastedValue())
.map(ValueObject::getValue)
.map(Carbon::new)
.orElse(null);

if (isDelayNecessary && isGreaterThanMinimumThreshold) {
final long optimalTime = emissionTimeframe.getOptimalTime().asOffsetDateTime().toInstant().toEpochMilli();
final long delayedBy = optimalTime - OffsetDateTime.now(ZoneOffset.UTC).toInstant().toEpochMilli();
final boolean executionDelayed = !input.isMeasurementOnly();
return new CarbonReduction(
new Delay(executionDelayed, delayedBy),
new Carbon(emissionTimeframe.getEarliestForecastedValue().getValue()),
carbonWithoutOptimization,
new Carbon(emissionTimeframe.getOptimalValue().getValue()),
new Percentage(emissionTimeframe.calculateSavedCarbonPercentage())
);
}
// execution is optimal currently
return new CarbonReduction(
new Delay(false, 0),
new Carbon(emissionTimeframe.getEarliestForecastedValue().getValue()),
new Carbon(emissionTimeframe.getEarliestForecastedValue().getValue()),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this a bug?

carbonWithoutOptimization,
new Carbon(emissionTimeframe.getOptimalValue().getValue()),
new Percentage(0.0)
);
}
Expand Down
Loading
Loading