Skip to content

Commit 93620a2

Browse files
committed
2 parents 8af1b66 + d61f07d commit 93620a2

15 files changed

+99
-37
lines changed

src/main/java/net/finmath/climate/models/dice/DICEModel.java

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package net.finmath.climate.models.dice;
22

33
import java.util.Arrays;
4+
import java.util.Map;
45
import java.util.function.DoubleUnaryOperator;
6+
import java.util.function.Function;
7+
import java.util.function.IntToDoubleFunction;
8+
import java.util.function.Predicate;
59
import java.util.function.UnaryOperator;
610
import java.util.logging.Logger;
711

@@ -56,7 +60,15 @@ public class DICEModel implements ClimateModel {
5660
private double[] welfare;
5761
private double[] value;
5862

59-
public DICEModel(TimeDiscretization timeDiscretization, UnaryOperator<Double> abatementFunction, UnaryOperator<Double> savingsRateFunction, double discountRate) {
63+
/**
64+
*
65+
* @param timeDiscretization
66+
* @param abatementFunction
67+
* @param savingsRateFunction
68+
* @param discountRate
69+
* @param modelProperties A key value map of optional model properties or parameters.
70+
*/
71+
public DICEModel(TimeDiscretization timeDiscretization, UnaryOperator<Double> abatementFunction, UnaryOperator<Double> savingsRateFunction, double discountRate, Map<String, Object> modelProperties) {
6072
super();
6173
this.timeDiscretization = timeDiscretization;
6274
this.abatementFunction = abatementFunction;
@@ -77,14 +89,23 @@ public DICEModel(TimeDiscretization timeDiscretization, UnaryOperator<Double> ab
7789
welfare = new double[numberOfTimes];
7890
value = new double[numberOfTimes];
7991

80-
this.init();
92+
this.init(modelProperties);
93+
}
94+
95+
public DICEModel(TimeDiscretization timeDiscretization, UnaryOperator<Double> abatementFunction, UnaryOperator<Double> savingsRateFunction, double discountRate) {
96+
this(timeDiscretization, abatementFunction, savingsRateFunction, discountRate, Map.of());
8197
}
8298

8399
public DICEModel(TimeDiscretization timeDiscretization, UnaryOperator<Double> abatementFunction) {
84100
this(timeDiscretization, abatementFunction, t -> 0.259029014481802, 0.03);
85101
}
86102

87-
private void init() {
103+
private void init(Map<String, Object> modelProperties) {
104+
105+
Predicate<Integer> isTimeIndexToShift = (Predicate<Integer>) modelProperties.getOrDefault("isTimeIndexToShift", (Predicate<Integer>) i -> true);
106+
double initialEmissionShift = (double) modelProperties.getOrDefault("initialEmissionShift", 0.0);
107+
double initialConsumptionShift = (double) modelProperties.getOrDefault("initialConsumptionShift", 0.0);
108+
88109
final double timeStep = timeDiscretization.getTimeStep(0);
89110

90111
/*
@@ -109,12 +130,13 @@ private void init() {
109130
final DoubleUnaryOperator damageFunction = new DamageFromTemperature();
110131

111132
final EmissionIndustrialIntensityFunction emissionIndustrialIntensityFunction = new EmissionIndustrialIntensityFunction();
112-
final EmissionExternalFunction emissionExternalFunction = new EmissionExternalFunction();
133+
134+
final Function<Double, Double> emissionExternalFunction = new EmissionExternalFunction();
113135

114136
final EvolutionOfCarbonConcentration evolutionOfCarbonConcentration = new EvolutionOfCarbonConcentration(timeDiscretization);
115137

116138
final ForcingFunction forcingFunction = new ForcingFunction();
117-
final double forcingExternal = 1.0/5.0; // per year
139+
final double forcingExternal = 1.0;
118140

119141
final EvolutionOfTemperature evolutionOfTemperature = new EvolutionOfTemperature(timeDiscretization);
120142

@@ -175,6 +197,10 @@ private void init() {
175197
double emissionIndustrial = emissionIndustrialIntensityFunction.apply(time) * gdp[timeIndex];
176198
double emissionExternal = emissionExternalFunction.apply(time);
177199
emission[timeIndex] = (1 - abatement[timeIndex])/(1-abatement[0]) * emissionIndustrial + emissionExternal;
200+
201+
// Allow for an external shift to the emissions (e.g. to calculate SCC).
202+
emission[timeIndex] += isTimeIndexToShift.test(timeIndex) ? initialEmissionShift : 0.0;
203+
178204
carbonConcentration[timeIndex+1] = evolutionOfCarbonConcentration.apply(timeIndex, carbonConcentration[timeIndex], emission[timeIndex]);
179205

180206
/*
@@ -204,9 +230,12 @@ private void init() {
204230
// Constant from the original model - in the original model this is a time varying control variable.
205231
double savingsRate = savingsRateFunction.apply(time); //0.259029014481802;
206232

207-
double consumption = (1-savingsRate) * gdpNet;
233+
double consumption = (1-savingsRate) * gdpNet;
208234
double investment = savingsRate * gdpNet;
209235

236+
// Allow for an external shift to the emissions (e.g. to calculate SCC).
237+
consumption += isTimeIndexToShift.test(timeIndex) ? initialConsumptionShift : 0.0;
238+
210239
capital[timeIndex+1] = evolutionOfCapital.apply(timeIndex).apply(capital[timeIndex], investment);
211240

212241
/*

src/main/java/net/finmath/climate/models/dice/submodels/AbatementCostFunction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class AbatementCostFunction implements BiFunction<Double, Double, Double>
3232
*
3333
* @param backstopPriceInitial USD per ton CO2 to abate 100% per year.
3434
* @param backstopRate Annual rate by which the price declines.
35-
* @param abatementExponent Exponent for &mu;
35+
* @param abatementExponent Exponent for &mu; (sometimes called theta_2)
3636
*/
3737
public AbatementCostFunction(double backstopPriceInitial, double backstopRate, double abatementExponent) {
3838
super();
@@ -49,7 +49,7 @@ public AbatementCostFunction() {
4949
@Override
5050
public Double apply(Double time, Double abatement) {
5151
final double backstopPrice = backstopPriceInitial * Math.exp(-backstopRate * time);
52-
final double abatementCost = backstopPrice * Math.pow(abatement , abatementExponent)/abatementExponent;
52+
final double abatementCost = backstopPrice * Math.pow(abatement, abatementExponent)/abatementExponent;
5353

5454
return abatementCost;
5555
}

src/main/java/net/finmath/climate/models/dice/submodels/CarbonConcentration3DScalar.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package net.finmath.climate.models.dice.submodels;
22

3+
import org.apache.commons.lang3.Validate;
4+
35
import net.finmath.climate.models.CarbonConcentration;
46
import net.finmath.stochastic.RandomVariable;
57
import net.finmath.stochastic.Scalar;
68

79
/**
8-
* State vector representing carbon concentration.
10+
* State vector representing carbon concentration in units of GtC.
911
*
1012
* @author Christian Fries
1113
*/
@@ -17,6 +19,9 @@ public class CarbonConcentration3DScalar implements CarbonConcentration {
1719

1820
public CarbonConcentration3DScalar(double carbonConcentrationInAtmosphere, double carbonConcentrationInShallowOcean, double carbonConcentrationInLowerOcean) {
1921
super();
22+
Validate.isTrue(carbonConcentrationInAtmosphere >= 0, "carbonConcentrationInAtmosphere must not be negative.", carbonConcentrationInAtmosphere);
23+
Validate.isTrue(carbonConcentrationInShallowOcean >= 0, "carbonConcentrationInShallowOcean must not be negative.", carbonConcentrationInShallowOcean);
24+
Validate.isTrue(carbonConcentrationInLowerOcean >= 0, "carbonConcentrationInLowerOcean must not be negative.", carbonConcentrationInLowerOcean);
2025
this.carbonConcentrationInAtmosphere = carbonConcentrationInAtmosphere;
2126
this.carbonConcentrationInShallowOcean = carbonConcentrationInShallowOcean;
2227
this.carbonConcentrationInLowerOcean = carbonConcentrationInLowerOcean;
@@ -26,6 +31,9 @@ public CarbonConcentration3DScalar(double[] carbonConcentration) {
2631
this(carbonConcentration[0], carbonConcentration[1], carbonConcentration[2]);
2732
}
2833

34+
/**
35+
* Create a state vector with 851 GtC in atmosphere, 460 GtC in shallow ocean, 1740 in lower ocean.
36+
*/
2937
public CarbonConcentration3DScalar() {
3038
this(851, 460, 1740); // GtC
3139
}

src/main/java/net/finmath/climate/models/dice/submodels/EmissionIndustrialIntensityFunction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class EmissionIndustrialIntensityFunction implements Function<Double, Dou
2424

2525
private final double emissionIntensityInitial; // sigma0;
2626
private final double emissionIntensityRateInitial; // = 0.0152; // -g // per year
27-
private final double emissionIntensityRateDecay; // = 0.001; // -d // per year
27+
private final double emissionIntensityRateDecay; // exp decay rate corresponding to annual -0.001; // -d // per year
2828

2929
/**
3030
*

src/main/java/net/finmath/climate/models/dice/submodels/EvolutionOfTemperature.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,24 @@
99

1010
/**
1111
*
12-
* The evolution of the temperature \( \mathrm{d}T(t) = \left( \Gamma_{T} T(t) + F(t) \right) \mathrm{d}t \).
12+
* The evolution of the temperature \( \mathrm{d}T(t) = \left( \Gamma_{T} T(t) + \xi \cdot F(t) \right) \mathrm{d}t \).
1313
*
1414
* The unit of \( T \) is K (Kelvin).
1515
*
16-
* The evolution is modelled as \( \mathrm{d}T(t) = \left( \Gamma_{T} T(t) + F(t) \right) \mathrm{d}t \).
16+
* This is a function of timeIndex, previous temperature and forcing.
17+
*
18+
* The evolution is modelled as \( \mathrm{d}T(t) = \left( \Gamma_{T} T(t) + \xi \cdot F(t) \right) \mathrm{d}t \).
1719
* With the given {@link TimeDiscretization} it is approximated via an Euler-step
1820
* \(
19-
* T(t_{i+1}) = \Phi T(t_{i}) + (forcingToTemp \cdot (forcing, 0, 0) \cdot \Delta t_{i}
21+
* T(t_{i+1}) = \Phi T(t_{i}) + (forcingToTemp \cdot (forcing, 0) \cdot \Delta t_{i}
2022
* \)
2123
* where \( \Phi = (1 + \Gamma_{T} \Delta t_{i}) \).
2224
*
2325
* @author Christian Fries
2426
*/
2527
public class EvolutionOfTemperature implements TriFunction<Integer, Temperature2DScalar, Double, Temperature2DScalar> {
2628

27-
private static double forcingToTempDefault = 0.1005; // sometimes called xi1 or c1 // TODO check role of time step
29+
private static double forcingToTemp5YDefault = 0.1005; // (climate sensitivity) sometimes called xi1 or c1 (original parameter was per 5 year)
2830

2931
private static double[][] transitionMatrix5YDefault;
3032
static {
@@ -33,8 +35,8 @@ public class EvolutionOfTemperature implements TriFunction<Integer, Temperature2
3335
final double c3 = 0.088; // Transfer coefficient upper to lower stratum
3436
final double c4 = 0.025;
3537

36-
final double phi11 = 1-forcingToTempDefault*((fco22x/t2xco2) + c3);
37-
final double phi12 = forcingToTempDefault*c3;
38+
final double phi11 = 1-forcingToTemp5YDefault*((fco22x/t2xco2) + c3);
39+
final double phi12 = forcingToTemp5YDefault*c3;
3840
final double phi21 = c4;
3941
final double phi22 = 1-c4;
4042

@@ -48,7 +50,7 @@ public class EvolutionOfTemperature implements TriFunction<Integer, Temperature2
4850
/**
4951
* @param timeDiscretization The time discretization.
5052
* @param transitionMatrices Transition matrix \( \Phi \) for each time step.
51-
* @param forcingToTemp The scaling coefficient for the external forcing.
53+
* @param forcingToTemp The scaling coefficient for the external forcing to temperature change per year (this is per 1Y).
5254
*/
5355
public EvolutionOfTemperature(TimeDiscretization timeDiscretization, Function<Integer, double[][]> transitionMatrices, double forcingToTemp) {
5456
super();
@@ -61,7 +63,7 @@ public EvolutionOfTemperature(TimeDiscretization timeDiscretization) {
6163
Function<Integer, Double> timeSteps = ((Integer timeIndex) -> { return timeDiscretization.getTimeStep(timeIndex); });
6264
this.timeDiscretization = timeDiscretization;
6365
transitionMatrices = timeSteps.andThen(Cached.<Double, double[][]>of(timeStep -> timeStep == 5.0 ? transitionMatrix5YDefault : LinearAlgebra.matrixPow(transitionMatrix5YDefault, (Double)timeStep/5.0)));
64-
this.forcingToTemp = forcingToTempDefault;
66+
this.forcingToTemp = forcingToTemp5YDefault/5; // Rescale to per 1 Y
6567
}
6668

6769
@Override

src/main/java/net/finmath/climate/models/dice/submodels/ForcingFunction.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
*/
1010
public class ForcingFunction implements BiFunction<CarbonConcentration3DScalar, Double, Double> {
1111

12+
// Parameters of the orignal model
1213
private final double carbonConcentrationBase = 580;
13-
private final double forcingPerCarbonDoubling = 3.6813/5; // Parameter in original model was per 5 year
14+
private final double forcingPerCarbonDoubling = 3.6813;
1415

1516
@Override
1617
public Double apply(CarbonConcentration3DScalar carbonConcentration, Double forcingExternal) {

src/main/java/net/finmath/climate/models/dice/submodels/Temperature2DScalar.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import net.finmath.stochastic.Scalar;
66

77
/**
8-
* State vector representing temperature above pre-industrial level.
8+
* State vector representing temperature above pre-industrial level in Kelvin (K).
99
*
1010
* @author Christian Fries
1111
*/
@@ -14,6 +14,12 @@ public class Temperature2DScalar implements Temperature {
1414
private final double temperatureOfAtmosphere;
1515
private final double temperatureOfLandAndOcean;
1616

17+
/**
18+
* Create a temperature vector.
19+
*
20+
* @param temperatureOfAtmosphere Temperature over pre-industrial of the atmosphere.
21+
* @param temperatureOfLandAndOcean Temperature over pre-industrial of land and ocean.
22+
*/
1723
public Temperature2DScalar(double temperatureOfAtmosphere, double temperatureOfLandAndOcean) {
1824
super();
1925
this.temperatureOfAtmosphere = temperatureOfAtmosphere;

src/main/java/net/finmath/equities/marketdata/FlatYieldCurve.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ public FlatYieldCurve(
2525
this.rate = rate;
2626
this.dayCounter = dayCounter;
2727
}
28+
29+
public FlatYieldCurve rollToDate(LocalDate date)
30+
{
31+
return new FlatYieldCurve(date, rate, dayCounter);
32+
}
2833

2934
public double getRate(double maturity)
3035
{

src/main/java/net/finmath/equities/models/BuehlerDividendForwardStructure.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public BuehlerDividendForwardStructure cloneWithNewDate(LocalDate newDate)
6565
return new BuehlerDividendForwardStructure(
6666
newDate,
6767
this.spot,
68-
this.repoCurve,
68+
this.repoCurve.rollToDate(newDate),
6969
this.dividendStream,
7070
this.dayCounter);
7171
}

src/main/java/net/finmath/equities/pricer/AnalyticOptionValuation.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ public double calculate(
5858
final var ttm = dcc.getDaycountFraction(forwardStructure.getValuationDate(), expiryDate);
5959
final var forward = forwardStructure.getForward(expiryDate);
6060
final var discountFactor = discountCurve.getDiscountFactor(expiryDate);
61-
//var repoRate = forwardStructure.getRepoCurve().getRate(expiryDate);
62-
final var repoRate = discountCurve.getRate(expiryDate);
61+
final var discountRate = discountCurve.getRate(expiryDate);
6362
final var adjustedForward = forwardStructure.getDividendAdjustedStrike(forward, expiryDate);
6463
final var adjustedStrike = forwardStructure.getDividendAdjustedStrike(option.getStrike(), expiryDate);
6564
final var volatility = volaSurface.getVolatility(
@@ -111,14 +110,7 @@ public double calculate(
111110
volatility,
112111
option.isCallOption(),
113112
discountFactor * adjustedForward,
114-
repoRate)
115-
- repoRate * Black76Model.optionPrice(
116-
1.0,
117-
adjustedStrike / adjustedForward,
118-
ttm,
119-
volatility,
120-
option.isCallOption(),
121-
discountFactor * adjustedForward);
113+
discountRate);
122114
default:
123115
throw new NotImplementedException("Calculation for " + calcType + " not implemented yet.");
124116
}

src/main/java/net/finmath/equities/pricer/PdeOptionValuation.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,8 @@ else if (i == j - 1 || i == j + 1)
401401
* dFdS / dFdX;
402402
final var gamma = discountFactor * (prices.getEntry(spotIndex + 1) + prices.getEntry(spotIndex - 1)
403403
- 2 * prices.getEntry(spotIndex)) / spaceStepSq * dFdS * dFdS / dFdX / dFdX;
404-
final var theta = (discountFactor * lastAtmPrice - price) / dt;
404+
final var discountFactorTheta = discountCurve.getDiscountFactor(expiryTime - dt);
405+
final var theta = (discountFactorTheta * lastAtmPrice - price) / dt;
405406
return new double[] {price, delta, gamma, theta};
406407
}
407408
else

src/main/java/net/finmath/finitedifference/models/FDMConstantElasticityOfVarianceModel.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,6 @@ public double getNumStandardDeviations() {
105105
return numStandardDeviations;
106106
}
107107

108-
/* (non-Javadoc)
109-
* @see net.finmath.finitedifference.models.FiniteDifference1DModel#valueOptionWithThetaMethod(net.finmath.finitedifference.products.FDMEuropeanCallOption, double)
110-
*/
111108
@Override
112109
public double[][] getValue(double evaluationTime, double time, DoubleUnaryOperator values, FiniteDifference1DBoundary boundary) {
113110
final FDMThetaMethod solver = new FDMThetaMethod(this, boundary, time, center, theta);

src/main/java/net/finmath/time/TimeDiscretization.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,28 @@ public interface TimeDiscretization extends Iterable<Double> {
7676
*/
7777
int getTimeIndexNearestGreaterOrEqual(double time);
7878

79+
/**
80+
* Returns the first time in the time discretization.
81+
*
82+
* @return The first time in the time discretization.
83+
*/
84+
default double getFirstTime() {
85+
return getTime(0);
86+
}
87+
88+
/**
89+
* Returns the last time in the time discretization.
90+
*
91+
* @return The last time in the time discretization.
92+
*/
93+
default double getLastTime() {
94+
return getTime(getNumberOfTimes()-1);
95+
}
96+
97+
7998
/**
8099
* Return a clone of this time discretization as <code>double[]</code>.
100+
*
81101
* @return The time discretization as <code>double[]</code>
82102
*/
83103
double[] getAsDoubleArray();

src/test/java/net/finmath/equities/AnalyticOptionValuationTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,9 @@ public void Test_sensis() throws CalculationException
133133
final var anaTheta = pricer.getTheta(option, fwdStructure, discountCurve, flatVol);
134134
final var thetaDate = valDate.plusDays(1);
135135
final var thetaSpot = fwdStructure.getForward(thetaDate);
136+
final var thetaCurve = discountCurve.rollToDate(thetaDate);
136137
final var shiftedFwdStructure = fwdStructure.cloneWithNewSpot(thetaSpot).cloneWithNewDate(thetaDate);
137-
final var priceTheta = pricer.getPrice(option, shiftedFwdStructure, discountCurve, flatVol);
138+
final var priceTheta = pricer.getPrice(option, shiftedFwdStructure, thetaCurve, flatVol);
138139
final var fdTheta = (priceTheta - price) / dcc.getDaycountFraction(valDate, thetaDate);
139140

140141

src/test/java/net/finmath/equities/PdeOptionPricerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ public void Test_europeanSensis() throws CalculationException
345345
assertEquals("Pde Vega deviates to much from analytic value for " + (isCall ? "Call" : "Put"),
346346
0.0, pdeVega/anaVega - 1.0, 0.02);
347347
assertEquals("Pde Theta deviates to much from analytic value for " + (isCall ? "Call" : "Put"),
348-
0.0, pdeSensis[3]/anaTheta - 1.0, 0.02);
348+
0.0, pdeSensis[3]/anaTheta - 1.0, 0.04);
349349
}
350350
}
351351

0 commit comments

Comments
 (0)