Skip to content

Commit 46cc25f

Browse files
authored
Merge pull request #299 from Elyahu41/fix/spa-nan-zenith-hang
SPACalculator: return NaN instead of running for hours when the target zenith is NaN Thanks @ Elyahu41
2 parents afadabc + f247780 commit 46cc25f

2 files changed

Lines changed: 34 additions & 0 deletions

File tree

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,11 @@ private double getUTCSunRiseSet(LocalDate localDate, GeoLocation geoLocation, do
311311
}
312312
}
313313
double adjustedZenith = adjustZenith(zenith, elevation, localDate);
314+
if (!Double.isFinite(adjustedZenith)) {
315+
// A NaN or infinite zenith (or a NaN / infinite refraction, solar radius or earth radius, which make the adjusted
316+
// zenith NaN) has no crossing, and the bracketing search can't rule out any interval of a NaN function.
317+
return Double.NaN;
318+
}
314319
double riseSet = solveRiseSet(localDate, geoLocation, adjustedZenith, solarEvent);
315320
if (Double.isNaN(riseSet)) {
316321
return Double.NaN;
@@ -654,6 +659,11 @@ private static final class CrossingSearch {
654659
* @return the crossing time, or NaN if the interval holds none
655660
*/
656661
double find(double start, double end, double a, double b) {
662+
if (Double.isNaN(a) || Double.isNaN(b)) {
663+
// No interval of a NaN function can be discarded, so without this the search would split all the way down to
664+
// TIME_TOLERANCE in every branch (about 2^27 evaluations).
665+
return Double.NaN;
666+
}
657667
double width = end - start;
658668
double slope = (b - a) / width;
659669
double slopeError = curvature * width / 2.0 + 2.0 * ROUNDING_ERROR / width;

‎src/test/java/com/kosherjava/zmanim/util/SPACalculatorTest.java‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,4 +322,28 @@ public void defaultsAndSetters() {
322322
public void getCalculatorName() {
323323
assertEquals("NREL Solar Position Algorithm", calculator().getCalculatorName());
324324
}
325+
326+
/**
327+
* A NaN target zenith (from a NaN / infinite zenith, refraction or solar radius, or a negative / NaN earth radius) used to
328+
* make the bracketing rise / set search run for hours. It must return NaN like the other calculators.
329+
*/
330+
@Test(timeout = 10_000)
331+
public void nonFiniteZenithReturnsNaNPromptly() {
332+
GeoLocation location = new GeoLocation("Lakewood, NJ", 40.0721087, -74.2400243, 5017, ZoneId.of("America/New_York"));
333+
LocalDate date = LocalDate.of(2026, 1, 1);
334+
assertTrue(Double.isNaN(calculator().getUTCSunrise(date, location, Double.POSITIVE_INFINITY, false)));
335+
assertTrue(Double.isNaN(calculator().getUTCSunset(date, location, Double.NaN, false)));
336+
337+
SPACalculator negativeEarthRadius = calculator();
338+
negativeEarthRadius.setEarthRadius(-6371);
339+
assertTrue(Double.isNaN(negativeEarthRadius.getUTCSunrise(date, location, 90.0, true)));
340+
341+
SPACalculator nanRefraction = calculator();
342+
nanRefraction.setRefraction(Double.NaN);
343+
assertTrue(Double.isNaN(nanRefraction.getUTCSunset(date, location, 90.0, false)));
344+
345+
SPACalculator infiniteSolarRadius = calculator();
346+
infiniteSolarRadius.setSolarRadius(Double.POSITIVE_INFINITY);
347+
assertTrue(Double.isNaN(infiniteSolarRadius.getUTCSunrise(date, location, 90.0, false)));
348+
}
325349
}

0 commit comments

Comments
 (0)