Skip to content

Commit 32e514b

Browse files
authored
MeeusCalculator - caching (more significant)
1 parent 07dd98c commit 32e514b

1 file changed

Lines changed: 98 additions & 9 deletions

File tree

‎src/main/java/com/kosherjava/zmanim/util/MeeusCalculator.java‎

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@
6464
*/
6565
public class MeeusCalculator extends AstronomicalCalculator {
6666

67+
/**
68+
* {@inheritDoc} This calculator's results depend only on the arguments and on settings whose setters clear the cache.
69+
*/
70+
@Override
71+
protected boolean isResultCacheable() {
72+
return true;
73+
}
74+
6775
/**
6876
* The <a href="https://en.wikipedia.org/wiki/Julian_day">Julian day</a> of January 1, 2000, known as
6977
* <a href="https://en.wikipedia.org/wiki/Epoch_(astronomy)#J2000">J2000.0</a>.
@@ -112,6 +120,7 @@ public String getCalculatorName() {
112120
*/
113121
public void setApplyDeltaT(boolean applyDeltaT) {
114122
this.applyDeltaT = applyDeltaT;
123+
clearResultCache();
115124
}
116125

117126
/**
@@ -227,15 +236,66 @@ private double getSolarNoonMidnightUTC(double julianDay, double longitude, Solar
227236
* @return The UTC time of sunrise or sunset in minutes. {@link Double#NaN} if the event does not occur (e.g. polar day
228237
* / night).
229238
*/
239+
/**
240+
* The part of a rise / set solution that does not depend on the zenith: the equation of time and the declination at solar noon.
241+
* Every zman of a day needs the same values, so the last ones calculated are kept in {@link #lastNoonSolution} and reused. The
242+
* key is every input they depend on (the Julian day, the longitude and {@link #isApplyDeltaT()}), so a cached value is only ever
243+
* returned for identical inputs, and the results are bit-for-bit identical to calculating them each time. Immutable, so it can be
244+
* shared between threads and clones.
245+
*/
246+
private static final class NoonSolution {
247+
final double julianDay;
248+
final double longitude;
249+
final boolean applyDeltaT;
250+
final double equationOfTime;
251+
final double solarDeclination;
252+
253+
NoonSolution(double julianDay, double longitude, boolean applyDeltaT, double equationOfTime, double solarDeclination) {
254+
this.julianDay = julianDay;
255+
this.longitude = longitude;
256+
this.applyDeltaT = applyDeltaT;
257+
this.equationOfTime = equationOfTime;
258+
this.solarDeclination = solarDeclination;
259+
}
260+
}
261+
262+
/**
263+
* The last {@link NoonSolution} calculated. A single entry (callers almost always move one day at a time), so memory use is fixed
264+
* and nothing accumulates. Swapped atomically as one immutable object, so concurrent callers at most recalculate, and can never see a
265+
* partial or mismatched entry. Not part of {@link #equals(Object)} or {@link #hashCode()}.
266+
*/
267+
private volatile NoonSolution lastNoonSolution;
268+
269+
/**
270+
* Returns the noon solution for the day and longitude, reusing the last one when every input matches.
271+
* @param julianDay the Julian day
272+
* @param longitude the longitude
273+
* @return the noon solution
274+
*/
275+
private NoonSolution getNoonSolution(double julianDay, double longitude) {
276+
boolean applyDeltaTNow = applyDeltaT; // read the setting once, so the key and the calculation use the same value
277+
NoonSolution cached = lastNoonSolution;
278+
if (cached != null && cached.julianDay == julianDay && cached.longitude == longitude && cached.applyDeltaT == applyDeltaTNow) {
279+
return cached;
280+
}
281+
double noonmin = getSolarNoonMidnightUTC(julianDay, longitude, SolarEvent.NOON);
282+
double tnoon = getJulianCenturiesFromJulianDay(julianDay + noonmin / 1440.0);
283+
double[] sun = getSunApparentEclipticCoordinates(tnoon); // shared by the equation of time and the declination
284+
NoonSolution solution = new NoonSolution(julianDay, longitude, applyDeltaTNow, getEquationOfTime(tnoon, sun), getSunDeclination(tnoon, sun));
285+
if (applyDeltaT == applyDeltaTNow) { // only cache if the setting did not change mid calculation
286+
lastNoonSolution = solution;
287+
}
288+
return solution;
289+
}
290+
230291
private double getSunRiseSetUTC(LocalDate localDate, double latitude, double longitude, double zenith,
231292
SolarEvent solarEvent) {
232293
double julianDay = getJulianDay(localDate);
233294

234295
// Use the declination at local solar noon as the starting point, then iterate.
235-
double noonmin = getSolarNoonMidnightUTC(julianDay, longitude, SolarEvent.NOON);
236-
double tnoon = getJulianCenturiesFromJulianDay(julianDay + noonmin / 1440.0);
237-
double equationOfTime = getEquationOfTime(tnoon);
238-
double solarDeclination = getSunDeclination(tnoon);
296+
NoonSolution noon = getNoonSolution(julianDay, longitude); // independent of the zenith, so shared by every zman of the day
297+
double equationOfTime = noon.equationOfTime;
298+
double solarDeclination = noon.solarDeclination;
239299
double hourAngle = getSunHourAngle(latitude, solarDeclination, zenith, solarEvent);
240300
double delta = longitude - Math.toDegrees(hourAngle);
241301
double timeUTC = 720 + (4 * delta) - equationOfTime;
@@ -244,8 +304,9 @@ private double getSunRiseSetUTC(LocalDate localDate, double latitude, double lon
244304
// implementation's two helps convergence at high latitudes).
245305
for (int i = 0; i < 2; i++) {
246306
double newt = getJulianCenturiesFromJulianDay(julianDay + timeUTC / 1440.0);
247-
equationOfTime = getEquationOfTime(newt);
248-
solarDeclination = getSunDeclination(newt);
307+
double[] sun = getSunApparentEclipticCoordinates(newt); // shared by the equation of time and the declination
308+
equationOfTime = getEquationOfTime(newt, sun);
309+
solarDeclination = getSunDeclination(newt, sun);
249310
hourAngle = getSunHourAngle(latitude, solarDeclination, zenith, solarEvent);
250311
if (Double.isNaN(hourAngle)) {
251312
return Double.NaN;
@@ -461,7 +522,16 @@ private static double getDeltaT(double julianDay) {
461522
* @return the sun's apparent declination in degrees.
462523
*/
463524
private static double getSunDeclination(double julianCenturies) {
464-
double[] sun = getSunApparentEclipticCoordinates(julianCenturies); // {lambda, beta} in degrees
525+
return getSunDeclination(julianCenturies, getSunApparentEclipticCoordinates(julianCenturies));
526+
}
527+
528+
/**
529+
* {@link #getSunDeclination(double)} from already calculated apparent ecliptic coordinates.
530+
* @param julianCenturies the Julian centuries (TT)
531+
* @param sun {@link #getSunApparentEclipticCoordinates(double)} for the same time
532+
* @return the declination in degrees
533+
*/
534+
private static double getSunDeclination(double julianCenturies, double[] sun) { // {lambda, beta} in degrees
465535
double lambda = sun[0];
466536
double beta = sun[1];
467537
double epsilon = getTrueObliquity(julianCenturies);
@@ -476,7 +546,16 @@ private static double getSunDeclination(double julianCenturies) {
476546
* @return the sun's apparent right ascension in degrees.
477547
*/
478548
private static double getSunRightAscension(double julianCenturies) {
479-
double[] sun = getSunApparentEclipticCoordinates(julianCenturies);
549+
return getSunRightAscension(julianCenturies, getSunApparentEclipticCoordinates(julianCenturies));
550+
}
551+
552+
/**
553+
* {@link #getSunRightAscension(double)} from already calculated apparent ecliptic coordinates.
554+
* @param julianCenturies the Julian centuries (TT)
555+
* @param sun {@link #getSunApparentEclipticCoordinates(double)} for the same time
556+
* @return the right ascension in degrees
557+
*/
558+
private static double getSunRightAscension(double julianCenturies, double[] sun) {
480559
double lambda = sun[0];
481560
double beta = sun[1];
482561
double epsilon = getTrueObliquity(julianCenturies);
@@ -561,12 +640,22 @@ private static double[] getNutation(double julianCenturies) {
561640
* @return the equation of time in minutes of time.
562641
*/
563642
private static double getEquationOfTime(double julianCenturies) {
643+
return getEquationOfTime(julianCenturies, getSunApparentEclipticCoordinates(julianCenturies));
644+
}
645+
646+
/**
647+
* {@link #getEquationOfTime(double)} from already calculated apparent ecliptic coordinates.
648+
* @param julianCenturies the Julian centuries (TT)
649+
* @param sun {@link #getSunApparentEclipticCoordinates(double)} for the same time
650+
* @return the equation of time in minutes
651+
*/
652+
private static double getEquationOfTime(double julianCenturies, double[] sun) {
564653
double tau = julianCenturies / 10.0;
565654
// Mean longitude of the sun (Meeus 28.1), in degrees.
566655
double l0 = 280.4664567 + tau * (360007.6982779 + tau * (0.03032028
567656
+ tau * (1.0 / 49931.0 - tau * (1.0 / 15300.0 + tau / 2000000.0))));
568657
l0 = (l0 % 360 + 360) % 360;
569-
double alpha = getSunRightAscension(julianCenturies);
658+
double alpha = getSunRightAscension(julianCenturies, sun);
570659
double[] nut = getNutation(julianCenturies);
571660
double epsilon = getTrueObliquity(julianCenturies);
572661
double e = l0 - 0.0057183 - alpha + (nut[0] / 3600.0) * cosDegrees(epsilon); // degrees

0 commit comments

Comments
 (0)