Skip to content

Commit

Permalink
major changes to allow section titles between plots
Browse files Browse the repository at this point in the history
  • Loading branch information
kainagel committed Jul 15, 2024
1 parent 64047b1 commit fd99dc2
Show file tree
Hide file tree
Showing 13 changed files with 460 additions and 433 deletions.
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
<artifactId>tablesaw-jsplot</artifactId>
<version>0.43.1</version>
</dependency>
<dependency>
<groupId>tech.tablesaw</groupId>
<artifactId>tablesaw-html</artifactId>
<version>0.43.1</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
Expand Down
19 changes: 7 additions & 12 deletions src/main/java/org/tub/vsp/bvwp/computation/ComputationKN.java
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ static double nkvOhneKR_induz(Modifications modifications, Amounts amounts, Bene
double b_co2_reroute = amounts.pkwkm_reroute * amounts.co2_per_pkwkm * b_per_co2;


// ### first deduct the CO2 components so that we can later re-scale the other material according to changed discount rate:
// ### first deduct the CO2 components so that we can afterwards re-scale the other material according to changed discount rate:

// --- for infra:
b_all -= benefits.co2_infra;
Expand All @@ -202,13 +202,7 @@ static double nkvOhneKR_induz(Modifications modifications, Amounts amounts, Bene
b_all -= b_co2_verl;
b_all -= b_co2_induz;

// ### then rescale the remaining benefit with the corresponding modification:
{
double b_tmp = b_all;
b_all *= modifications.nonCo2BenefitsFactor();
prn( "b w nonCo2 factor:", b_all, b_tmp );
}
// ### then add the CO2 components with the new values:
// ### then re-add the CO2 components with the new values:

// co2 Bau
{
Expand All @@ -218,21 +212,22 @@ static double nkvOhneKR_induz(Modifications modifications, Amounts amounts, Bene
}

// co2 Betrieb
final double operationsCorrFactor = modifications.co2Price() / 145. * modifications.discountCorrFact() * modifications.emobCorrFact();

{
double b_tmp = b_all;
b_all += b_co2_reroute / 145. * modifications.co2Price();
b_all += b_co2_reroute / 145. * modifications.co2Price() * modifications.discountCorrFact() * modifications.emobCorrFact();
prn("b after co2 reroute:", b_all, b_tmp);
}
{
double b_tmp = b_all;
b_all += b_co2_verl / 145. * modifications.co2Price();
b_all += b_co2_verl / 145. * modifications.co2Price() * modifications.discountCorrFact() * modifications.emobCorrFact();
prn("b after co2 verl:", b_all, b_tmp);
}
{
double b_tmp = b_all;
b_all += b_co2_induz / 145. * modifications.co2Price();
b_all += modifications.mehrFzkm() * amounts.co2_per_pkwkm * b_per_co2 * modifications.co2Price() / 145;
b_all += b_co2_induz / 145. * modifications.co2Price() * modifications.discountCorrFact() * modifications.emobCorrFact() ;
b_all += modifications.mehrFzkm() * amounts.co2_per_pkwkm * b_per_co2 * modifications.co2Price() / 145 * modifications.discountCorrFact() * modifications.emobCorrFact() ;
prn("b after co2 induz", b_all, b_tmp);
}
// prn("b_co2_betrieb", b_all, bb_tmp);
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/tub/vsp/bvwp/computation/Modifications.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public record Modifications(double co2Price, double mehrFzkm, double constructionCostFactor, double nonCo2BenefitsFactor) {
//TODO nonCo2BenefitsFactor is not used in the codebase
public record Modifications(double co2Price, double mehrFzkm, double constructionCostFactor, double discountCorrFact, double emobCorrFact) {

private static final Logger log = LogManager.getLogger(Modifications.class);
public static final double co2PriceBVWP = 145.;
public static final double co2Price700 = 642.;
// (700 sind die Klimakosten von in 2030 erzeugten CO2-Emissionen, zum Preisstand 2020. Rückgerechnet nach 2012 führt das zu 642.)
public static final double co2Price2000 = 2000.*642/700;
/**
* Wie {@link #co2Price700}, aber mit 1% Zeitpräferenzrate.
*/
Expand All @@ -28,7 +28,7 @@ public record Modifications(double co2Price, double mehrFzkm, double constructio
}
}

public static final Modifications NO_CHANGE = new Modifications(co2PriceBVWP, 0., 1, 1. );
public static final Modifications NO_CHANGE = new Modifications(co2PriceBVWP, 0., 1, 1, 1. );

@Override public String toString() {
return "[co2Price=" + co2Price + "; mehrFzkm=" + mehrFzkm + "]";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ public Double calculateNkv( Modifications modifications ) {
@Deprecated // use instance approach
public static Double calculateNkv(Modifications modifications, RailBaseDataContainer railBaseDataContainer) {
assert modifications.mehrFzkm() == 0;
assert modifications.nonCo2BenefitsFactor() == 1.;

double baukosten_MioEur = railBaseDataContainer.getCostBenefitAnalysis().getCost().overallCosts() * modifications.constructionCostFactor();
double benefit_MioEur = railBaseDataContainer.getCostBenefitAnalysis().getOverallBenefit().overall();
Expand Down
26 changes: 15 additions & 11 deletions src/main/java/org/tub/vsp/bvwp/data/Headers.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public final class Headers{
/**
* Mehrverkehr auf Pkw, den das Projekt erzeugt, laut PRINS.
*/
public static final String ADDTL_PKWKM_ORIG = ADDTL_PKWKM + " aus PRINS";
public static final String ADDTL_PKWKM_ORIG = ADDTL_PKWKM + " in PRINS";
public static final String ADDTL_PKWKM_FROM_TTIME = ADDTL_PKWKM + " aus Reisezeitgewinnen";
public static final String ADDTL_PKWKM_FROM_TTIME_DIFF = "addtl_pkwkm_from_ttime_diff";

Expand All @@ -35,7 +35,7 @@ public final class Headers{
*
* @see org.tub.vsp.bvwp.data.type.Bautyp
*/
public static final String BAUTYP = "bautyp";
public static final String BAUTYP = "Bautyp";
/**
* Die (vermutlich negativen) Nutzenbeiträge ("benefits") durch CO2 sowie durch Emissionen, die auf CO2 umgerechnet werden können. (??)
*/
Expand Down Expand Up @@ -63,11 +63,11 @@ public final class Headers{
/**
* Originale Investitionskosten.
*/
public static final String INVCOST_ORIG = "investmentCost_orig";
public static final String INVCOST_ORIG = "Inv.kosten BVWP 2030";
/**
* Investitionskosten nach Berechnung der TUD
*/
public static final String INVCOST_TUD = "Investitionskosten neu";
public static final String INVCOST_TUD = "Inv.kosten neu";
/**
* Umrechnung von Emissionen (welchen??) in CO2-Äquivalente.
*/
Expand All @@ -89,7 +89,7 @@ public final class Headers{
/**
* min( 5 , {@link #NKV_ORIG} )
*/
public static final String NKV_ORIG = "NKV aus PRINS";
public static final String NKV_ORIG = "NKV BVWP 2030";
/**
* NKV bei erhöhtem CO2-Preis (welchem?).
*/
Expand All @@ -105,7 +105,7 @@ public final class Headers{
*/
public static final String NKV_EL03 = "NKV_el03";
public static final String NKV_EL03_CAPPED5 = NKV_EL03 + "_capped5";
public static final String NKV_CARBON700ptpr0 = "NKV mit erhöhten CO2-Kosten";
public static final String NKV_CARBON700ptpr0 = "NKV CO2-Kosten+";
public static final String NKV_CARBON700_CAPPED5 = NKV_CARBON700ptpr0 + "_capped5";
/**
* Neues NKV mit höherem Mehrverkehr, CO2-Preis 215, sowie höheren Investitionskosten.
Expand All @@ -130,14 +130,18 @@ public final class Headers{
* Dies ist da, damit "bubble size" als Funktion der Einstufung geplottet werden kann.
*/
public static final String EINSTUFUNG_AS_NUMBER = "einstufungAsNumber";
public static final String PROJECT_NAME = "project _name";
public static final String PROJECT_NAME = "Projektname";
public static final String VERKEHRSBELASTUNG_PLANFALL = "DTV_Planfall";
public static final String NKV_INVCOSTTUD = "NKV mit erhöhten Baukosten";
public static final String NKV_ELTTIME = "NKV mit erhöhtem Straßenmehrverkehr";
public static final String NKV_INVCOSTTUD = "NKV mit Inv.kosten+";
public static final String NKV_ELTTIME = "NKV mit Straßenmehrverkehr+";
public static final String NKV_ELTTIME_CARBON215_INVCOSTTUD = "NKV_elTtime_carbon215_invcostTud";
public static final String NKV_ELTTIME_CARBON700TPR0_INVCOSTTUD = "NKV mit erhöhtem Straßenmehrverkehr/Baukosten/CO2-Preis";
public static final String NKV_ELTTIME_CARBON700ptpr0 = "NKV mit Straßenmehrverkehr+/CO2-Preis+";
public static final String NKV_ELTTIME_CARBON700ptpr0_INVCOSTTUD = "NKV mit Inv.Kosten+/Straßenmehrverkehr+/CO2-Preis+";
public static final String NKV_ELTTIME_CARBON700ptpr0_EMOB_INVCOSTTUD = "NKV mit Inv.Kosten+/Straßenmehrverkehr+/CO2-Preis+ & EMob";
public static final String NKV_ELTTIME_CARBON2000ptpr0_EMOB_INVCOSTTUD = "NKV mit Inv.Kosten+/Straßenmehrverkehr+/CO2-Preis++ & EMob";
public static final String NProCo2_ELTTIME_CARBON2000ptpr0_EMOB_INVCOSTTUD = "Nutzen_pro_CO2 mit Inv.Kosten+/Straßenmehrverkehr+/CO2-Preis++ & EMob";
public static final String NKV_ELTTIME_CARBON2000_INVCOSTTUD = "NKV_elTtime_carbon2000_invcostTud";
public static final String CO2_ELTTIME = "CO2[mt] bei erhöhem Straßenmehrverkehr";
public static final String CO2_ELTTIME = "CO2[Mt] bei Straßenmehrverkehr+";

public static String capped5Of( String str ) {
int cap=5;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ private void addComputations() {
entries.put(Headers.B_PER_KM, baseDataContainer.getCostBenefitAnalysis().getOverallBenefit().overall() / baseDataContainer.getProjectInformation().getLength() );

entries.put(Headers.NKV_ORIG, NkvCalculatorRail.calculateNkv( NO_CHANGE, baseDataContainer ) );
entries.put(Headers.NKV_CO2, NkvCalculatorRail.calculateNkv(new Modifications(co2Price700, 0., 1, 1.), baseDataContainer));
entries.put(Headers.NKV_CO2_700_EN, NkvCalculatorRail.calculateNkv(new Modifications(co2Price700, 0., 1, 1.), baseDataContainer));
entries.put(Headers.NKV_CARBON700ptpr0, NkvCalculatorRail.calculateNkv(new Modifications(co2Price700, 0., 1, 1.), baseDataContainer ) );
entries.put(Headers.NKV_CO2_2000_EN, NkvCalculatorRail.calculateNkv( new Modifications( 2000 * INFLATION_Factor2022to2012, 0, 1, 1 ), baseDataContainer ) );
entries.put(Headers.NKV_CO2, NkvCalculatorRail.calculateNkv(new Modifications(co2Price700, 0., 1, 1, 1. ), baseDataContainer ) );
entries.put(Headers.NKV_CO2_700_EN, NkvCalculatorRail.calculateNkv(new Modifications(co2Price700, 0., 1, 1, 1. ), baseDataContainer ) );
entries.put(Headers.NKV_CARBON700ptpr0, NkvCalculatorRail.calculateNkv(new Modifications(co2Price700, 0., 1, 1, 1. ), baseDataContainer ) );
entries.put(Headers.NKV_CO2_2000_EN, NkvCalculatorRail.calculateNkv( new Modifications( 2000 * INFLATION_Factor2022to2012, 0, 1, 1, 1. ), baseDataContainer ) );
// entries.put(Headers.NKV_EL03, NkvCalculatorRail.calculateNkv( new Modifications( co2PriceBVWP, addtlFzkmBeyondPrinsEl03, 1, 1. ), baseDataContainer ) );
// entries.put(Headers.NKV_EL03_CARBON215_INVCOSTTUD, NkvCalculatorRail.calculateNkv( new Modifications( co2Price215, addtlFzkmBeyondPrinsEl03, constructionCostFactor, 1. ), baseDataContainer ) );
// entries.put(Headers.NKV_EL03_CARBON700tpr0_INVCOSTTUD, NkvCalculatorRail.calculateNkv( new Modifications( co2Price700, addtlFzkmBeyondPrinsEl03, constructionCostFactor, 1.75 ), baseDataContainer ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,42 +81,56 @@ private void addComputations() {
// return;
// }

final double fact = 1.;
final double discountCorrFact = 1.14;
// ptpr0 = stpr 1%. Diskontierung 1% statt 1.7% über Jahre 0 bis 42 bedeutet 1.14 x so viel Nutzen. Vgl. prins.mw. Könnte man ordentlicher machen.
// dennoch 1 gewählt, weil wir nach neuester Überlegung co2 um diesen Faktor nach oben schieben müssen. Machen wir jetzt aber auch nicht, wg. Elektromobilität.

// Nach neuerer Überlegung lassen wir alles andere bei 1.7% , und schieben nur co2 um diesen Faktor nach oben.

final double emobCorrFact = 0.1;

NkvCalculator nkvCalculator = new NkvCalculator( streetBaseData );

entries.put(Headers.NKV_ORIG, nkvCalculator.calculateNkv( NO_CHANGE ) );
entries.put(Headers.NKV_CO2, nkvCalculator.calculateNkv( new Modifications( co2Price700, 0., 1, fact ) ) );
entries.put(Headers.NKV_CO2_700_EN, nkvCalculator.calculateNkv( new Modifications( co2Price700, 0., 1, fact ) ) );
entries.put(Headers.NKV_CARBON700ptpr0, nkvCalculator.calculateNkv( new Modifications( co2Price700, 0., 1, fact ) ) );
entries.put(Headers.NKV_CO2_2000_EN, nkvCalculator.calculateNkv( new Modifications( 2000 * INFLATION_Factor2022to2012, 0, 1, 1. ) ) );
entries.put(Headers.NKV_EL03, nkvCalculator.calculateNkv( new Modifications( co2PriceBVWP, addtlFzkmBeyondPrinsEl03, 1, 1. ) ) );
entries.put(Headers.NKV_EL03_CARBON215_INVCOSTTUD, nkvCalculator.calculateNkv( new Modifications( co2Price215, addtlFzkmBeyondPrinsEl03, constructionCostFactor, 1. ) ) );
entries.put(Headers.NKV_EL03_CARBON700ptpr0_INVCOSTTUD, nkvCalculator.calculateNkv( new Modifications( co2Price700, addtlFzkmBeyondPrinsEl03, constructionCostFactor, fact ) ) );
entries.put(Headers.NKV_EL03_CARBON700ptpr0, nkvCalculator.calculateNkv( new Modifications( co2Price700, addtlFzkmBeyondPrinsEl03, 1., fact ) ) );
entries.put(Headers.NKV_CO2, nkvCalculator.calculateNkv( new Modifications( co2Price700, 0., 1, 1, 1. ) ) );
entries.put(Headers.NKV_CO2_700_EN, nkvCalculator.calculateNkv( new Modifications( co2Price700, 0., 1, 1, 1. ) ) );
entries.put(Headers.NKV_CO2_2000_EN, nkvCalculator.calculateNkv( new Modifications( 2000 * INFLATION_Factor2022to2012, 0, 1, 1, 1. ) ) );
entries.put(Headers.NKV_EL03, nkvCalculator.calculateNkv( new Modifications( co2PriceBVWP, addtlFzkmBeyondPrinsEl03, 1, 1, 1. ) ) );
// entries.put(Headers.NKV_EL03_CARBON215_INVCOSTTUD, nkvCalculator.calculateNkv( new Modifications( co2Price215, addtlFzkmBeyondPrinsEl03, constructionCostFactor ) ) );
// entries.put(Headers.NKV_EL03_CARBON700ptpr0_INVCOSTTUD, nkvCalculator.calculateNkv( new Modifications( discountCorr*co2Price700, addtlFzkmBeyondPrinsEl03, constructionCostFactor ) ) );
// entries.put(Headers.NKV_EL03_CARBON700ptpr0, nkvCalculator.calculateNkv( new Modifications( discountCorr*co2Price700, addtlFzkmBeyondPrinsEl03, 1. ) ) );
// entries.put(Headers.NKV_EL03_CO2_INVCOST50, NkvCalculator.calculateNkv( new Modifications( co2Price700, addtlFzkmBeyondPrinsEl03, constructionCostFactor ), streetBaseData ) );
//
entries.put(Headers.ADDTL_PKWKM_EL03, addtlFzkmFromElasticity03 );
// entries.put(Headers.CO2_COST_ORIG, Math.max( 1., nkvCalculator.calculateCost_CO2( NO_CHANGE, streetBaseData ) ) );
entries.put(Headers.CO2_COST_EL03, Math.max( 1., nkvCalculator.calculateCo2_t( new Modifications( co2PriceBVWP, addtlFzkmBeyondPrinsEl03, 1, 1. ) ) ) );
// entries.put(Headers.CO2_COST_EL03, Math.max( 1., nkvCalculator.calculateCo2_t( new Modifications( co2PriceBVWP, addtlFzkmBeyondPrinsEl03, 1 ) ) ) );
// ("max(1,...)" so that they become visible on logplot. find other solution!
entries.put(Headers.INVCOST_TUD, this.constructionCostTud );

double AVERAGE_SPEED_OF_ADDITIONAL_TRAVEL = 50; // km/h
double addtlFzkmFromTtime = - streetBaseData.getPhysicalEffect().getVehicleHours().overall() * AVERAGE_SPEED_OF_ADDITIONAL_TRAVEL;
entries.put( Headers.ADDTL_PKWKM_FROM_TTIME, addtlFzkmFromTtime );

entries.put( Headers.NKV_INVCOSTTUD, nkvCalculator.calculateNkv( new Modifications( co2PriceBVWP, 0., constructionCostFactor, 1. ) ) );
entries.put( Headers.NKV_ELTTIME, nkvCalculator.calculateNkv( new Modifications( co2PriceBVWP, addtlFzkmFromTtime, 1, 1. ) ) );
entries.put( Headers.NKV_ELTTIME_CARBON215_INVCOSTTUD, nkvCalculator.calculateNkv( new Modifications( co2Price215, addtlFzkmFromTtime, constructionCostFactor, 1. ) ) );
entries.put( Headers.NKV_ELTTIME_CARBON700TPR0_INVCOSTTUD, nkvCalculator.calculateNkv( new Modifications( co2Price700, addtlFzkmFromTtime, constructionCostFactor, 1. ) ) );
// Beiträge einzeln:
entries.put( Headers.NKV_INVCOSTTUD, nkvCalculator.calculateNkv( new Modifications( co2PriceBVWP, 0., constructionCostFactor, 1, 1. ) ) );
entries.put( Headers.NKV_ELTTIME, nkvCalculator.calculateNkv( new Modifications( co2PriceBVWP, addtlFzkmFromTtime, 1, 1, 1. ) ) );
entries.put(Headers.NKV_CARBON700ptpr0, nkvCalculator.calculateNkv( new Modifications( co2Price700, 0., 1, discountCorrFact, 1. ) ) );

// Induz. Strassenmehrverkehr und CO2-Preis kombiniert:
entries.put( Headers.NKV_ELTTIME_CARBON700ptpr0, nkvCalculator.calculateNkv( new Modifications( co2Price700, addtlFzkmFromTtime, 1., discountCorrFact, 1. )) );

// ... + Investitionskosten:
entries.put( Headers.NKV_ELTTIME_CARBON700ptpr0_INVCOSTTUD, nkvCalculator.calculateNkv( new Modifications( co2Price700, addtlFzkmFromTtime, constructionCostFactor, discountCorrFact, 1. ) ) );

// ... + eMob:
entries.put( Headers.NKV_ELTTIME_CARBON700ptpr0_EMOB_INVCOSTTUD, nkvCalculator.calculateNkv( new Modifications( co2Price700, addtlFzkmFromTtime, constructionCostFactor, discountCorrFact, emobCorrFact ) ) );

entries.put( Headers.NKV_ELTTIME_CARBON215_INVCOSTTUD, nkvCalculator.calculateNkv( new Modifications( co2Price215, addtlFzkmFromTtime, constructionCostFactor, 1, 1. ) ) );
entries.put( Headers.NKV_ELTTIME_CARBON2000ptpr0_EMOB_INVCOSTTUD, nkvCalculator.calculateNkv( new Modifications( co2Price2000, addtlFzkmFromTtime, constructionCostFactor, discountCorrFact, emobCorrFact ) ) );
// for the 1.15 see "barwert.mw". kai, jun'24
entries.put(Headers.CO2_ELTTIME, nkvCalculator.calculateCo2_t( new Modifications( co2PriceBVWP, addtlFzkmFromTtime, 1, 1. ) ) );
entries.put(Headers.CO2_ELTTIME, nkvCalculator.calculateCo2_t( new Modifications( co2PriceBVWP, addtlFzkmFromTtime, 1, 1, 1. ) ) );

entries.put( Headers.NKV_ELTTIME_CARBON2000_INVCOSTTUD, nkvCalculator.calculateNkv( new Modifications( 2000 * INFLATION_Factor2022to2012, addtlFzkmFromTtime, constructionCostFactor, fact ) ) );
entries.put( Headers.NKV_ELTTIME_CARBON2000_INVCOSTTUD, nkvCalculator.calculateNkv(
new Modifications( 2000 * INFLATION_Factor2022to2012, addtlFzkmFromTtime, constructionCostFactor, 1, 1. ) ) );

if ( streetBaseData.getProjectInformation().getProjectNumber().contains("A1-G50-NI" )) {
this.remarks.add("Eher geringer Benefit pro km ... erzeugt dann ueber die El pro km relativ viel Verkehr " +
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/org/tub/vsp/bvwp/plot/MultiPlotUtils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package org.tub.vsp.bvwp.plot;

import tech.tablesaw.plotly.components.Figure;

import java.util.Map;

/**
* Bausteine, die für die Erstellung der Multiplots benötigt werden.
* Für Beispiele zum Erstellen von Plots siehe {@link MultiPlotExample}
Expand Down Expand Up @@ -38,4 +42,36 @@ public static String pageTop(){

public static final String pageBottom = "</body>" + System.lineSeparator() + "</html>";

public static String createPageV2( Map<String,Figure> figures ) {
StringBuilder result = new StringBuilder( "<html>" + System.lineSeparator()
+ "<head>" + System.lineSeparator()
+ " <title>Multi-plot test</title>" + System.lineSeparator()
+ " <script src=\"https://cdn.plot.ly/plotly-latest.min.js\"></script>" + System.lineSeparator()
+ "</head>" + System.lineSeparator()
+ "<body>" + System.lineSeparator()
+ "<h1>Part A</h1>" + System.lineSeparator() );

// append the html that references each individual plot:
{
int ii = 0;
for( Map.Entry<String, Figure> entry : figures.entrySet() ){
result.append( entry.getKey() ).append( "<div id='plot" ).append( ii ).append( "'>" ).append( System.lineSeparator() );
ii++;
}
}

// append the figures themselves:
{
int ii = 0;
for( Figure figure : figures.values() ){
result.append( figure.asJavascript( "plot" + ii ) ).append( System.lineSeparator() );
ii++;
}
}

// terminating lines:
result.append( "</body>" ).append( System.lineSeparator() ).append( "</html>" );

return result.toString();
}
}
Loading

0 comments on commit fd99dc2

Please sign in to comment.