diff --git a/src/wres/pipeline/EvaluationUtilities.java b/src/wres/pipeline/EvaluationUtilities.java index 26b9239202..e37d19c89e 100644 --- a/src/wres/pipeline/EvaluationUtilities.java +++ b/src/wres/pipeline/EvaluationUtilities.java @@ -36,6 +36,7 @@ import wres.config.yaml.components.GeneratedBaselines; import wres.config.yaml.components.ThresholdType; import wres.datamodel.bootstrap.BootstrapUtilities; +import wres.datamodel.time.TimeWindowSlicer; import wres.datamodel.types.Ensemble; import wres.datamodel.MissingValues; import wres.datamodel.pools.Pool; @@ -1416,18 +1417,18 @@ private static ToIntFunction>>> getEnsemb * @param separateMetricsForBaseline whether to generate a filter for baseline pools with separate metrics * @return the filters */ - private static List> getTimeWindowFilters( Set timeWindows, + private static List> getTimeWindowFilters( Set timeWindows, boolean separateMetricsForBaseline ) { List> filters = new ArrayList<>(); - for ( TimeWindow timeWindow : timeWindows ) + for ( TimeWindowOuter timeWindow : timeWindows ) { // Filter for main pools Predicate nextMainFilter = statistics -> statistics.hasPool() && statistics.getPool() .getTimeWindow() - .equals( timeWindow ); + .equals( timeWindow.getTimeWindow() ); filters.add( nextMainFilter ); // Separate metrics for baseline? @@ -1437,7 +1438,7 @@ private static List> getTimeWindowFilters( Set && statistics.hasBaselinePool() && statistics.getBaselinePool() .getTimeWindow() - .equals( timeWindow ); + .equals( timeWindow.getTimeWindow() ); filters.add( nextBaseFilter ); } } @@ -1610,7 +1611,7 @@ private static Map> getSummaryStatisti .separateMetrics(); // Get the time window and threshold filters - Set timeWindows = DeclarationUtilities.getTimeWindows( declaration ); + Set timeWindows = TimeWindowSlicer.getTimeWindows( declaration ); Set thresholds = DeclarationUtilities.getInbandThresholds( declaration ); List timeWindowAndThresholdFilters = EvaluationUtilities.getTimeWindowAndThresholdFilters( timeWindows, @@ -2053,7 +2054,7 @@ private static FeatureGroupFilterAdapter getBaselineFeatureGroupForSummaryStatis * @param clearThresholdValues is true to clear event threshold values, false otherwise * @return the filters */ - private static List getTimeWindowAndThresholdFilters( Set timeWindows, + private static List getTimeWindowAndThresholdFilters( Set timeWindows, Set thresholds, boolean separateMetricsForBaseline, boolean clearThresholdValues ) diff --git a/src/wres/pipeline/pooling/PoolFactory.java b/src/wres/pipeline/pooling/PoolFactory.java index bbdbfc6c29..8f39a6d30f 100644 --- a/src/wres/pipeline/pooling/PoolFactory.java +++ b/src/wres/pipeline/pooling/PoolFactory.java @@ -48,6 +48,7 @@ import wres.config.yaml.components.Season; import wres.config.yaml.components.Source; import wres.config.yaml.components.Values; +import wres.datamodel.time.TimeWindowSlicer; import wres.datamodel.types.Ensemble; import wres.datamodel.types.Ensemble.Labels; import wres.datamodel.MissingValues; @@ -498,17 +499,14 @@ private Map> getTimeWindows( EvaluationDeclar RetrieverFactory eventRetriever ) { // Declared time windows - Set timeWindows = DeclarationUtilities.getTimeWindows( declaration ); + Set timeWindows = TimeWindowSlicer.getTimeWindows( declaration ); // Time windows without event detection if ( Objects.isNull( declaration.eventDetection() ) ) { - Set timeWindowsWrapped = timeWindows.stream() - .map( TimeWindowOuter::of ) - .collect( Collectors.toCollection( TreeSet::new ) ); return featureGroups.stream() .collect( Collectors.toMap( Function.identity(), - g -> timeWindowsWrapped ) ); + g -> timeWindows ) ); } // Time windows based on event detection @@ -524,14 +522,18 @@ private Map> getTimeWindows( EvaluationDeclar Set adjustedEvents = new HashSet<>(); for ( TimeWindowOuter next : events ) { - for ( TimeWindow adjust : timeWindows ) + for ( TimeWindowOuter adjust : timeWindows ) { TimeWindow adjusted = next.getTimeWindow() .toBuilder() - .setEarliestReferenceTime( adjust.getEarliestReferenceTime() ) - .setLatestReferenceTime( adjust.getLatestReferenceTime() ) - .setEarliestLeadDuration( adjust.getEarliestLeadDuration() ) - .setLatestLeadDuration( adjust.getLatestLeadDuration() ) + .setEarliestReferenceTime( adjust.getTimeWindow() + .getEarliestReferenceTime() ) + .setLatestReferenceTime( adjust.getTimeWindow() + .getLatestReferenceTime() ) + .setEarliestLeadDuration( adjust.getTimeWindow() + .getEarliestLeadDuration() ) + .setLatestLeadDuration( adjust.getTimeWindow() + .getLatestLeadDuration() ) .build(); adjustedEvents.add( TimeWindowOuter.of( adjusted ) ); } diff --git a/src/wres/pipeline/pooling/PoolsGenerator.java b/src/wres/pipeline/pooling/PoolsGenerator.java index 3ea248cceb..482441abbc 100644 --- a/src/wres/pipeline/pooling/PoolsGenerator.java +++ b/src/wres/pipeline/pooling/PoolsGenerator.java @@ -27,6 +27,7 @@ import wres.config.yaml.components.DataType; import wres.config.yaml.components.DatasetOrientation; import wres.config.yaml.components.Variable; +import wres.datamodel.time.TimeWindowSlicer; import wres.datamodel.types.Climatology; import wres.datamodel.baselines.BaselineGenerator; import wres.datamodel.pools.Pool; @@ -752,7 +753,7 @@ private Map>>> getLeftRetrievers( { // Find the union of the time windows, bearing in mind that lead durations can influence the valid // datetimes for observation selection - TimeWindowOuter unionWindow = TimeWindowOuter.unionOf( timeWindows ); + TimeWindowOuter unionWindow = TimeWindowSlicer.union( timeWindows ); Supplier>> leftRetriever = factory.getLeftRetriever( features, unionWindow ); Supplier>> cachingRetriever = CachingRetriever.of( leftRetriever ); diff --git a/test/wres/pipeline/EvaluationUtilitiesTest.java b/test/wres/pipeline/EvaluationUtilitiesTest.java index e51a4bf4f7..f2cc7de6c4 100644 --- a/test/wres/pipeline/EvaluationUtilitiesTest.java +++ b/test/wres/pipeline/EvaluationUtilitiesTest.java @@ -15,7 +15,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import wres.config.MetricConstants; -import wres.config.yaml.DeclarationUtilities; import wres.config.yaml.components.EvaluationDeclaration; import wres.config.yaml.components.EvaluationDeclarationBuilder; import wres.config.yaml.components.FeatureGroups; @@ -27,6 +26,8 @@ import wres.datamodel.space.FeatureTuple; import wres.datamodel.thresholds.MetricsAndThresholds; import wres.datamodel.thresholds.ThresholdOuter; +import wres.datamodel.time.TimeWindowOuter; +import wres.datamodel.time.TimeWindowSlicer; import wres.metrics.SummaryStatisticsCalculator; import wres.statistics.generated.Geometry; import wres.statistics.generated.GeometryGroup; @@ -35,7 +36,6 @@ import wres.statistics.generated.Statistics; import wres.statistics.generated.SummaryStatistic; import wres.statistics.generated.Threshold; -import wres.statistics.generated.TimeWindow; /** * Tests the {@link EvaluationUtilities}. @@ -188,13 +188,13 @@ void testGetSummaryStatisticsCalculatorsForNamedThresholdsAcrossAllFeatures() .setLeftThresholdValue( 23.0 ) .build(); - TimeWindow big = DeclarationUtilities.getOneBigTimeWindow( evaluation ); + TimeWindowOuter big = TimeWindowSlicer.getOneBigTimeWindow( evaluation ); Statistics statistics = Statistics.newBuilder() .setPool( Pool.newBuilder() .setEventThreshold( eventThreshold ) - .setTimeWindow( big ) ) + .setTimeWindow( big.getTimeWindow() ) ) .build(); assertTrue( calculators.values().stream() diff --git a/wres-config/src/wres/config/yaml/DeclarationUtilities.java b/wres-config/src/wres/config/yaml/DeclarationUtilities.java index 31942ae6cb..642d8e92db 100644 --- a/wres-config/src/wres/config/yaml/DeclarationUtilities.java +++ b/wres-config/src/wres/config/yaml/DeclarationUtilities.java @@ -9,7 +9,6 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.time.Duration; -import java.time.Instant; import java.time.MonthDay; import java.util.ArrayList; import java.util.Collections; @@ -25,7 +24,6 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import com.google.protobuf.Timestamp; import org.apache.commons.lang3.tuple.Pair; import org.apache.tika.config.TikaConfig; import org.apache.tika.detect.Detector; @@ -52,7 +50,6 @@ import wres.config.yaml.components.FeatureGroupsBuilder; import wres.config.yaml.components.FeatureServiceGroup; import wres.config.yaml.components.Features; -import wres.config.yaml.components.LeadTimeInterval; import wres.config.yaml.components.Metric; import wres.config.yaml.components.MetricBuilder; import wres.config.yaml.components.MetricParameters; @@ -64,14 +61,11 @@ import wres.config.yaml.components.Threshold; import wres.config.yaml.components.ThresholdBuilder; import wres.config.yaml.components.ThresholdType; -import wres.config.yaml.components.TimeInterval; -import wres.config.yaml.components.TimePools; import wres.statistics.generated.Geometry; import wres.statistics.generated.GeometryGroup; import wres.statistics.generated.GeometryTuple; import wres.statistics.generated.ReferenceTime; import wres.statistics.generated.TimeScale; -import wres.statistics.generated.TimeWindow; import wres.statistics.MessageFactory; /** @@ -105,149 +99,6 @@ public class DeclarationUtilities .generated( true ) .build(); - /** Re-used message. */ - private static final String CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION = "Cannot determine time " - + "windows from missing " - + "declaration."; - - /** - * Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindow} for evaluation. Only - * returns the time windows that can be inferred from the declaration alone and not from any ingested data sources, - * notably those associated with event detection. - * - * @param declaration the declaration, cannot be null - * @return a set of one or more time windows for evaluation - * @throws NullPointerException if any required input is null - */ - - public static Set getTimeWindows( EvaluationDeclaration declaration ) - { - Objects.requireNonNull( declaration, CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION ); - - TimePools leadDurationPools = declaration.leadTimePools(); - TimePools referenceDatesPools = declaration.referenceDatePools(); - TimePools validDatesPools = declaration.validDatePools(); - - Set timeWindows = Set.of(); // Default to none - - // Add the time windows generated from a declared sequence - if ( Objects.nonNull( leadDurationPools ) - || Objects.nonNull( referenceDatesPools ) - || Objects.nonNull( validDatesPools ) ) - { - timeWindows = DeclarationUtilities.getTimeWindowsFromPoolSequence( declaration ); - } - // One big pool if no explicitly declared time windows and no event detection - else if ( declaration.timePools() - .isEmpty() - && Objects.isNull( declaration.eventDetection() ) ) - { - LOGGER.debug( "Building one big time window." ); - - timeWindows = Collections.singleton( DeclarationUtilities.getOneBigTimeWindow( declaration ) ); - } - - // Add the explicitly declared time windows - Set finalWindows = new HashSet<>( timeWindows ); - - LOGGER.debug( "Added {} explicitly declared time pools to the overall group of time pools.", - declaration.timePools() - .size() ); - - finalWindows.addAll( declaration.timePools() ); - - return Collections.unmodifiableSet( finalWindows ); - } - - /** - *

Builds a {@link TimeWindow} whose {@link TimeWindow#getEarliestReferenceTime()} and - * {@link TimeWindow#getLatestReferenceTime()} return the {@code earliest} and {@code latest} bookends of the - * {@link EvaluationDeclaration#referenceDates()}, respectively, whose {@link TimeWindow#getEarliestValidTime()} - * and {@link TimeWindow#getLatestValidTime()} return the {@code earliest} and {@code latest} bookends of the - * {@link EvaluationDeclaration#validDates()}, respectively, and whose {@link TimeWindow#getEarliestLeadDuration()} - * and {@link TimeWindow#getLatestLeadDuration()} return the {@code minimum} and {@code maximum} bookends of the - * {@link EvaluationDeclaration#leadTimes()}, respectively. - * - *

If any of these variables are missing from the input, defaults are used, which represent the - * computationally-feasible limiting values. For example, the smallest and largest possible instant is - * {@link Instant#MIN} and {@link Instant#MAX}, respectively. The smallest and largest possible {@link Duration} is - * {@link MessageFactory#DURATION_MIN} and {@link MessageFactory#DURATION_MAX}, respectively. - * - * @param declaration the declaration - * @return a time window - * @throws NullPointerException if the pairConfig is null - */ - - public static TimeWindow getOneBigTimeWindow( EvaluationDeclaration declaration ) - { - Objects.requireNonNull( declaration, CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION ); - - Instant earliestReferenceTime = Instant.MIN; - Instant latestReferenceTime = Instant.MAX; - Instant earliestValidTime = Instant.MIN; - Instant latestValidTime = Instant.MAX; - Duration smallestLeadDuration = MessageFactory.DURATION_MIN; - Duration largestLeadDuration = MessageFactory.DURATION_MAX; - - // Reference datetimes - if ( Objects.nonNull( declaration.referenceDates() ) ) - { - if ( Objects.nonNull( declaration.referenceDates() - .minimum() ) ) - { - earliestReferenceTime = declaration.referenceDates() - .minimum(); - } - if ( Objects.nonNull( declaration.referenceDates() - .maximum() ) ) - { - latestReferenceTime = declaration.referenceDates() - .maximum(); - } - } - - // Valid datetimes - if ( Objects.nonNull( declaration.validDates() ) ) - { - if ( Objects.nonNull( declaration.validDates() - .minimum() ) ) - { - earliestValidTime = declaration.validDates() - .minimum(); - } - if ( Objects.nonNull( declaration.validDates() - .maximum() ) ) - { - latestValidTime = declaration.validDates() - .maximum(); - } - } - - // Lead durations - if ( Objects.nonNull( declaration.leadTimes() ) ) - { - if ( Objects.nonNull( declaration.leadTimes() - .minimum() ) ) - { - smallestLeadDuration = declaration.leadTimes() - .minimum(); - } - if ( Objects.nonNull( declaration.leadTimes() - .maximum() ) ) - { - largestLeadDuration = declaration.leadTimes() - .maximum(); - } - } - - return MessageFactory.getTimeWindow( earliestReferenceTime, - latestReferenceTime, - earliestValidTime, - latestValidTime, - smallestLeadDuration, - largestLeadDuration ); - } - /** * @param evaluation the evaluation * @return whether a baseline dataset has been declared @@ -1452,78 +1303,6 @@ static String getFirstLine( String lines ) return line; } - /** - * Generates time windows from an explicit pool sequence. - * - * @param declaration the declaration - * @return the time windows - */ - private static Set getTimeWindowsFromPoolSequence( EvaluationDeclaration declaration ) - { - TimePools leadDurationPools = declaration.leadTimePools(); - TimePools referenceDatesPools = declaration.referenceDatePools(); - TimePools validDatesPools = declaration.validDatePools(); - - Set timeWindows; - - // All dimensions - if ( Objects.nonNull( referenceDatesPools ) - && Objects.nonNull( validDatesPools ) - && Objects.nonNull( leadDurationPools ) ) - { - LOGGER.debug( "Building time windows for reference dates and valid dates and lead durations." ); - - timeWindows = DeclarationUtilities.getReferenceDatesValidDatesAndLeadDurationTimeWindows( declaration ); - } - // Reference dates and valid dates - else if ( Objects.nonNull( referenceDatesPools ) - && Objects.nonNull( validDatesPools ) ) - { - LOGGER.debug( "Building time windows for reference dates and valid dates." ); - - timeWindows = DeclarationUtilities.getReferenceDatesAndValidDatesTimeWindows( declaration ); - } - // Reference dates and lead durations - else if ( Objects.nonNull( referenceDatesPools ) - && Objects.nonNull( leadDurationPools ) ) - { - LOGGER.debug( "Building time windows for reference dates and lead durations." ); - - timeWindows = DeclarationUtilities.getReferenceDatesAndLeadDurationTimeWindows( declaration ); - } - // Valid dates and lead durations - else if ( Objects.nonNull( validDatesPools ) - && Objects.nonNull( leadDurationPools ) ) - { - LOGGER.debug( "Building time windows for valid dates and lead durations." ); - - timeWindows = DeclarationUtilities.getValidDatesAndLeadDurationTimeWindows( declaration ); - } - // Reference dates - else if ( Objects.nonNull( referenceDatesPools ) ) - { - LOGGER.debug( "Building time windows for reference dates." ); - - timeWindows = DeclarationUtilities.getReferenceDatesTimeWindows( declaration ); - } - // Lead durations - else if ( Objects.nonNull( leadDurationPools ) ) - { - LOGGER.debug( "Building time windows for lead durations." ); - - timeWindows = DeclarationUtilities.getLeadDurationTimeWindows( declaration ); - } - // Valid dates - else - { - LOGGER.debug( "Building time windows for valid dates." ); - - timeWindows = DeclarationUtilities.getValidDatesTimeWindows( declaration ); - } - - return timeWindows; - } - /** * @param builder the builder * @param groupType the group the metric should be part of @@ -1769,430 +1548,6 @@ private static void addDataSources( DatasetBuilder builder, List sources, D builder.sources( combinedSources ); } - /** - *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindow} for evaluation using - * the {@link EvaluationDeclaration#leadTimePools()} and the {@link EvaluationDeclaration#leadTimes()}. Returns at - * least one {@link TimeWindow}. - * - * @param declaration the declaration - * @return the set of lead duration time windows - * @throws NullPointerException if any required input is null - */ - - private static Set getLeadDurationTimeWindows( EvaluationDeclaration declaration ) - { - String messageStart = "Cannot determine lead duration time windows "; - - Objects.requireNonNull( declaration, messageStart + "from null declaration." ); - - LeadTimeInterval leadHours = declaration.leadTimes(); - - Objects.requireNonNull( leadHours, "Cannot determine lead duration time windows without 'lead_times'." ); - Objects.requireNonNull( leadHours.minimum(), - "Cannot determine lead duration time windows without a 'minimum' value for " - + "'lead_times'." ); - Objects.requireNonNull( leadHours.maximum(), - "Cannot determine lead duration time windows without a 'maximum' value for " - + "'lead_times'." ); - - TimePools leadTimesPoolingWindow = declaration.leadTimePools(); - - Objects.requireNonNull( leadTimesPoolingWindow, - "Cannot determine lead duration time windows without a 'lead_time_pools'." ); - - // Obtain the base window - TimeWindow baseWindow = DeclarationUtilities.getOneBigTimeWindow( declaration ); - - // Period associated with the leadTimesPoolingWindow - Duration periodOfLeadTimesPoolingWindow = leadTimesPoolingWindow.period(); - - // Exclusive lower bound: #56213-104 - Duration earliestLeadDurationExclusive = leadHours.minimum(); - - // Inclusive upper bound - Duration latestLeadDurationInclusive = leadHours.maximum(); - - // Duration by which to increment. Defaults to the period associated - // with the leadTimesPoolingWindow, otherwise the frequency. - Duration increment = periodOfLeadTimesPoolingWindow; - if ( Objects.nonNull( leadTimesPoolingWindow.frequency() ) ) - { - increment = leadTimesPoolingWindow.frequency(); - } - - // Lower bound of the current window - Duration earliestExclusive = earliestLeadDurationExclusive; - - // Upper bound of the current window - Duration latestInclusive = earliestExclusive.plus( periodOfLeadTimesPoolingWindow ); - - // Create the time windows - Set timeWindows = new HashSet<>(); - - // Increment left-to-right and stop when the right bound extends past the - // latestLeadDurationInclusive: #56213-104 - // Window increments are zero? - if ( Duration.ZERO.equals( increment ) ) - { - com.google.protobuf.Duration earliest = MessageFactory.getDuration( earliestExclusive ); - com.google.protobuf.Duration latest = MessageFactory.getDuration( latestInclusive ); - TimeWindow window = baseWindow.toBuilder() - .setEarliestLeadDuration( earliest ) - .setLatestLeadDuration( latest ) - .build(); - timeWindows.add( window ); - } - // Create as many windows as required at the prescribed increment - else - { - while ( latestInclusive.compareTo( latestLeadDurationInclusive ) <= 0 ) - { - // Add the current time window - com.google.protobuf.Duration earliest = MessageFactory.getDuration( earliestExclusive ); - com.google.protobuf.Duration latest = MessageFactory.getDuration( latestInclusive ); - TimeWindow window = baseWindow.toBuilder() - .setEarliestLeadDuration( earliest ) - .setLatestLeadDuration( latest ) - .build(); - timeWindows.add( window ); - - // Increment from left-to-right: #56213-104 - earliestExclusive = earliestExclusive.plus( increment ); - latestInclusive = latestInclusive.plus( increment ); - } - } - - return Collections.unmodifiableSet( timeWindows ); - } - - /** - *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindow} for evaluation using - * the {@link EvaluationDeclaration#referenceDatePools()} and the {@link EvaluationDeclaration#referenceDates()}. - * Returns at least one {@link TimeWindow}. - * - * @param declaration the declaration - * @return the set of reference time windows - * @throws NullPointerException if the declaration is null or any required input within it is null - */ - - private static Set getReferenceDatesTimeWindows( EvaluationDeclaration declaration ) - { - Objects.requireNonNull( declaration, "Cannot determine reference time windows from missing " - + "declaration." ); - Objects.requireNonNull( declaration.referenceDates(), - "Cannot determine reference time windows without 'reference_dates'." ); - Objects.requireNonNull( declaration.referenceDates() - .minimum(), - "Cannot determine reference time windows without the 'minimum' for the " - + "'reference_dates'." ); - Objects.requireNonNull( declaration.referenceDates() - .maximum(), - "Cannot determine reference time windows without the 'maximum' for the " - + "'reference_dates'." ); - Objects.requireNonNull( declaration.referenceDatePools(), - "Cannot determine reference time windows without 'reference_date_pools'." ); - - // Base window from which to generate a sequence of windows - TimeWindow baseWindow = DeclarationUtilities.getOneBigTimeWindow( declaration ); - - return DeclarationUtilities.getTimeWindowsForDateSequence( declaration.referenceDates(), - declaration.referenceDatePools(), - baseWindow, - true ); - } - - /** - *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindow} for evaluation using - * the {@link EvaluationDeclaration#validDatePools()} and the {@link EvaluationDeclaration#validDates()}. - * Returns at least one {@link TimeWindow}. - * - * @param declaration the declaration - * @return the set of reference time windows - * @throws NullPointerException if the declaration is null or any required input within it is null - */ - - private static Set getValidDatesTimeWindows( EvaluationDeclaration declaration ) - { - Objects.requireNonNull( declaration, "Cannot determine valid time windows from missing declaration." ); - Objects.requireNonNull( declaration.validDates(), - "Cannot determine valid time windows without 'valid_dates'." ); - Objects.requireNonNull( declaration.validDates() - .minimum(), - "Cannot determine valid time windows without the 'minimum' for the " - + "'valid_dates'." ); - Objects.requireNonNull( declaration.validDates() - .maximum(), - "Cannot determine valid time windows without the 'maximum' for the " - + "'valid_dates'." ); - Objects.requireNonNull( declaration.validDatePools(), - "Cannot determine valid time windows without 'valid_date_pools'." ); - - // Base window from which to generate a sequence of windows - TimeWindow baseWindow = DeclarationUtilities.getOneBigTimeWindow( declaration ); - - return DeclarationUtilities.getTimeWindowsForDateSequence( declaration.validDates(), - declaration.validDatePools(), - baseWindow, - false ); - } - - /** - *

Generates a set of time windows based on a sequence of datetimes. - * - * @param dates the date constraints - * @param pools the sequence of datetimes to generate - * @param baseWindow the basic time window from which each pool in the sequence begins - * @param areReferenceTimes is true if the dates are reference dates, false for valid dates - * @return the set of reference time windows - * @throws NullPointerException if any input is null - */ - - private static Set getTimeWindowsForDateSequence( TimeInterval dates, - TimePools pools, - TimeWindow baseWindow, - boolean areReferenceTimes ) - { - Objects.requireNonNull( dates ); - Objects.requireNonNull( pools ); - Objects.requireNonNull( baseWindow ); - - // Period associated with the reference time pool - Duration periodOfPoolingWindow = pools.period(); - - // Exclusive lower bound: #56213-104 - Instant earliestInstantExclusive = dates.minimum(); - - // Inclusive upper bound - Instant latestInstantInclusive = dates.maximum(); - - // Duration by which to increment. Defaults to the period associated with the reference time pools, otherwise - // the frequency. - Duration increment = periodOfPoolingWindow; - if ( Objects.nonNull( pools.frequency() ) ) - { - increment = pools.frequency(); - } - - // Lower bound of the current window - Instant earliestExclusive = earliestInstantExclusive; - - // Upper bound of the current window - Instant latestInclusive = earliestExclusive.plus( periodOfPoolingWindow ); - - // Create the time windows - Set timeWindows = new HashSet<>(); - - // Increment left-to-right and stop when the right bound - // extends past the latestInstantInclusive: #56213-104 - while ( latestInclusive.compareTo( latestInstantInclusive ) <= 0 ) - { - TimeWindow timeWindow = DeclarationUtilities.getTimeWindowFromDates( earliestExclusive, - latestInclusive, - baseWindow, - areReferenceTimes ); - - // Add the current time window - timeWindows.add( timeWindow ); - - // Increment left-to-right: #56213-104 - earliestExclusive = earliestExclusive.plus( increment ); - latestInclusive = latestInclusive.plus( increment ); - } - - return Collections.unmodifiableSet( timeWindows ); - } - - /** - * Returns a time window from the inputs. - * - * @param earliestExclusive the earliest exclusive time - * @param latestInclusive the latest inclusive time - * @param baseWindow the base window with default times - * @param areReferenceTimes is true if the earliestExclusive and latestInclusive are reference times, false for - * valid times - * @return a time window - */ - - private static TimeWindow getTimeWindowFromDates( Instant earliestExclusive, - Instant latestInclusive, - TimeWindow baseWindow, - boolean areReferenceTimes ) - { - Timestamp earliest = MessageFactory.getTimestamp( earliestExclusive ); - Timestamp latest = MessageFactory.getTimestamp( latestInclusive ); - - // Reference dates - if ( areReferenceTimes ) - { - return baseWindow.toBuilder() - .setEarliestReferenceTime( earliest ) - .setLatestReferenceTime( latest ) - .build(); - } - // Valid dates - else - { - return baseWindow.toBuilder() - .setEarliestValidTime( earliest ) - .setLatestValidTime( latest ) - .build(); - } - } - - /** - *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindow} for evaluation using - * the {@link EvaluationDeclaration#leadTimePools()}, the {@link EvaluationDeclaration#leadTimes()}, the - * {@link EvaluationDeclaration#referenceDatePools()} and the {@link EvaluationDeclaration#referenceDates()}. - * Returns at least one {@link TimeWindow}. - * - * @param declaration the declaration - * @return the set of lead duration and reference time windows - * @throws NullPointerException if the declaration is null - */ - - private static Set getReferenceDatesAndLeadDurationTimeWindows( EvaluationDeclaration declaration ) - { - Objects.requireNonNull( declaration, CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION ); - - Set leadDurationWindows = DeclarationUtilities.getLeadDurationTimeWindows( declaration ); - - Set referenceDatesWindows = DeclarationUtilities.getReferenceDatesTimeWindows( declaration ); - - // Create a new window for each combination of reference dates and lead duration - Set timeWindows = - new HashSet<>( leadDurationWindows.size() * referenceDatesWindows.size() ); - for ( TimeWindow nextReferenceWindow : referenceDatesWindows ) - { - for ( TimeWindow nextLeadWindow : leadDurationWindows ) - { - TimeWindow window = nextReferenceWindow.toBuilder() - .setEarliestLeadDuration( nextLeadWindow.getEarliestLeadDuration() ) - .setLatestLeadDuration( nextLeadWindow.getLatestLeadDuration() ) - .build(); - timeWindows.add( window ); - } - } - - return Collections.unmodifiableSet( timeWindows ); - } - - /** - *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindow} for evaluation using - * the {@link EvaluationDeclaration#referenceDatePools()}, the {@link EvaluationDeclaration#referenceDates()}, the - * {@link EvaluationDeclaration#validDatePools()} and the {@link EvaluationDeclaration#validDates()}. Returns at - * least one {@link TimeWindow}. - * - * @param declaration the declaration - * @return the set of reference time and valid time windows - * @throws NullPointerException if the declaration is null - */ - - private static Set getReferenceDatesAndValidDatesTimeWindows( EvaluationDeclaration declaration ) - { - Objects.requireNonNull( declaration, CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION ); - - Set validDatesWindows = DeclarationUtilities.getValidDatesTimeWindows( declaration ); - - Set referenceDatesWindows = DeclarationUtilities.getReferenceDatesTimeWindows( declaration ); - - // Create a new window for each combination of reference dates and lead duration - Set timeWindows = new HashSet<>( validDatesWindows.size() * referenceDatesWindows.size() ); - for ( TimeWindow nextValidWindow : validDatesWindows ) - { - for ( TimeWindow nextReferenceWindow : referenceDatesWindows ) - { - TimeWindow window = - nextValidWindow.toBuilder() - .setEarliestReferenceTime( nextReferenceWindow.getEarliestReferenceTime() ) - .setLatestReferenceTime( nextReferenceWindow.getLatestReferenceTime() ) - .build(); - timeWindows.add( window ); - } - } - - return Collections.unmodifiableSet( timeWindows ); - } - - /** - *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindow} for evaluation using - * the {@link EvaluationDeclaration#leadTimePools()} ()}, the {@link EvaluationDeclaration#leadTimes()}, the - * {@link EvaluationDeclaration#validDatePools()} ()} and the {@link EvaluationDeclaration#validDates()}. Returns - * at least one {@link TimeWindow}. - * - * @param declaration the declaration - * @return the set of lead duration and valid dates time windows - * @throws NullPointerException if the pairConfig is null - */ - - private static Set getValidDatesAndLeadDurationTimeWindows( EvaluationDeclaration declaration ) - { - Objects.requireNonNull( declaration, CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION ); - - Set leadDurationWindows = DeclarationUtilities.getLeadDurationTimeWindows( declaration ); - - Set validDatesWindows = DeclarationUtilities.getValidDatesTimeWindows( declaration ); - - // Create a new window for each combination of valid dates and lead duration - Set timeWindows = new HashSet<>( leadDurationWindows.size() * validDatesWindows.size() ); - for ( TimeWindow nextValidWindow : validDatesWindows ) - { - for ( TimeWindow nextLeadWindow : leadDurationWindows ) - { - TimeWindow window = - nextValidWindow.toBuilder() - .setEarliestLeadDuration( nextLeadWindow.getEarliestLeadDuration() ) - .setLatestLeadDuration( nextLeadWindow.getLatestLeadDuration() ) - .build(); - timeWindows.add( window ); - } - } - - return Collections.unmodifiableSet( timeWindows ); - } - - /** - *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindow} for evaluation using - * the {@link EvaluationDeclaration#leadTimePools()}, the {@link EvaluationDeclaration#leadTimes()}, the - * {@link EvaluationDeclaration#referenceDatePools()} the {@link EvaluationDeclaration#referenceDates()}, the - * {@link EvaluationDeclaration#validDatePools()} and the {@link EvaluationDeclaration#validDates()}. Returns at - * least one {@link TimeWindow}. - * - * @param declaration the declaration - * @return the set of lead duration, reference time and valid time windows - * @throws NullPointerException if the declaration is null - */ - - private static Set getReferenceDatesValidDatesAndLeadDurationTimeWindows( EvaluationDeclaration declaration ) - { - Objects.requireNonNull( declaration, CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION ); - - Set leadDurationWindows = DeclarationUtilities.getLeadDurationTimeWindows( declaration ); - Set referenceDatesWindows = DeclarationUtilities.getReferenceDatesTimeWindows( declaration ); - Set validDatesWindows = DeclarationUtilities.getValidDatesTimeWindows( declaration ); - - // Create a new window for each combination of reference dates and lead duration - Set timeWindows = new HashSet<>( leadDurationWindows.size() * referenceDatesWindows.size() ); - for ( TimeWindow nextReferenceWindow : referenceDatesWindows ) - { - for ( TimeWindow nextValidWindow : validDatesWindows ) - { - for ( TimeWindow nextLeadWindow : leadDurationWindows ) - { - TimeWindow window = - nextValidWindow.toBuilder() - .setEarliestReferenceTime( nextReferenceWindow.getEarliestReferenceTime() ) - .setLatestReferenceTime( nextReferenceWindow.getLatestReferenceTime() ) - .setEarliestLeadDuration( nextLeadWindow.getEarliestLeadDuration() ) - .setLatestLeadDuration( nextLeadWindow.getLatestLeadDuration() ) - .build(); - timeWindows.add( window ); - } - } - } - - return Collections.unmodifiableSet( timeWindows ); - } - /** * Expands the supplied metrics, replacing any timing error summary statistics with equivalent top-level metrics. * @param metrics the metrics diff --git a/wres-config/test/wres/config/yaml/DeclarationUtilitiesTest.java b/wres-config/test/wres/config/yaml/DeclarationUtilitiesTest.java index 66803db3f7..0b86a0b0b4 100644 --- a/wres-config/test/wres/config/yaml/DeclarationUtilitiesTest.java +++ b/wres-config/test/wres/config/yaml/DeclarationUtilitiesTest.java @@ -7,11 +7,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; -import java.time.Instant; import java.time.MonthDay; import java.time.ZoneOffset; import java.util.Collections; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -86,1292 +84,6 @@ */ class DeclarationUtilitiesTest { - private static final String INSTANT_ONE = "2017-08-08T00:00:00Z"; - private static final String INSTANT_TWO = "2017-08-08T23:00:00Z"; - private static final String INSTANT_THREE = "2017-08-09T17:00:00Z"; - private static final String INSTANT_FOUR = "2017-08-08T01:00:00Z"; - private static final String INSTANT_FIVE = "2017-08-08T02:00:00Z"; - private static final String INSTANT_SIX = "2017-08-08T03:00:00Z"; - private static final String INSTANT_SEVEN = "2017-08-08T04:00:00Z"; - private static final String INSTANT_EIGHT = "2017-08-08T05:00:00Z"; - private static final String INSTANT_NINE = "2551-03-17T00:00:00Z"; - private static final String INSTANT_TEN = "2551-03-20T00:00:00Z"; - private static final String INSTANT_ELEVEN = "2551-03-19T00:00:00Z"; - private static final String INSTANT_TWELVE = "2551-03-17T13:00:00Z"; - private static final String INSTANT_THIRTEEN = "2551-03-17T07:00:00Z"; - private static final String INSTANT_FOURTEEN = "2551-03-17T20:00:00Z"; - private static final String INSTANT_FIFTEEN = "2551-03-17T14:00:00Z"; - private static final String INSTANT_SIXTEEN = "2551-03-18T03:00:00Z"; - private static final String INSTANT_SEVENTEEN = "2551-03-17T21:00:00Z"; - private static final String INSTANT_EIGHTEEN = "2551-03-18T10:00:00Z"; - private static final String INSTANT_NINETEEN = "2551-03-18T04:00:00Z"; - private static final String INSTANT_TWENTY = "2551-03-18T17:00:00Z"; - private static final String INSTANT_TWENTY_ONE = "2551-03-18T11:00:00Z"; - private static final String INSTANT_TWENTY_TWO = "2551-03-18T18:00:00Z"; - private static final String INSTANT_TWENTY_THREE = "2551-03-19T07:00:00Z"; - private static final String INSTANT_TWENTY_FOUR = "2551-03-19T01:00:00Z"; - private static final String INSTANT_TWENTY_FIVE = "2551-03-19T14:00:00Z"; - private static final String INSTANT_TWENTY_SIX = "2551-03-19T08:00:00Z"; - private static final String INSTANT_TWENTY_SEVEN = "2551-03-19T21:00:00Z"; - private static final String INSTANT_TWENTY_EIGHT = "2551-03-24T00:00:00Z"; - - /** - *

Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} where the project declaration - * includes a lead_times and a lead_time_pools. Expects twenty-four time windows - * with prescribed characteristics. - * - *

The project declaration from this test scenario matches (in all important ways) the declaration associated - * with system test scenario017, as of commit fa548da9da85b16631f238f78b358d85ddbebed5. - */ - - @Test - void testGetTimeWindowsWithLeadTimesAndLeadTimePoolsReturnsTwentyFourWindows() - { - LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() - .minimum( Duration.ofHours( 0 ) ) - .maximum( Duration.ofHours( 24 ) ) - .build(); - TimePools leadTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 1 ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .leadTimes( leadTimes ) - .leadTimePools( leadTimePools ) - .build(); - - // Generate the expected windows - Set expectedTimeWindows = new HashSet<>( 24 ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 0 ), - Duration.ofHours( 1 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 1 ), - Duration.ofHours( 2 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 2 ), - Duration.ofHours( 3 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 3 ), - Duration.ofHours( 4 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 4 ), - Duration.ofHours( 5 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 5 ), - Duration.ofHours( 6 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 6 ), - Duration.ofHours( 7 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 7 ), - Duration.ofHours( 8 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 8 ), - Duration.ofHours( 9 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 9 ), - Duration.ofHours( 10 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 10 ), - Duration.ofHours( 11 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 11 ), - Duration.ofHours( 12 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 12 ), - Duration.ofHours( 13 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 13 ), - Duration.ofHours( 14 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 14 ), - Duration.ofHours( 15 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 15 ), - Duration.ofHours( 16 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 16 ), - Duration.ofHours( 17 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 17 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 18 ), - Duration.ofHours( 19 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 19 ), - Duration.ofHours( 20 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 20 ), - Duration.ofHours( 21 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 21 ), - Duration.ofHours( 22 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 22 ), - Duration.ofHours( 23 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 23 ), - Duration.ofHours( 24 ) ) ); - - // Generate the actual windows - Set actualTimeWindows = DeclarationUtilities.getTimeWindows( declaration ); - - // Assert the expected cardinality - assertEquals( 24, actualTimeWindows.size() ); - - // Assert that the expected and actual are equal - assertEquals( expectedTimeWindows, actualTimeWindows ); - } - - /** - *

Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} where the project declaration - * includes a lead_times and a lead_time_pools. Expects two time windows with - * prescribed characteristics. - * - *

The project declaration from this test scenario matches (in all important ways) the declaration associated - * with system test scenario403, as of commit fa548da9da85b16631f238f78b358d85ddbebed5. - */ - - @Test - void testGetTimeWindowsWithLeadTimesAndLeadTimePoolsReturnsTwoWindows() - { - LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() - .minimum( Duration.ofHours( 0 ) ) - .maximum( Duration.ofHours( 48 ) ) - .build(); - TimePools leadTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 24 ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .leadTimes( leadTimes ) - .leadTimePools( leadTimePools ) - .build(); - - // Generate the expected windows - Set expectedTimeWindows = new HashSet<>( 2 ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 0 ), - Duration.ofHours( 24 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 24 ), - Duration.ofHours( 48 ) ) ); - - // Generate the actual windows - Set actualTimeWindows = DeclarationUtilities.getTimeWindows( declaration ); - - // Assert the expected cardinality - assertEquals( 2, actualTimeWindows.size() ); - - // Assert that the expected and actual are equal - assertEquals( expectedTimeWindows, actualTimeWindows ); - } - - /** - *

Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} where the project declaration - * includes a lead_times, a lead_time_pools, an reference_dates, and an - * referenceDatesPoolingWindow. Expects eighteen time windows with prescribed characteristics. - * - *

The project declaration from this test scenario matches (in all important ways) the declaration associated - * with system test scenario505, which is in development as of commit 766c6d0b4ad96f191bcafb8f2a357c0f2e6a2d3c. - */ - - @Test - void testGetTimeWindowsWithLeadTimesReferenceDatesLeadTimePoolsAndReferenceDatesPoolingWindowReturnsEighteenWindows() - { - LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() - .minimum( Duration.ofHours( 0 ) ) - .maximum( Duration.ofHours( 40 ) ) - .build(); - TimePools leadTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 23 ) ) - .frequency( Duration.ofHours( 17 ) ) - .build(); - TimeInterval referenceDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_NINE ) ) - .maximum( Instant.parse( INSTANT_TEN ) ) - .build(); - TimePools referenceTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 13 ) ) - .frequency( Duration.ofHours( 7 ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .leadTimes( leadTimes ) - .leadTimePools( leadTimePools ) - .referenceDates( referenceDates ) - .referenceDatePools( referenceTimePools ) - .build(); - - // Generate the expected windows - Set expectedTimeWindows = new HashSet<>( 18 ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINE ), - //2551-03-17T00:00:00Z - Instant.parse( INSTANT_TWELVE ), - //2551-03-17T13:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 23 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINE ), - //2551-03-17T00:00:00Z - Instant.parse( INSTANT_TWELVE ), - //2551-03-17T13:00:00Z - Duration.ofHours( 17 ), - Duration.ofHours( 40 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_THIRTEEN ), - //2551-03-17T07:00:00Z - Instant.parse( INSTANT_FOURTEEN ), - //2551-03-17T20:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 23 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_THIRTEEN ), - //2551-03-17T07:00:00Z - Instant.parse( INSTANT_FOURTEEN ), - //2551-03-17T20:00:00Z - Duration.ofHours( 17 ), - Duration.ofHours( 40 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_FIFTEEN ), - //2551-03-17T14:00:00Z - Instant.parse( INSTANT_SIXTEEN ), - //2551-03-18T03:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 23 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_FIFTEEN ), - //2551-03-17T14:00:00Z - Instant.parse( INSTANT_SIXTEEN ), - //2551-03-18T03:00:00Z - Duration.ofHours( 17 ), - Duration.ofHours( 40 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_SEVENTEEN ), - //2551-03-17T21:00:00Z - Instant.parse( INSTANT_EIGHTEEN ), - //2551-03-18T10:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 23 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_SEVENTEEN ), - //2551-03-17T21:00:00Z - Instant.parse( INSTANT_EIGHTEEN ), - //2551-03-18T10:00:00Z - Duration.ofHours( 17 ), - Duration.ofHours( 40 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINETEEN ), - //2551-03-18T04:00:00Z - Instant.parse( INSTANT_TWENTY ), - //2551-03-18T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 23 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINETEEN ), - //2551-03-18T04:00:00Z - Instant.parse( INSTANT_TWENTY ), - //2551-03-18T17:00:00Z - Duration.ofHours( 17 ), - Duration.ofHours( 40 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_ONE ), - //2551-03-18T11:00:00Z - Instant.parse( INSTANT_ELEVEN ), - //2551-03-19T00:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 23 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_ONE ), - //2551-03-18T11:00:00Z - Instant.parse( INSTANT_ELEVEN ), - //2551-03-19T00:00:00Z - Duration.ofHours( 17 ), - Duration.ofHours( 40 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_TWO ), - //2551-03-18T18:00:00Z - Instant.parse( INSTANT_TWENTY_THREE ), - //2551-03-19T07:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 23 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_TWO ), - //2551-03-18T18:00:00Z - Instant.parse( INSTANT_TWENTY_THREE ), - //2551-03-19T07:00:00Z - Duration.ofHours( 17 ), - Duration.ofHours( 40 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_FOUR ), - //2551-03-19T01:00:00Z - Instant.parse( INSTANT_TWENTY_FIVE ), - //2551-03-19T14:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 23 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_FOUR ), - //2551-03-19T01:00:00Z - Instant.parse( INSTANT_TWENTY_FIVE ), - //2551-03-19T14:00:00Z - Duration.ofHours( 17 ), - Duration.ofHours( 40 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_SIX ), - //2551-03-19T08:00:00Z - Instant.parse( INSTANT_TWENTY_SEVEN ), - //2551-03-19T21:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 23 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_SIX ), - //2551-03-19T08:00:00Z - Instant.parse( INSTANT_TWENTY_SEVEN ), - //2551-03-19T21:00:00Z - Duration.ofHours( 17 ), - Duration.ofHours( 40 ) ) ); - - // Generate the actual windows - Set actualTimeWindows = DeclarationUtilities.getTimeWindows( declaration ); - - // Assert the expected cardinality - assertEquals( 18, actualTimeWindows.size() ); - - // Assert that the expected and actual are equal - assertEquals( expectedTimeWindows, actualTimeWindows ); - } - - /** - *

Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} where the project declaration - * includes a lead_times, a lead_time_pools, a dates and an - * reference_dates. Expects one time window with prescribed characteristics. - */ - - @Test - void testGetTimeWindowsWithLeadTimesValidDatesReferenceDatesAndLeadTimePoolsReturnsOneWindow() - { - LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() - .minimum( Duration.ofHours( 1 ) ) - .maximum( Duration.ofHours( 48 ) ) - .build(); - TimePools leadTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 24 ) ) - .build(); - TimeInterval referenceDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_NINE ) ) - .maximum( Instant.parse( INSTANT_TEN ) ) - .build(); - TimeInterval validDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_ELEVEN ) ) - .maximum( Instant.parse( INSTANT_TWENTY_EIGHT ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .leadTimes( leadTimes ) - .validDates( validDates ) - .leadTimePools( leadTimePools ) - .referenceDates( referenceDates ) - .build(); - - // Generate the expected windows - Set expectedTimeWindows = new HashSet<>( 1 ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINE ), - //2551-03-17T00:00:00Z - Instant.parse( INSTANT_TEN ), - //2551-03-20T00:00:00Z - Instant.parse( INSTANT_ELEVEN ), - //2551-03-19T00:00:00Z - Instant.parse( INSTANT_TWENTY_EIGHT ), - //2551-03-24T00:00:00Z - Duration.ofHours( 1 ), - Duration.ofHours( 25 ) ) ); - - // Generate the actual windows - Set actualTimeWindows = DeclarationUtilities.getTimeWindows( declaration ); - - // Assert the expected cardinality - assertEquals( 1, actualTimeWindows.size() ); - - // Assert that the expected and actual are equal - assertEquals( expectedTimeWindows, actualTimeWindows ); - } - - /** - *

Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} where the project declaration - * does not include any constraints on time, aka "one big pool". - * - *

This is analogous to system test scenario508, as of commit b9a7214ec22999482784119a8527149348c80119. - */ - - @Test - void testGetTimeWindowsFromUnconstrainedDeclarationReturnsOneWindow() - { - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .build(); - - // Generate the expected windows - Set expectedTimeWindows = new HashSet<>( 1 ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.MIN, - Instant.MAX, - Instant.MIN, - Instant.MAX, - MessageFactory.DURATION_MIN, - MessageFactory.DURATION_MAX ) ); - - // Generate the actual windows - Set actualTimeWindows = DeclarationUtilities.getTimeWindows( declaration ); - - // Assert the expected cardinality - assertEquals( 1, actualTimeWindows.size() ); - - // Assert that the expected and actual are equal - assertEquals( expectedTimeWindows, actualTimeWindows ); - } - - /** - *

Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} where the project declaration - * includes a referenceDatesPoolingWindow and a lead_time_pools. Expects twenty-three - * time windows. Tests both an explicit and implicit declaration of the frequency. - * - *

The project declaration from this test matches the declaration associated - * with system test scenario704, as of commit da07c16148429740496b8cc6df89a73e3697f17c, - * except the period is 1.0 time units here. - */ - - @Test - void testGetTimeWindowsWithLeadTimesValidDatesReferenceDatesReferenceDatesPoolingWindowAndLeadTimePoolsReturnsTwentyThreeWindows() - { - LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() - .minimum( Duration.ofHours( 0 ) ) - .maximum( Duration.ofHours( 18 ) ) - .build(); - TimePools leadTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 18 ) ) - .build(); - TimeInterval referenceDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_ONE ) ) - .maximum( Instant.parse( INSTANT_TWO ) ) - .build(); - TimePools referenceTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 1 ) ) - .frequency( Duration.ofHours( 1 ) ) - .build(); - TimeInterval validDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_ONE ) ) - .maximum( Instant.parse( INSTANT_THREE ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .leadTimes( leadTimes ) - .leadTimePools( leadTimePools ) - .referenceDates( referenceDates ) - .referenceDatePools( referenceTimePools ) - .validDates( validDates ) - .build(); - - // Generate the expected time windows - Set expectedTimeWindows = new HashSet<>( 23 ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_FOUR ), - //2017-08-08T01:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_FOUR ), - //2017-08-08T01:00:00Z - Instant.parse( INSTANT_FIVE ), - //2017-08-08T02:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_FIVE ), - //2017-08-08T02:00:00Z - Instant.parse( INSTANT_SIX ), - //2017-08-08T03:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_SIX ), - //2017-08-08T03:00:00Z - Instant.parse( INSTANT_SEVEN ), - //2017-08-08T04:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_SEVEN ), - //2017-08-08T04:00:00Z - Instant.parse( INSTANT_EIGHT ), - //2017-08-08T05:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_EIGHT ), - //2017-08-08T05:00:00Z - Instant.parse( "2017-08-08T06:00:00Z" ), - //2017-08-08T06:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T06:00:00Z" ), - //2017-08-08T06:00:00Z - Instant.parse( "2017-08-08T07:00:00Z" ), - //2017-08-08T07:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T07:00:00Z" ), - //2017-08-08T07:00:00Z - Instant.parse( "2017-08-08T08:00:00Z" ), - //2017-08-08T08:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T08:00:00Z" ), - //2017-08-08T08:00:00Z - Instant.parse( "2017-08-08T09:00:00Z" ), - //2017-08-08T09:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T09:00:00Z" ), - //2017-08-08T09:00:00Z - Instant.parse( "2017-08-08T10:00:00Z" ), - //2017-08-08T10:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T10:00:00Z" ), - //2017-08-08T10:00:00Z - Instant.parse( "2017-08-08T11:00:00Z" ), - //2017-08-08T11:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T11:00:00Z" ), - //2017-08-08T11:00:00Z - Instant.parse( "2017-08-08T12:00:00Z" ), - //2017-08-08T12:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T12:00:00Z" ), - //2017-08-08T12:00:00Z - Instant.parse( "2017-08-08T13:00:00Z" ), - //2017-08-08T13:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T13:00:00Z" ), - //2017-08-08T13:00:00Z - Instant.parse( "2017-08-08T14:00:00Z" ), - //2017-08-08T14:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T14:00:00Z" ), - //2017-08-08T14:00:00Z - Instant.parse( "2017-08-08T15:00:00Z" ), - //2017-08-08T15:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T15:00:00Z" ), - //2017-08-08T15:00:00Z - Instant.parse( "2017-08-08T16:00:00Z" ), - //2017-08-08T16:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T16:00:00Z" ), - //2017-08-08T16:00:00Z - Instant.parse( "2017-08-08T17:00:00Z" ), - //2017-08-08T17:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T17:00:00Z" ), - //2017-08-08T17:00:00Z - Instant.parse( "2017-08-08T18:00:00Z" ), - //2017-08-08T18:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T18:00:00Z" ), - //2017-08-08T18:00:00Z - Instant.parse( "2017-08-08T19:00:00Z" ), - //2017-08-08T19:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T19:00:00Z" ), - //2017-08-08T19:00:00Z - Instant.parse( "2017-08-08T20:00:00Z" ), - //2017-08-08T20:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T20:00:00Z" ), - //2017-08-08T20:00:00Z - Instant.parse( "2017-08-08T21:00:00Z" ), - //2017-08-08T21:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T21:00:00Z" ), - //2017-08-08T21:00:00Z - Instant.parse( "2017-08-08T22:00:00Z" ), - //2017-08-08T22:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T22:00:00Z" ), - //2017-08-08T22:00:00Z - Instant.parse( INSTANT_TWO ), - //2017-08-08T23:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - - // Generate the actual time windows for the explicit test - Set actualTimeWindows = DeclarationUtilities.getTimeWindows( declaration ); - - // Assert the expected cardinality - assertEquals( 23, actualTimeWindows.size() ); - - // Assert that the expected and actual time windows are equal - assertEquals( expectedTimeWindows, actualTimeWindows ); - - // Declare the same version of this test with implicit frequency - TimePools referenceTimePoolsNoFreq = TimePoolsBuilder.builder() - .period( Duration.ofHours( 1 ) ) - .build(); - EvaluationDeclaration declarationNoFreq = EvaluationDeclarationBuilder.builder() - .leadTimes( leadTimes ) - .leadTimePools( leadTimePools ) - .referenceDates( referenceDates ) - .referenceDatePools( - referenceTimePoolsNoFreq ) - .validDates( validDates ) - .build(); - - // Generate the actual time windows for the implicit test - Set actualTimeWindowsNoFreq = DeclarationUtilities.getTimeWindows( declarationNoFreq ); - - // Assert that the expected and actual time windows are equal - assertEquals( expectedTimeWindows, actualTimeWindowsNoFreq ); - } - - /** - *

Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} where the project declaration - * does not include any explicit time windows, but is constrained by lead_times, - * reference_dates and dates, aka "one big pool" with constraints. - */ - - @Test - void testGetTimeWindowsWithLeadTimesValidDatesAndReferenceDatesReturnsOneWindow() - { - LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() - .minimum( Duration.ofHours( 0 ) ) - .maximum( Duration.ofHours( 18 ) ) - .build(); - TimeInterval referenceDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_ONE ) ) - .maximum( Instant.parse( INSTANT_TWO ) ) - .build(); - TimeInterval validDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_ONE ) ) - .maximum( Instant.parse( INSTANT_THREE ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .leadTimes( leadTimes ) - .referenceDates( referenceDates ) - .validDates( validDates ) - .build(); - - // Generate the expected windows - Set expectedTimeWindows = new HashSet<>( 1 ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_TWO ), - //2017-08-08T23:00:00Z - Instant.parse( INSTANT_ONE ), - //2017-08-08T00:00:00Z - Instant.parse( INSTANT_THREE ), - //2017-08-09T17:00:00Z - Duration.ofHours( 0 ), - Duration.ofHours( 18 ) ) ); - - // Generate the actual windows - Set actualTimeWindows = DeclarationUtilities.getTimeWindows( declaration ); - - // Assert the expected cardinality - assertEquals( 1, actualTimeWindows.size() ); - - // Assert that the expected and actual are equal - assertEquals( expectedTimeWindows, actualTimeWindows ); - } - - /** - *

Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} where the project declaration - * includes a lead_times, a an reference_dates, and an referenceDatesPoolingWindow. - * Expects nine time windows with prescribed characteristics. - * - *

The project declaration from this test scenario is similar to the declaration associated - * with system test scenario505, as of commit c8def0cf2d608c0617786f7cb4f28b563960d667, but without - * a lead_time_pools. - */ - - @Test - void testGetTimeWindowsWithLeadTimesReferenceDatesAndReferenceDatesPoolingWindowAndNoLeadTimePoolsReturnsNineWindows() - { - LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() - .minimum( Duration.ofHours( 0 ) ) - .maximum( Duration.ofHours( 40 ) ) - .build(); - TimeInterval referenceDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_NINE ) ) - .maximum( Instant.parse( INSTANT_TEN ) ) - .build(); - TimePools referenceTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 13 ) ) - .frequency( Duration.ofHours( 7 ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .leadTimes( leadTimes ) - .referenceDates( referenceDates ) - .referenceDatePools( referenceTimePools ) - .build(); - - // Generate the expected windows - Set expectedTimeWindows = new HashSet<>( 9 ); - - Duration first = Duration.ofHours( 0 ); - Duration last = Duration.ofHours( 40 ); - - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINE ), - //2551-03-17T00:00:00Z - Instant.parse( INSTANT_TWELVE ), - //2551-03-17T13:00:00Z - first, - last ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_THIRTEEN ), - //2551-03-17T07:00:00Z - Instant.parse( INSTANT_FOURTEEN ), - //2551-03-17T20:00:00Z - first, - last ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_FIFTEEN ), - //2551-03-17T14:00:00Z - Instant.parse( INSTANT_SIXTEEN ), - //2551-03-18T03:00:00Z - first, - last ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_SEVENTEEN ), - //2551-03-17T21:00:00Z - Instant.parse( INSTANT_EIGHTEEN ), - //2551-03-18T10:00:00Z - first, - last ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINETEEN ), - //2551-03-18T04:00:00Z - Instant.parse( INSTANT_TWENTY ), - //2551-03-18T17:00:00Z - first, - last ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_ONE ), - //2551-03-18T11:00:00Z - Instant.parse( INSTANT_ELEVEN ), - //2551-03-19T00:00:00Z - first, - last ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_TWO ), - //2551-03-18T18:00:00Z - Instant.parse( INSTANT_TWENTY_THREE ), - //2551-03-19T07:00:00Z - first, - last ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_FOUR ), - //2551-03-19T01:00:00Z - Instant.parse( INSTANT_TWENTY_FIVE ), - //2551-03-19T14:00:00Z - first, - last ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_SIX ), - //2551-03-19T08:00:00Z - Instant.parse( INSTANT_TWENTY_SEVEN ), - //2551-03-19T21:00:00Z - first, - last ) ); - - // Generate the actual windows - Set actualTimeWindows = DeclarationUtilities.getTimeWindows( declaration ); - - // Assert the expected cardinality - assertEquals( 9, actualTimeWindows.size() ); - - // Assert that the expected and actual are equal - assertEquals( expectedTimeWindows, actualTimeWindows ); - } - - /** - *

Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} where the project declaration - * includes a lead_times and an lead_time_pools and the minimum and - * maximum lead hours are the same value and the period associated with the - * lead_time_pools is zero wide. This is equivalent to system test scenario010 as of commit - * 8480aa4d4ddc09275746fe590623ecfd83e452ae and is used to check that a zero-wide pool centered on a single lead - * duration does not increment infinitely. - */ - - @Test - void testGetTimeWindowsWithZeroWideLeadTimesAndLeadTimePoolsWithZeroPeriodReturnsOneWindow() - { - LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() - .minimum( Duration.ofHours( 43 ) ) - .maximum( Duration.ofHours( 43 ) ) - .build(); - TimePools leadTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 0 ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .leadTimes( leadTimes ) - .leadTimePools( leadTimePools ) - .build(); - - // Generate the expected windows - Set expectedTimeWindows = new HashSet<>( 1 ); - - Duration first = Duration.ofHours( 43 ); - Duration last = Duration.ofHours( 43 ); - - TimeWindow inner = MessageFactory.getTimeWindow( first, - last ); - expectedTimeWindows.add( inner ); - - // Generate the actual windows - Set actualTimeWindows = DeclarationUtilities.getTimeWindows( declaration ); - - // Assert the expected cardinality - assertEquals( 1, actualTimeWindows.size() ); - - // Assert that the expected and actual are equal - assertEquals( expectedTimeWindows, actualTimeWindows ); - } - - @Test - void testGetTimeWindowsWithValidDatesAndValidDatePoolsReturnsTwoWindows() - { - TimeInterval validDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_ONE ) ) - .maximum( Instant.parse( INSTANT_TWO ) ) - .build(); - TimePools validTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 13 ) ) - .frequency( Duration.ofHours( 7 ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .validDates( validDates ) - .validDatePools( validTimePools ) - .build(); - - // Generate the actual windows - Set actualTimeWindows = DeclarationUtilities.getTimeWindows( declaration ); - - // Assert the expected cardinality - assertEquals( 2, actualTimeWindows.size() ); - - // Generate the expected windows - Set expectedTimeWindows = new HashSet<>( 2 ); - - TimeWindow innerOne = MessageFactory.getTimeWindow( Instant.parse( INSTANT_ONE ), - Instant.parse( "2017-08-08T13:00:00Z" ) ); - TimeWindow innerTwo = MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T07:00:00Z" ), - Instant.parse( "2017-08-08T20:00:00Z" ) ); - - expectedTimeWindows.add( innerOne ); - expectedTimeWindows.add( innerTwo ); - - // Assert that the expected and actual are equal - assertEquals( expectedTimeWindows, actualTimeWindows ); - } - - @Test - void testGetTimeWindowsWithLeadTimesAndLeadTimePoolsAndValidDatesAndValidDatePoolsReturnsFourWindows() - { - LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() - .minimum( Duration.ofHours( 19 ) ) - .maximum( Duration.ofHours( 34 ) ) - .build(); - TimePools leadTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 8 ) ) - .frequency( Duration.ofHours( 7 ) ) - .build(); - TimeInterval validDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_ONE ) ) - .maximum( Instant.parse( INSTANT_TWO ) ) - .build(); - TimePools validTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 13 ) ) - .frequency( Duration.ofHours( 7 ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .validDates( validDates ) - .validDatePools( validTimePools ) - .leadTimes( leadTimes ) - .leadTimePools( leadTimePools ) - .build(); - - // Generate the actual windows - Set actualTimeWindows = DeclarationUtilities.getTimeWindows( declaration ); - - // Assert the expected cardinality - assertEquals( 4, actualTimeWindows.size() ); - - // Generate the expected windows - Set expectedTimeWindows = new HashSet<>( 4 ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.MIN, - Instant.MAX, - Instant.parse( INSTANT_ONE ), - Instant.parse( "2017-08-08T13:00:00Z" ), - Duration.ofHours( 19 ), - Duration.ofHours( 27 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.MIN, - Instant.MAX, - Instant.parse( "2017-08-08T07:00:00Z" ), - Instant.parse( "2017-08-08T20:00:00Z" ), - Duration.ofHours( 19 ), - Duration.ofHours( 27 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.MIN, - Instant.MAX, - Instant.parse( INSTANT_ONE ), - Instant.parse( "2017-08-08T13:00:00Z" ), - Duration.ofHours( 26 ), - Duration.ofHours( 34 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.MIN, - Instant.MAX, - Instant.parse( "2017-08-08T07:00:00Z" ), - Instant.parse( "2017-08-08T20:00:00Z" ), - Duration.ofHours( 26 ), - Duration.ofHours( 34 ) ) ); - - // Assert that the expected and actual are equal - assertEquals( expectedTimeWindows, actualTimeWindows ); - } - - @Test - void testGetTimeWindowsWithLeadTimesAndLeadTimePoolsAndValidDatesAndValidDatePoolsAndReferenceDatesAndReferenceDatePoolsReturnsFourWindows() - { - LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() - .minimum( Duration.ofHours( 19 ) ) - .maximum( Duration.ofHours( 34 ) ) - .build(); - TimePools leadTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 8 ) ) - .frequency( Duration.ofHours( 7 ) ) - .build(); - TimeInterval referenceDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_TWO ) ) - .maximum( Instant.parse( INSTANT_THREE ) ) - .build(); - TimePools referenceTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 17 ) ) - .frequency( Duration.ofHours( 23 ) ) - .build(); - TimeInterval validDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_ONE ) ) - .maximum( Instant.parse( INSTANT_TWO ) ) - .build(); - TimePools validTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 13 ) ) - .frequency( Duration.ofHours( 7 ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .validDates( validDates ) - .validDatePools( validTimePools ) - .referenceDates( referenceDates ) - .referenceDatePools( referenceTimePools ) - .leadTimes( leadTimes ) - .leadTimePools( leadTimePools ) - .build(); - - // Generate the actual windows - Set actualTimeWindows = DeclarationUtilities.getTimeWindows( declaration ); - - // Assert the expected cardinality - assertEquals( 4, actualTimeWindows.size() ); - - // Generate the expected windows - Set expectedTimeWindows = new HashSet<>( 4 ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T23:00:00Z" ), - Instant.parse( "2017-08-09T16:00:00Z" ), - Instant.parse( INSTANT_ONE ), - Instant.parse( "2017-08-08T13:00:00Z" ), - Duration.ofHours( 19 ), - Duration.ofHours( 27 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T23:00:00Z" ), - Instant.parse( "2017-08-09T16:00:00Z" ), - Instant.parse( "2017-08-08T07:00:00Z" ), - Instant.parse( "2017-08-08T20:00:00Z" ), - Duration.ofHours( 19 ), - Duration.ofHours( 27 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T23:00:00Z" ), - Instant.parse( "2017-08-09T16:00:00Z" ), - Instant.parse( INSTANT_ONE ), - Instant.parse( "2017-08-08T13:00:00Z" ), - Duration.ofHours( 26 ), - Duration.ofHours( 34 ) ) ); - expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T23:00:00Z" ), - Instant.parse( "2017-08-09T16:00:00Z" ), - Instant.parse( "2017-08-08T07:00:00Z" ), - Instant.parse( "2017-08-08T20:00:00Z" ), - Duration.ofHours( 26 ), - Duration.ofHours( 34 ) ) ); - - // Assert that the expected and actual are equal - assertEquals( expectedTimeWindows, actualTimeWindows ); - } - - /** - * Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} for an expected exception when - * lead_times are required but missing. - */ - - @Test - void testGetTimeWindowsThrowsNullPointerExceptionWhenLeadTimesExpectedButMissing() - { - TimePools leadTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 18 ) ) - .build(); - - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .leadTimePools( leadTimePools ) - .build(); - - NullPointerException thrown = assertThrows( NullPointerException.class, - () -> DeclarationUtilities.getTimeWindows( declaration ) ); - - assertEquals( "Cannot determine lead duration time windows without 'lead_times'.", - thrown.getMessage() ); - } - - /** - * Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} for an expected exception when the - * minimum lead_times is required but missing. - */ - - @Test - void testGetTimeWindowsThrowsNullPointerExceptionWhenLeadTimesMinimumExpectedButMissing() - { - LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() - .maximum( Duration.ofHours( 40 ) ) - .build(); - TimePools leadTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 18 ) ) - .build(); - - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .leadTimes( leadTimes ) - .leadTimePools( leadTimePools ) - .build(); - - NullPointerException thrown = assertThrows( NullPointerException.class, - () -> DeclarationUtilities.getTimeWindows( declaration ) ); - - assertEquals( "Cannot determine lead duration time windows without a 'minimum' value for 'lead_times'.", - thrown.getMessage() ); - } - - /** - * Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} for an expected exception when the - * maximum lead_times is required but missing. - */ - - @Test - void testGetTimeWindowsThrowsNullPointerExceptionWhenLeadTimesMaximumExpectedButMissing() - { - LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() - .minimum( Duration.ofHours( 0 ) ) - .build(); - TimePools leadTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 18 ) ) - .build(); - - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .leadTimes( leadTimes ) - .leadTimePools( leadTimePools ) - .build(); - - NullPointerException thrown = assertThrows( NullPointerException.class, - () -> DeclarationUtilities.getTimeWindows( declaration ) ); - - assertEquals( "Cannot determine lead duration time windows without a 'maximum' value for 'lead_times'.", - thrown.getMessage() ); - } - - /** - * Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} for an expected exception when - * reference_dates is required but missing. - */ - - @Test - void testGetTimeWindowsThrowsNullPointerExceptionWhenReferenceDatesExpectedButMissing() - { - TimePools referenceTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 13 ) ) - .frequency( Duration.ofHours( 7 ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .referenceDatePools( referenceTimePools ) - .build(); - - NullPointerException thrown = assertThrows( NullPointerException.class, - () -> DeclarationUtilities.getTimeWindows( declaration ) ); - - assertEquals( "Cannot determine reference time windows without 'reference_dates'.", - thrown.getMessage() ); - } - - /** - * Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} for an expected exception when the - * minimum reference_dates is required but missing. - */ - - @Test - void testGetTimeWindowsThrowsNullPointerExceptionWhenReferenceDatesEarliestExpectedButMissing() - { - TimeInterval referenceDates = TimeIntervalBuilder.builder() - .maximum( Instant.parse( INSTANT_ONE ) ) - .build(); - TimePools referenceTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 13 ) ) - .frequency( Duration.ofHours( 7 ) ) - .build(); - - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .referenceDates( referenceDates ) - .referenceDatePools( referenceTimePools ) - .build(); - - NullPointerException thrown = assertThrows( NullPointerException.class, - () -> DeclarationUtilities.getTimeWindows( declaration ) ); - - assertEquals( "Cannot determine reference time windows without the 'minimum' for the 'reference_dates'.", - thrown.getMessage() ); - } - - /** - * Tests the {@link DeclarationUtilities#getTimeWindows(EvaluationDeclaration)} for an expected exception when the - * maximum reference_dates is required but missing. - */ - - @Test - void testGetTimeWindowsThrowsNullPointerExceptionWhenReferenceDatesLatestExpectedButMissing() - { - TimeInterval referenceDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_ONE ) ) - .build(); - TimePools referenceTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 13 ) ) - .frequency( Duration.ofHours( 7 ) ) - .build(); - - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .referenceDates( referenceDates ) - .referenceDatePools( referenceTimePools ) - .build(); - - NullPointerException thrown = assertThrows( NullPointerException.class, - () -> DeclarationUtilities.getTimeWindows( declaration ) ); - - assertEquals( "Cannot determine reference time windows without the 'maximum' for the 'reference_dates'.", - thrown.getMessage() ); - } - - @Test - void testGetTimeWindowsThrowsNullPointerExceptionWhenValidDatesExpectedButMissing() - { - TimePools validTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 13 ) ) - .frequency( Duration.ofHours( 7 ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .validDatePools( validTimePools ) - .build(); - - NullPointerException thrown = assertThrows( NullPointerException.class, - () -> DeclarationUtilities.getTimeWindows( declaration ) ); - - assertEquals( "Cannot determine valid time windows without 'valid_dates'.", - thrown.getMessage() ); - } - - @Test - void testGetTimeWindowsThrowsNullPointerExceptionWhenValidDatesEarliestExpectedButMissing() - { - TimeInterval validDates = TimeIntervalBuilder.builder() - .maximum( Instant.parse( INSTANT_ONE ) ) - .build(); - TimePools validTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 13 ) ) - .frequency( Duration.ofHours( 7 ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .validDates( validDates ) - .validDatePools( validTimePools ) - .build(); - - NullPointerException thrown = assertThrows( NullPointerException.class, - () -> DeclarationUtilities.getTimeWindows( declaration ) ); - - assertEquals( "Cannot determine valid time windows without the 'minimum' for the 'valid_dates'.", - thrown.getMessage() ); - } - - @Test - void testGetTimeWindowsThrowsNullPointerExceptionWhenValidDatesLatestExpectedButMissing() - { - TimeInterval validDates = TimeIntervalBuilder.builder() - .minimum( Instant.parse( INSTANT_ONE ) ) - .build(); - TimePools validTimePools = TimePoolsBuilder.builder() - .period( Duration.ofHours( 13 ) ) - .frequency( Duration.ofHours( 7 ) ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .validDates( validDates ) - .validDatePools( validTimePools ) - .build(); - - NullPointerException thrown = assertThrows( NullPointerException.class, - () -> DeclarationUtilities.getTimeWindows( declaration ) ); - - assertEquals( "Cannot determine valid time windows without the 'maximum' for the 'valid_dates'.", - thrown.getMessage() ); - } - @Test void testHasBaselineReturnsTrue() { diff --git a/wres-datamodel/src/wres/datamodel/pools/PoolSlicer.java b/wres-datamodel/src/wres/datamodel/pools/PoolSlicer.java index 3859f278bd..03d8a1b84e 100644 --- a/wres-datamodel/src/wres/datamodel/pools/PoolSlicer.java +++ b/wres-datamodel/src/wres/datamodel/pools/PoolSlicer.java @@ -19,6 +19,7 @@ import wres.datamodel.time.Event; import wres.datamodel.time.TimeSeries; +import wres.datamodel.time.TimeWindowSlicer; import wres.datamodel.types.Climatology; import wres.datamodel.Slicer; import wres.datamodel.messages.MessageFactory; @@ -660,7 +661,7 @@ public static PoolMetadata unionOf( List input ) if ( !unionWindows.isEmpty() ) { - TimeWindowOuter unionWindow = TimeWindowOuter.unionOf( unionWindows ); + TimeWindowOuter unionWindow = TimeWindowSlicer.union( unionWindows ); builder.setTimeWindow( unionWindow.getTimeWindow() ); } diff --git a/wres-datamodel/src/wres/datamodel/time/TimeSeriesSlicer.java b/wres-datamodel/src/wres/datamodel/time/TimeSeriesSlicer.java index 438ea98b79..688b45c63a 100644 --- a/wres-datamodel/src/wres/datamodel/time/TimeSeriesSlicer.java +++ b/wres-datamodel/src/wres/datamodel/time/TimeSeriesSlicer.java @@ -1615,84 +1615,6 @@ public static TimeSeries snip( TimeSeries toSnip, return snipped; } - /** - * Subtracts any non-instantaneous desired timescale from the earliest lead duration and the earliest valid time to - * ensure that sufficient data is retrieved for upscaling. - * - * @param timeWindow the time window to adjust - * @param timeScale the timescale to use - * @return the adjusted time window - */ - - public static TimeWindowOuter adjustTimeWindowForTimeScale( TimeWindowOuter timeWindow, - TimeScaleOuter timeScale ) - { - TimeWindow.Builder adjusted = timeWindow.getTimeWindow() - .toBuilder(); - - // Earliest lead duration - if ( !timeWindow.getEarliestLeadDuration() - .equals( TimeWindowOuter.DURATION_MIN ) ) - { - Duration period = Duration.ZERO; - - // Adjust the lower bound of the lead duration window by the non-instantaneous desired timescale - if ( Objects.nonNull( timeScale ) - && !timeScale.isInstantaneous() ) - { - period = TimeScaleOuter.getOrInferPeriodFromTimeScale( timeScale ); - } - - Duration lowered = timeWindow.getEarliestLeadDuration() - .minus( period ); - - if ( Objects.nonNull( timeScale ) - && LOGGER.isDebugEnabled() ) - { - LOGGER.debug( "Adjusting the lower lead duration of time window {} from {} to {} " - + "in order to acquire data at the desired timescale of {}.", - timeWindow, - timeWindow.getEarliestLeadDuration(), - lowered, - timeScale ); - } - - adjusted.setEarliestLeadDuration( MessageFactory.getDuration( lowered ) ); - } - - // Earliest valid time - if ( !timeWindow.getEarliestValidTime() - .equals( Instant.MIN ) ) - { - Duration period = Duration.ZERO; - - // Adjust the lower bound of the lead duration window by the non-instantaneous desired timescale - if ( Objects.nonNull( timeScale ) - && !timeScale.isInstantaneous() ) - { - period = TimeScaleOuter.getOrInferPeriodFromTimeScale( timeScale ); - } - - Instant lowered = timeWindow.getEarliestValidTime() - .minus( period ); - - if ( Objects.nonNull( timeScale ) - && LOGGER.isDebugEnabled() ) - { - LOGGER.debug( "Adjusting the lower valid datetime of time window {} from {} to {} " - + "in order to acquire data at the desired timescale of {}.", - timeWindow, - timeWindow.getEarliestValidTime(), - lowered, - timeScale ); - } - - adjusted.setEarliestValidTime( MessageFactory.getTimestamp( lowered ) ); - } - - return TimeWindowOuter.of( adjusted.build() ); - } - /** * Adds a prescribed offset to the valid time of each time-series in the list. * diff --git a/wres-datamodel/src/wres/datamodel/time/TimeWindowOuter.java b/wres-datamodel/src/wres/datamodel/time/TimeWindowOuter.java index f603524a35..fbeac08ecb 100644 --- a/wres-datamodel/src/wres/datamodel/time/TimeWindowOuter.java +++ b/wres-datamodel/src/wres/datamodel/time/TimeWindowOuter.java @@ -2,9 +2,7 @@ import java.time.Duration; import java.time.Instant; -import java.util.HashSet; import java.util.Objects; -import java.util.Set; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -104,80 +102,6 @@ public static TimeWindowOuter of( TimeWindow timeWindow ) return newInstance; } - /** - * Returns a {@link TimeWindowOuter} that represents the union of the inputs, specifically where the - * {@link #getEarliestReferenceTime()} and {@link #getLatestReferenceTime()} are the earliest and latest instances, - * respectively, and likewise for the {@link #getEarliestValidTime()} and {@link #getLatestValidTime()}, and the - * {@link #getEarliestLeadDuration()} and {@link #getLatestLeadDuration()}. - * - * @param input the input windows - * @return the union of the inputs with respect to dates and lead times - * @throws NullPointerException if the input is null - * @throws IllegalArgumentException if the input is empty - * @throws NullPointerException if any input is null - */ - - public static TimeWindowOuter unionOf( Set input ) - { - Objects.requireNonNull( input, "Cannot determine the union of time windows for a null input." ); - - if ( input.isEmpty() ) - { - throw new IllegalArgumentException( "Cannot determine the union of time windows for empty input." ); - } - - if ( new HashSet<>( input ).contains( null ) ) - { - throw new IllegalArgumentException( "Cannot determine the union of time windows for input that contains " - + "one or more null time windows." ); - } - - // Check and set time parameters - TimeWindowOuter first = input.iterator().next(); - Instant earliestR = first.getEarliestReferenceTime(); - Instant latestR = first.getLatestReferenceTime(); - Instant earliestV = first.getEarliestValidTime(); - Instant latestV = first.getLatestValidTime(); - Duration earliestL = first.getEarliestLeadDuration(); - Duration latestL = first.getLatestLeadDuration(); - - for ( TimeWindowOuter next : input ) - { - if ( earliestR.isAfter( next.getEarliestReferenceTime() ) ) - { - earliestR = next.getEarliestReferenceTime(); - } - if ( latestR.isBefore( next.getLatestReferenceTime() ) ) - { - latestR = next.getLatestReferenceTime(); - } - if ( earliestL.compareTo( next.getEarliestLeadDuration() ) > 0 ) - { - earliestL = next.getEarliestLeadDuration(); - } - if ( latestL.compareTo( next.getLatestLeadDuration() ) < 0 ) - { - latestL = next.getLatestLeadDuration(); - } - if ( earliestV.isAfter( next.getEarliestValidTime() ) ) - { - earliestV = next.getEarliestValidTime(); - } - if ( latestV.isBefore( next.getLatestValidTime() ) ) - { - latestV = next.getLatestValidTime(); - } - } - - TimeWindow unionWindow = wres.statistics.MessageFactory.getTimeWindow( earliestR, - latestR, - earliestV, - latestV, - earliestL, - latestL ); - return TimeWindowOuter.of( unionWindow ); - } - @Override public int compareTo( TimeWindowOuter o ) { diff --git a/wres-datamodel/src/wres/datamodel/time/TimeWindowSlicer.java b/wres-datamodel/src/wres/datamodel/time/TimeWindowSlicer.java new file mode 100644 index 0000000000..6f530a0f4d --- /dev/null +++ b/wres-datamodel/src/wres/datamodel/time/TimeWindowSlicer.java @@ -0,0 +1,974 @@ +package wres.datamodel.time; + +import java.time.Duration; +import java.time.Instant; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import com.google.protobuf.Timestamp; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import wres.config.yaml.components.EvaluationDeclaration; +import wres.config.yaml.components.LeadTimeInterval; +import wres.config.yaml.components.TimeInterval; +import wres.config.yaml.components.TimePools; +import wres.datamodel.scale.TimeScaleOuter; +import wres.statistics.MessageFactory; +import wres.statistics.generated.TimeWindow; + +/** + * Utility class for manipulating time windows. + * + * @author James Brown + */ +public class TimeWindowSlicer +{ + + /** Logger. */ + private static final Logger LOGGER = LoggerFactory.getLogger( TimeWindowSlicer.class ); + + /** Re-used message. */ + private static final String CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION = "Cannot determine time " + + "windows from missing " + + "declaration."; + + /** + * Returns a {@link TimeWindowOuter} that represents the union of the inputs, specifically where the + * {@link TimeWindowOuter#getEarliestReferenceTime()} and {@link TimeWindowOuter#getLatestReferenceTime()} are the + * earliest and latest instances, respectively, and likewise for the {@link TimeWindowOuter#getEarliestValidTime()} + * and {@link TimeWindowOuter#getLatestValidTime()}, and the {@link TimeWindowOuter#getEarliestLeadDuration()} and + * {@link TimeWindowOuter#getLatestLeadDuration()}. + * + * @param input the input windows + * @return the union of the inputs with respect to dates and lead times + * @throws NullPointerException if the input is null + * @throws IllegalArgumentException if the input is empty + * @throws NullPointerException if any input is null + */ + + public static TimeWindowOuter union( Set input ) + { + Objects.requireNonNull( input, "Cannot determine the union of time windows for a null input." ); + + if ( input.isEmpty() ) + { + throw new IllegalArgumentException( "Cannot determine the union of time windows for empty input." ); + } + + if ( new HashSet<>( input ).contains( null ) ) + { + throw new IllegalArgumentException( "Cannot determine the union of time windows for input that contains " + + "one or more null time windows." ); + } + + // Check and set time parameters + TimeWindowOuter first = input.iterator().next(); + Instant earliestR = first.getEarliestReferenceTime(); + Instant latestR = first.getLatestReferenceTime(); + Instant earliestV = first.getEarliestValidTime(); + Instant latestV = first.getLatestValidTime(); + Duration earliestL = first.getEarliestLeadDuration(); + Duration latestL = first.getLatestLeadDuration(); + + for ( TimeWindowOuter next : input ) + { + if ( earliestR.isAfter( next.getEarliestReferenceTime() ) ) + { + earliestR = next.getEarliestReferenceTime(); + } + if ( latestR.isBefore( next.getLatestReferenceTime() ) ) + { + latestR = next.getLatestReferenceTime(); + } + if ( earliestL.compareTo( next.getEarliestLeadDuration() ) > 0 ) + { + earliestL = next.getEarliestLeadDuration(); + } + if ( latestL.compareTo( next.getLatestLeadDuration() ) < 0 ) + { + latestL = next.getLatestLeadDuration(); + } + if ( earliestV.isAfter( next.getEarliestValidTime() ) ) + { + earliestV = next.getEarliestValidTime(); + } + if ( latestV.isBefore( next.getLatestValidTime() ) ) + { + latestV = next.getLatestValidTime(); + } + } + + TimeWindow unionWindow = wres.statistics.MessageFactory.getTimeWindow( earliestR, + latestR, + earliestV, + latestV, + earliestL, + latestL ); + return TimeWindowOuter.of( unionWindow ); + } + + /** + * Returns the intersection of the two sets of {@link TimeWindowOuter}. One {@link TimeWindowOuter} intersects + * another if it overlaps across all the non-default time dimensions. + * @param first the first set + * @param second the second set + * @return the intersection of the two sets + * @throws NullPointerException if either input is null + */ + public static Set intersection( Set first, + Set second ) + { + Objects.requireNonNull( first ); + Objects.requireNonNull( second ); + + // Short circuit + if ( first.isEmpty() || second.isEmpty() ) + { + LOGGER.debug( "One or both of the sets to intersect was empty." ); + + return Set.of(); + } + + Set intersected = new HashSet<>(); + + for ( TimeWindowOuter outer : first ) + { + for ( TimeWindowOuter inner : second ) + { + if ( TimeWindowSlicer.intersects( outer, inner ) ) + { + intersected.add( outer ); + intersected.add( inner ); + } + } + } + + return Collections.unmodifiableSet( intersected ); + } + + /** + * Subtracts any non-instantaneous desired timescale from the earliest lead duration and the earliest valid time to + * ensure that sufficient data is retrieved for upscaling. + * + * @param timeWindow the time window to adjust + * @param timeScale the timescale to use + * @return the adjusted time window + */ + + public static TimeWindowOuter adjustTimeWindowForTimeScale( TimeWindowOuter timeWindow, + TimeScaleOuter timeScale ) + { + TimeWindow.Builder adjusted = timeWindow.getTimeWindow() + .toBuilder(); + + // Earliest lead duration + if ( !timeWindow.getEarliestLeadDuration() + .equals( TimeWindowOuter.DURATION_MIN ) ) + { + Duration period = Duration.ZERO; + + // Adjust the lower bound of the lead duration window by the non-instantaneous desired timescale + if ( Objects.nonNull( timeScale ) + && !timeScale.isInstantaneous() ) + { + period = TimeScaleOuter.getOrInferPeriodFromTimeScale( timeScale ); + } + + Duration lowered = timeWindow.getEarliestLeadDuration() + .minus( period ); + + if ( Objects.nonNull( timeScale ) + && LOGGER.isDebugEnabled() ) + { + LOGGER.debug( "Adjusting the lower lead duration of time window {} from {} to {} " + + "in order to acquire data at the desired timescale of {}.", + timeWindow, + timeWindow.getEarliestLeadDuration(), + lowered, + timeScale ); + } + + adjusted.setEarliestLeadDuration( MessageFactory.getDuration( lowered ) ); + } + + // Earliest valid time + if ( !timeWindow.getEarliestValidTime() + .equals( Instant.MIN ) ) + { + Duration period = Duration.ZERO; + + // Adjust the lower bound of the lead duration window by the non-instantaneous desired timescale + if ( Objects.nonNull( timeScale ) + && !timeScale.isInstantaneous() ) + { + period = TimeScaleOuter.getOrInferPeriodFromTimeScale( timeScale ); + } + + Instant lowered = timeWindow.getEarliestValidTime() + .minus( period ); + + if ( Objects.nonNull( timeScale ) + && LOGGER.isDebugEnabled() ) + { + LOGGER.debug( "Adjusting the lower valid datetime of time window {} from {} to {} " + + "in order to acquire data at the desired timescale of {}.", + timeWindow, + timeWindow.getEarliestValidTime(), + lowered, + timeScale ); + } + + adjusted.setEarliestValidTime( MessageFactory.getTimestamp( lowered ) ); + } + + return TimeWindowOuter.of( adjusted.build() ); + } + + /** + * Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindowOuter} for evaluation. + * Only returns the time windows that can be inferred from the declaration alone and not from any ingested data + * sources, notably those associated with event detection. + * + * @param declaration the declaration, cannot be null + * @return a set of one or more time windows for evaluation + * @throws NullPointerException if any required input is null + */ + + public static Set getTimeWindows( EvaluationDeclaration declaration ) + { + Objects.requireNonNull( declaration, CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION ); + + TimePools leadDurationPools = declaration.leadTimePools(); + TimePools referenceDatesPools = declaration.referenceDatePools(); + TimePools validDatesPools = declaration.validDatePools(); + + Set timeWindows = Set.of(); // Default to none + + // Add the time windows generated from a declared sequence + if ( Objects.nonNull( leadDurationPools ) + || Objects.nonNull( referenceDatesPools ) + || Objects.nonNull( validDatesPools ) ) + { + timeWindows = TimeWindowSlicer.getTimeWindowsFromPoolSequence( declaration ); + } + // One big pool if no explicitly declared time windows and no event detection + else if ( declaration.timePools() + .isEmpty() + && Objects.isNull( declaration.eventDetection() ) ) + { + TimeWindowSlicer.LOGGER.debug( "Building one big time window." ); + + timeWindows = Collections.singleton( getOneBigTimeWindow( declaration ) ); + } + + // Add the explicitly declared time windows + Set finalWindows = new HashSet<>( timeWindows ); + + TimeWindowSlicer.LOGGER.debug( "Added {} explicitly declared time pools to the overall group of time pools.", + declaration.timePools() + .size() ); + + finalWindows.addAll( declaration.timePools() + .stream() + .map( TimeWindowOuter::of ) + .collect( Collectors.toSet() ) ); + + return Collections.unmodifiableSet( finalWindows ); + } + + /** + *

Builds a {@link TimeWindowOuter} whose {@link TimeWindow#getEarliestReferenceTime()} and + * {@link TimeWindow#getLatestReferenceTime()} return the {@code earliest} and {@code latest} bookends of the + * {@link EvaluationDeclaration#referenceDates()}, respectively, whose {@link TimeWindow#getEarliestValidTime()} + * and {@link TimeWindow#getLatestValidTime()} return the {@code earliest} and {@code latest} bookends of the + * {@link EvaluationDeclaration#validDates()}, respectively, and whose {@link TimeWindow#getEarliestLeadDuration()} + * and {@link TimeWindow#getLatestLeadDuration()} return the {@code minimum} and {@code maximum} bookends of the + * {@link EvaluationDeclaration#leadTimes()}, respectively. + * + *

If any of these variables are missing from the input, defaults are used, which represent the + * computationally-feasible limiting values. For example, the smallest and largest possible instant is + * {@link Instant#MIN} and {@link Instant#MAX}, respectively. The smallest and largest possible {@link Duration} is + * {@link MessageFactory#DURATION_MIN} and {@link MessageFactory#DURATION_MAX}, respectively. + * + * @param declaration the declaration + * @return a time window + * @throws NullPointerException if the pairConfig is null + */ + + public static TimeWindowOuter getOneBigTimeWindow( EvaluationDeclaration declaration ) + { + Objects.requireNonNull( declaration, CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION ); + + Instant earliestReferenceTime = Instant.MIN; + Instant latestReferenceTime = Instant.MAX; + Instant earliestValidTime = Instant.MIN; + Instant latestValidTime = Instant.MAX; + Duration smallestLeadDuration = MessageFactory.DURATION_MIN; + Duration largestLeadDuration = MessageFactory.DURATION_MAX; + + // Reference datetimes + if ( Objects.nonNull( declaration.referenceDates() ) ) + { + if ( Objects.nonNull( declaration.referenceDates() + .minimum() ) ) + { + earliestReferenceTime = declaration.referenceDates() + .minimum(); + } + if ( Objects.nonNull( declaration.referenceDates() + .maximum() ) ) + { + latestReferenceTime = declaration.referenceDates() + .maximum(); + } + } + + // Valid datetimes + if ( Objects.nonNull( declaration.validDates() ) ) + { + if ( Objects.nonNull( declaration.validDates() + .minimum() ) ) + { + earliestValidTime = declaration.validDates() + .minimum(); + } + if ( Objects.nonNull( declaration.validDates() + .maximum() ) ) + { + latestValidTime = declaration.validDates() + .maximum(); + } + } + + // Lead durations + if ( Objects.nonNull( declaration.leadTimes() ) ) + { + if ( Objects.nonNull( declaration.leadTimes() + .minimum() ) ) + { + smallestLeadDuration = declaration.leadTimes() + .minimum(); + } + if ( Objects.nonNull( declaration.leadTimes() + .maximum() ) ) + { + largestLeadDuration = declaration.leadTimes() + .maximum(); + } + } + + TimeWindow timeWindow = MessageFactory.getTimeWindow( earliestReferenceTime, + latestReferenceTime, + earliestValidTime, + latestValidTime, + smallestLeadDuration, + largestLeadDuration ); + + return TimeWindowOuter.of( timeWindow ); + } + + + /** + * Generates time windows from an explicit pool sequence. + * + * @param declaration the declaration + * @return the time windows + */ + private static Set getTimeWindowsFromPoolSequence( EvaluationDeclaration declaration ) + { + TimePools leadDurationPools = declaration.leadTimePools(); + TimePools referenceDatesPools = declaration.referenceDatePools(); + TimePools validDatesPools = declaration.validDatePools(); + + Set timeWindows; + + // All dimensions + if ( Objects.nonNull( referenceDatesPools ) + && Objects.nonNull( validDatesPools ) + && Objects.nonNull( leadDurationPools ) ) + { + LOGGER.debug( "Building time windows for reference dates and valid dates and lead durations." ); + + timeWindows = TimeWindowSlicer.getReferenceDatesValidDatesAndLeadDurationTimeWindows( declaration ); + } + // Reference dates and valid dates + else if ( Objects.nonNull( referenceDatesPools ) + && Objects.nonNull( validDatesPools ) ) + { + LOGGER.debug( "Building time windows for reference dates and valid dates." ); + + timeWindows = TimeWindowSlicer.getReferenceDatesAndValidDatesTimeWindows( declaration ); + } + // Reference dates and lead durations + else if ( Objects.nonNull( referenceDatesPools ) + && Objects.nonNull( leadDurationPools ) ) + { + LOGGER.debug( "Building time windows for reference dates and lead durations." ); + + timeWindows = TimeWindowSlicer.getReferenceDatesAndLeadDurationTimeWindows( declaration ); + } + // Valid dates and lead durations + else if ( Objects.nonNull( validDatesPools ) + && Objects.nonNull( leadDurationPools ) ) + { + LOGGER.debug( "Building time windows for valid dates and lead durations." ); + + timeWindows = TimeWindowSlicer.getValidDatesAndLeadDurationTimeWindows( declaration ); + } + // Reference dates + else if ( Objects.nonNull( referenceDatesPools ) ) + { + LOGGER.debug( "Building time windows for reference dates." ); + + timeWindows = TimeWindowSlicer.getReferenceDatesTimeWindows( declaration ); + } + // Lead durations + else if ( Objects.nonNull( leadDurationPools ) ) + { + LOGGER.debug( "Building time windows for lead durations." ); + + timeWindows = TimeWindowSlicer.getLeadDurationTimeWindows( declaration ); + } + // Valid dates + else + { + LOGGER.debug( "Building time windows for valid dates." ); + + timeWindows = TimeWindowSlicer.getValidDatesTimeWindows( declaration ); + } + + return timeWindows; + } + + /** + *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindowOuter} for evaluation + * using the {@link EvaluationDeclaration#leadTimePools()} and the {@link EvaluationDeclaration#leadTimes()}. + * Returns at least one {@link TimeWindowOuter}. + * + * @param declaration the declaration + * @return the set of lead duration time windows + * @throws NullPointerException if any required input is null + */ + + private static Set getLeadDurationTimeWindows( EvaluationDeclaration declaration ) + { + String messageStart = "Cannot determine lead duration time windows "; + + Objects.requireNonNull( declaration, messageStart + "from null declaration." ); + + LeadTimeInterval leadHours = declaration.leadTimes(); + + Objects.requireNonNull( leadHours, "Cannot determine lead duration time windows without 'lead_times'." ); + Objects.requireNonNull( leadHours.minimum(), + "Cannot determine lead duration time windows without a 'minimum' value for " + + "'lead_times'." ); + Objects.requireNonNull( leadHours.maximum(), + "Cannot determine lead duration time windows without a 'maximum' value for " + + "'lead_times'." ); + + TimePools leadTimesPoolingWindow = declaration.leadTimePools(); + + Objects.requireNonNull( leadTimesPoolingWindow, + "Cannot determine lead duration time windows without a 'lead_time_pools'." ); + + // Obtain the base window + TimeWindowOuter baseWindow = TimeWindowSlicer.getOneBigTimeWindow( declaration ); + + // Period associated with the leadTimesPoolingWindow + Duration periodOfLeadTimesPoolingWindow = leadTimesPoolingWindow.period(); + + // Exclusive lower bound: #56213-104 + Duration earliestLeadDurationExclusive = leadHours.minimum(); + + // Inclusive upper bound + Duration latestLeadDurationInclusive = leadHours.maximum(); + + // Duration by which to increment. Defaults to the period associated + // with the leadTimesPoolingWindow, otherwise the frequency. + Duration increment = periodOfLeadTimesPoolingWindow; + if ( Objects.nonNull( leadTimesPoolingWindow.frequency() ) ) + { + increment = leadTimesPoolingWindow.frequency(); + } + + // Lower bound of the current window + Duration earliestExclusive = earliestLeadDurationExclusive; + + // Upper bound of the current window + Duration latestInclusive = earliestExclusive.plus( periodOfLeadTimesPoolingWindow ); + + // Create the time windows + Set timeWindows = new HashSet<>(); + + // Increment left-to-right and stop when the right bound extends past the + // latestLeadDurationInclusive: #56213-104 + // Window increments are zero? + if ( Duration.ZERO.equals( increment ) ) + { + com.google.protobuf.Duration earliest = MessageFactory.getDuration( earliestExclusive ); + com.google.protobuf.Duration latest = MessageFactory.getDuration( latestInclusive ); + TimeWindow window = baseWindow.getTimeWindow() + .toBuilder() + .setEarliestLeadDuration( earliest ) + .setLatestLeadDuration( latest ) + .build(); + timeWindows.add( TimeWindowOuter.of( window ) ); + } + // Create as many windows as required at the prescribed increment + else + { + while ( latestInclusive.compareTo( latestLeadDurationInclusive ) <= 0 ) + { + // Add the current time window + com.google.protobuf.Duration earliest = MessageFactory.getDuration( earliestExclusive ); + com.google.protobuf.Duration latest = MessageFactory.getDuration( latestInclusive ); + TimeWindow window = baseWindow.getTimeWindow() + .toBuilder() + .setEarliestLeadDuration( earliest ) + .setLatestLeadDuration( latest ) + .build(); + timeWindows.add( TimeWindowOuter.of( window ) ); + + // Increment from left-to-right: #56213-104 + earliestExclusive = earliestExclusive.plus( increment ); + latestInclusive = latestInclusive.plus( increment ); + } + } + + return Collections.unmodifiableSet( timeWindows ); + } + + /** + *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindowOuter} for evaluation + * using the {@link EvaluationDeclaration#referenceDatePools()} and the + * {@link EvaluationDeclaration#referenceDates()}. Returns at least one {@link TimeWindowOuter}. + * + * @param declaration the declaration + * @return the set of reference time windows + * @throws NullPointerException if the declaration is null or any required input within it is null + */ + + private static Set getReferenceDatesTimeWindows( EvaluationDeclaration declaration ) + { + Objects.requireNonNull( declaration, "Cannot determine reference time windows from missing " + + "declaration." ); + Objects.requireNonNull( declaration.referenceDates(), + "Cannot determine reference time windows without 'reference_dates'." ); + Objects.requireNonNull( declaration.referenceDates() + .minimum(), + "Cannot determine reference time windows without the 'minimum' for the " + + "'reference_dates'." ); + Objects.requireNonNull( declaration.referenceDates() + .maximum(), + "Cannot determine reference time windows without the 'maximum' for the " + + "'reference_dates'." ); + Objects.requireNonNull( declaration.referenceDatePools(), + "Cannot determine reference time windows without 'reference_date_pools'." ); + + // Base window from which to generate a sequence of windows + TimeWindowOuter baseWindow = TimeWindowSlicer.getOneBigTimeWindow( declaration ); + + return TimeWindowSlicer.getTimeWindowsForDateSequence( declaration.referenceDates(), + declaration.referenceDatePools(), + baseWindow, + true ); + } + + /** + *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindowOuter} for evaluation + * using the {@link EvaluationDeclaration#validDatePools()} and the {@link EvaluationDeclaration#validDates()}. + * Returns at least one {@link TimeWindowOuter}. + * + * @param declaration the declaration + * @return the set of reference time windows + * @throws NullPointerException if the declaration is null or any required input within it is null + */ + + private static Set getValidDatesTimeWindows( EvaluationDeclaration declaration ) + { + Objects.requireNonNull( declaration, "Cannot determine valid time windows from missing declaration." ); + Objects.requireNonNull( declaration.validDates(), + "Cannot determine valid time windows without 'valid_dates'." ); + Objects.requireNonNull( declaration.validDates() + .minimum(), + "Cannot determine valid time windows without the 'minimum' for the " + + "'valid_dates'." ); + Objects.requireNonNull( declaration.validDates() + .maximum(), + "Cannot determine valid time windows without the 'maximum' for the " + + "'valid_dates'." ); + Objects.requireNonNull( declaration.validDatePools(), + "Cannot determine valid time windows without 'valid_date_pools'." ); + + // Base window from which to generate a sequence of windows + TimeWindowOuter baseWindow = TimeWindowSlicer.getOneBigTimeWindow( declaration ); + + return TimeWindowSlicer.getTimeWindowsForDateSequence( declaration.validDates(), + declaration.validDatePools(), + baseWindow, + false ); + } + + /** + *

Generates a set of time windows based on a sequence of datetimes. + * + * @param dates the date constraints + * @param pools the sequence of datetimes to generate + * @param baseWindow the basic time window from which each pool in the sequence begins + * @param areReferenceTimes is true if the dates are reference dates, false for valid dates + * @return the set of reference time windows + * @throws NullPointerException if any input is null + */ + + private static Set getTimeWindowsForDateSequence( TimeInterval dates, + TimePools pools, + TimeWindowOuter baseWindow, + boolean areReferenceTimes ) + { + Objects.requireNonNull( dates ); + Objects.requireNonNull( pools ); + Objects.requireNonNull( baseWindow ); + + // Period associated with the reference time pool + Duration periodOfPoolingWindow = pools.period(); + + // Exclusive lower bound: #56213-104 + Instant earliestInstantExclusive = dates.minimum(); + + // Inclusive upper bound + Instant latestInstantInclusive = dates.maximum(); + + // Duration by which to increment. Defaults to the period associated with the reference time pools, otherwise + // the frequency. + Duration increment = periodOfPoolingWindow; + if ( Objects.nonNull( pools.frequency() ) ) + { + increment = pools.frequency(); + } + + // Lower bound of the current window + Instant earliestExclusive = earliestInstantExclusive; + + // Upper bound of the current window + Instant latestInclusive = earliestExclusive.plus( periodOfPoolingWindow ); + + // Create the time windows + Set timeWindows = new HashSet<>(); + + // Increment left-to-right and stop when the right bound + // extends past the latestInstantInclusive: #56213-104 + while ( latestInclusive.compareTo( latestInstantInclusive ) <= 0 ) + { + TimeWindowOuter timeWindow = TimeWindowSlicer.getTimeWindowFromDates( earliestExclusive, + latestInclusive, + baseWindow, + areReferenceTimes ); + + // Add the current time window + timeWindows.add( timeWindow ); + + // Increment left-to-right: #56213-104 + earliestExclusive = earliestExclusive.plus( increment ); + latestInclusive = latestInclusive.plus( increment ); + } + + return Collections.unmodifiableSet( timeWindows ); + } + + /** + * Returns a time window from the inputs. + * + * @param earliestExclusive the earliest exclusive time + * @param latestInclusive the latest inclusive time + * @param baseWindow the base window with default times + * @param areReferenceTimes is true if the earliestExclusive and latestInclusive are reference times, false for + * valid times + * @return a time window + */ + + private static TimeWindowOuter getTimeWindowFromDates( Instant earliestExclusive, + Instant latestInclusive, + TimeWindowOuter baseWindow, + boolean areReferenceTimes ) + { + Timestamp earliest = MessageFactory.getTimestamp( earliestExclusive ); + Timestamp latest = MessageFactory.getTimestamp( latestInclusive ); + + // Reference dates + if ( areReferenceTimes ) + { + TimeWindow window = baseWindow.getTimeWindow() + .toBuilder() + .setEarliestReferenceTime( earliest ) + .setLatestReferenceTime( latest ) + .build(); + return TimeWindowOuter.of( window ); + } + // Valid dates + else + { + TimeWindow window = baseWindow.getTimeWindow() + .toBuilder() + .setEarliestValidTime( earliest ) + .setLatestValidTime( latest ) + .build(); + return TimeWindowOuter.of( window ); + } + } + + /** + *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindowOuter} for evaluation + * using the {@link EvaluationDeclaration#leadTimePools()}, the {@link EvaluationDeclaration#leadTimes()}, the + * {@link EvaluationDeclaration#referenceDatePools()} and the {@link EvaluationDeclaration#referenceDates()}. + * Returns at least one {@link TimeWindowOuter}. + * + * @param declaration the declaration + * @return the set of lead duration and reference time windows + * @throws NullPointerException if the declaration is null + */ + + private static Set getReferenceDatesAndLeadDurationTimeWindows( EvaluationDeclaration declaration ) + { + Objects.requireNonNull( declaration, CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION ); + + Set leadDurationWindows = TimeWindowSlicer.getLeadDurationTimeWindows( declaration ); + + Set referenceDatesWindows = TimeWindowSlicer.getReferenceDatesTimeWindows( declaration ); + + // Create a new window for each combination of reference dates and lead duration + Set timeWindows = + new HashSet<>( leadDurationWindows.size() * referenceDatesWindows.size() ); + for ( TimeWindowOuter nextReferenceWindow : referenceDatesWindows ) + { + for ( TimeWindowOuter nextLeadWindow : leadDurationWindows ) + { + TimeWindow window = nextReferenceWindow.getTimeWindow() + .toBuilder() + .setEarliestLeadDuration( nextLeadWindow.getTimeWindow() + .getEarliestLeadDuration() ) + .setLatestLeadDuration( nextLeadWindow.getTimeWindow() + .getLatestLeadDuration() ) + .build(); + timeWindows.add( TimeWindowOuter.of( window ) ); + } + } + + return Collections.unmodifiableSet( timeWindows ); + } + + /** + *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindowOuter} for evaluation + * using the {@link EvaluationDeclaration#referenceDatePools()}, the {@link EvaluationDeclaration#referenceDates()}, + * the {@link EvaluationDeclaration#validDatePools()} and the {@link EvaluationDeclaration#validDates()}. Returns at + * least one {@link TimeWindowOuter}. + * + * @param declaration the declaration + * @return the set of reference time and valid time windows + * @throws NullPointerException if the declaration is null + */ + + private static Set getReferenceDatesAndValidDatesTimeWindows( EvaluationDeclaration declaration ) + { + Objects.requireNonNull( declaration, CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION ); + + Set validDatesWindows = TimeWindowSlicer.getValidDatesTimeWindows( declaration ); + + Set referenceDatesWindows = TimeWindowSlicer.getReferenceDatesTimeWindows( declaration ); + + // Create a new window for each combination of reference dates and lead duration + Set timeWindows = new HashSet<>( validDatesWindows.size() * referenceDatesWindows.size() ); + for ( TimeWindowOuter nextValidWindow : validDatesWindows ) + { + for ( TimeWindowOuter nextReferenceWindow : referenceDatesWindows ) + { + TimeWindow window = + nextValidWindow.getTimeWindow() + .toBuilder() + .setEarliestReferenceTime( nextReferenceWindow.getTimeWindow() + .getEarliestReferenceTime() ) + .setLatestReferenceTime( nextReferenceWindow.getTimeWindow() + .getLatestReferenceTime() ) + .build(); + timeWindows.add( TimeWindowOuter.of( window ) ); + } + } + + return Collections.unmodifiableSet( timeWindows ); + } + + /** + *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindowOuter} for evaluation + * using the {@link EvaluationDeclaration#leadTimePools()} ()}, the {@link EvaluationDeclaration#leadTimes()}, the + * {@link EvaluationDeclaration#validDatePools()} ()} and the {@link EvaluationDeclaration#validDates()}. Returns + * at least one {@link TimeWindowOuter}. + * + * @param declaration the declaration + * @return the set of lead duration and valid dates time windows + * @throws NullPointerException if the pairConfig is null + */ + + private static Set getValidDatesAndLeadDurationTimeWindows( EvaluationDeclaration declaration ) + { + Objects.requireNonNull( declaration, CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION ); + + Set leadDurationWindows = TimeWindowSlicer.getLeadDurationTimeWindows( declaration ); + + Set validDatesWindows = TimeWindowSlicer.getValidDatesTimeWindows( declaration ); + + // Create a new window for each combination of valid dates and lead duration + Set timeWindows = new HashSet<>( leadDurationWindows.size() * validDatesWindows.size() ); + for ( TimeWindowOuter nextValidWindow : validDatesWindows ) + { + for ( TimeWindowOuter nextLeadWindow : leadDurationWindows ) + { + TimeWindow window = + nextValidWindow.getTimeWindow() + .toBuilder() + .setEarliestLeadDuration( nextLeadWindow.getTimeWindow() + .getEarliestLeadDuration() ) + .setLatestLeadDuration( nextLeadWindow.getTimeWindow() + .getLatestLeadDuration() ) + .build(); + timeWindows.add( TimeWindowOuter.of( window ) ); + } + } + + return Collections.unmodifiableSet( timeWindows ); + } + + /** + *

Consumes a {@link EvaluationDeclaration} and returns a {@link Set} of {@link TimeWindowOuter} for evaluation + * using the {@link EvaluationDeclaration#leadTimePools()}, the {@link EvaluationDeclaration#leadTimes()}, the + * {@link EvaluationDeclaration#referenceDatePools()} the {@link EvaluationDeclaration#referenceDates()}, the + * {@link EvaluationDeclaration#validDatePools()} and the {@link EvaluationDeclaration#validDates()}. Returns at + * least one {@link TimeWindowOuter}. + * + * @param declaration the declaration + * @return the set of lead duration, reference time and valid time windows + * @throws NullPointerException if the declaration is null + */ + + private static Set getReferenceDatesValidDatesAndLeadDurationTimeWindows( EvaluationDeclaration declaration ) + { + Objects.requireNonNull( declaration, CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION ); + + Set leadDurationWindows = TimeWindowSlicer.getLeadDurationTimeWindows( declaration ); + Set referenceDatesWindows = TimeWindowSlicer.getReferenceDatesTimeWindows( declaration ); + Set validDatesWindows = TimeWindowSlicer.getValidDatesTimeWindows( declaration ); + + // Create a new window for each combination of reference dates and lead duration + Set timeWindows = new HashSet<>( leadDurationWindows.size() * referenceDatesWindows.size() ); + for ( TimeWindowOuter nextReferenceWindow : referenceDatesWindows ) + { + for ( TimeWindowOuter nextValidWindow : validDatesWindows ) + { + for ( TimeWindowOuter nextLeadWindow : leadDurationWindows ) + { + TimeWindow window = + nextValidWindow.getTimeWindow() + .toBuilder() + .setEarliestReferenceTime( nextReferenceWindow.getTimeWindow() + .getEarliestReferenceTime() ) + .setLatestReferenceTime( nextReferenceWindow.getTimeWindow() + .getLatestReferenceTime() ) + .setEarliestLeadDuration( nextLeadWindow.getTimeWindow() + .getEarliestLeadDuration() ) + .setLatestLeadDuration( nextLeadWindow.getTimeWindow() + .getLatestLeadDuration() ) + .build(); + timeWindows.add( TimeWindowOuter.of( window ) ); + } + } + } + + return Collections.unmodifiableSet( timeWindows ); + } + + /** + * @param first the first time window + * @param second the second time window + * @return true if the two windows intersect in all defined time dimensions, false otherwise + */ + private static boolean intersects( TimeWindowOuter first, TimeWindowOuter second ) + { + if ( first.equals( second ) ) + { + return true; + } + + boolean leadIntersects = first.bothLeadDurationsAreUnbounded() + || second.bothLeadDurationsAreUnbounded() + // Start of first window is within second window + || ( first.getEarliestLeadDuration() + .compareTo( second.getEarliestLeadDuration() ) >= 0 + && first.getEarliestLeadDuration() + .compareTo( second.getLatestLeadDuration() ) <= 0 ) + || // Start of second window is within first window + ( second.getEarliestLeadDuration() + .compareTo( first.getEarliestLeadDuration() ) >= 0 + && second.getEarliestLeadDuration() + .compareTo( first.getLatestLeadDuration() ) <= 0 ) + || // End of first window is within second window + ( first.getLatestLeadDuration() + .compareTo( second.getEarliestLeadDuration() ) >= 0 + && first.getLatestLeadDuration() + .compareTo( second.getLatestLeadDuration() ) <= 0 ) + || // End of second window is within first window + ( second.getLatestLeadDuration() + .compareTo( first.getEarliestLeadDuration() ) >= 0 + && second.getLatestLeadDuration() + .compareTo( first.getLatestLeadDuration() ) <= 0 ); + + boolean validIntersects = TimeWindowSlicer.intersects( first.getEarliestValidTime(), + first.getLatestValidTime(), + second.getEarliestValidTime(), + second.getLatestValidTime() ); + + boolean referenceIntersects = TimeWindowSlicer.intersects( first.getEarliestReferenceTime(), + first.getLatestReferenceTime(), + second.getEarliestReferenceTime(), + second.getLatestReferenceTime() ); + + return leadIntersects + && validIntersects + && referenceIntersects; + } + + /** + * @param firstLower the lower bound of the first window + * @param firstUpper the upper bound of the first window + * @param secondLower the lower bound of the second window + * @param secondUpper the upper bound of the second window + * @return whether the windows intersect + */ + private static boolean intersects( Instant firstLower, + Instant firstUpper, + Instant secondLower, + Instant secondUpper ) + { + return // Start of first window is within second window + ( firstLower.compareTo( secondLower ) >= 0 + && firstLower.compareTo( secondUpper ) <= 0 ) + || // Start of second window is within first window + ( secondLower.compareTo( firstLower ) >= 0 + && secondLower.compareTo( firstUpper ) <= 0 ) + || // End of first window is within second window + ( firstUpper.compareTo( secondLower ) >= 0 + && firstUpper.compareTo( secondUpper ) <= 0 ) + || // End of second window is within first window + ( secondUpper.compareTo( firstLower ) >= 0 + && secondUpper.compareTo( firstUpper ) <= 0 ); + } + + /** + * Do not construct. + */ + private TimeWindowSlicer() + { + } + +} \ No newline at end of file diff --git a/wres-datamodel/test/wres/datamodel/time/TimeSeriesSlicerTest.java b/wres-datamodel/test/wres/datamodel/time/TimeSeriesSlicerTest.java index 5b14947f95..6631dd0ce6 100644 --- a/wres-datamodel/test/wres/datamodel/time/TimeSeriesSlicerTest.java +++ b/wres-datamodel/test/wres/datamodel/time/TimeSeriesSlicerTest.java @@ -1460,64 +1460,6 @@ void testGetEnsembleTransformer() assertEquals( expected, actual ); } - @Test - void testAdjustByTimeScalePeriodWhenTimeScaleIsInstantaneous() - { - TimeWindow timeWindow = MessageFactory.getTimeWindow( Duration.ofHours( 1 ), - Duration.ofHours( 2 ) ); - TimeScale timeScale = TimeScale.newBuilder() - .setPeriod( com.google.protobuf.Duration.newBuilder() - .setSeconds( 1 ) ) - .build(); - TimeScaleOuter timeScaleOuter = TimeScaleOuter.of( timeScale ); - TimeWindowOuter timeWindowOuter = TimeWindowOuter.of( timeWindow ); - - TimeWindowOuter actual = TimeSeriesSlicer.adjustTimeWindowForTimeScale( timeWindowOuter, timeScaleOuter ); - assertEquals( timeWindowOuter, actual ); - } - - @Test - void testAdjustTimeWindowEarliestLeadDurationForTimeScale() - { - TimeWindow timeWindow = MessageFactory.getTimeWindow( Duration.ofHours( 1 ), - Duration.ofHours( 2 ) ); - TimeScale timeScale = TimeScale.newBuilder() - .setPeriod( com.google.protobuf.Duration.newBuilder() - .setSeconds( 1800 ) ) - .build(); - TimeScaleOuter timeScaleOuter = TimeScaleOuter.of( timeScale ); - TimeWindowOuter timeWindowOuter = TimeWindowOuter.of( timeWindow ); - - TimeWindowOuter actual = TimeSeriesSlicer.adjustTimeWindowForTimeScale( timeWindowOuter, timeScaleOuter ); - TimeWindow expectedInner = MessageFactory.getTimeWindow( Duration.ofMinutes( 30 ), - Duration.ofHours( 2 ) ); - TimeWindowOuter expected = TimeWindowOuter.of( expectedInner ); - - assertEquals( expected, actual ); - } - - @Test - void testAdjustTimeWindowEarliestValidTimeForTimeScale() - { - Instant earliest = Instant.parse( "2055-03-23T00:00:00Z" ); - Instant latest = Instant.parse( "2055-03-24T00:00:00Z" ); - - TimeWindow timeWindow = MessageFactory.getTimeWindow( earliest, latest ); - TimeScale timeScale = TimeScale.newBuilder() - .setPeriod( com.google.protobuf.Duration.newBuilder() - .setSeconds( 86400 ) ) - .build(); - TimeScaleOuter timeScaleOuter = TimeScaleOuter.of( timeScale ); - TimeWindowOuter timeWindowOuter = TimeWindowOuter.of( timeWindow ); - - TimeWindowOuter actual = TimeSeriesSlicer.adjustTimeWindowForTimeScale( timeWindowOuter, timeScaleOuter ); - TimeWindow expectedInner = MessageFactory.getTimeWindow( Instant.parse( "2055-03-22T00:00:00Z" ), - latest ); - TimeWindowOuter expected = TimeWindowOuter.of( expectedInner ); - - assertEquals( expected, actual ); - } - @Test void testGetTimesteps() { diff --git a/wres-datamodel/test/wres/datamodel/time/TimeWindowOuterTest.java b/wres-datamodel/test/wres/datamodel/time/TimeWindowOuterTest.java index aae6713aef..bc017c393b 100644 --- a/wres-datamodel/test/wres/datamodel/time/TimeWindowOuterTest.java +++ b/wres-datamodel/test/wres/datamodel/time/TimeWindowOuterTest.java @@ -8,8 +8,6 @@ import java.time.Duration; import java.time.Instant; -import java.util.HashSet; -import java.util.Set; import org.junit.Test; @@ -25,7 +23,6 @@ */ public final class TimeWindowOuterTest { - private static final Instant SEVENTH_TIME = Instant.parse( "2017-12-31T11:59:59Z" ); private static final Instant SIXTH_TIME = Instant.parse( "2015-12-31T11:59:59Z" ); private static final Instant FIFTH_TIME = Instant.parse( "2010-12-31T11:59:59Z" ); private static final Instant FOURTH_TIME = Instant.parse( "2009-12-31T11:59:59Z" ); @@ -433,99 +430,4 @@ public void testBothLeadDurationsAreUnbounded() assertTrue( unbounded.bothLeadDurationsAreUnbounded() ); } - /** - * Tests the {@link TimeWindowOuter#unionOf(Set)}. - */ - - @Test - public void testUnionOf() - { - TimeWindow firstInner = wres.statistics.MessageFactory.getTimeWindow( SECOND_TIME, - SEVENTH_TIME, - Duration.ofHours( 5 ), - Duration.ofHours( 25 ) ); - TimeWindowOuter first = TimeWindowOuter.of( firstInner ); - TimeWindow secondInner = wres.statistics.MessageFactory.getTimeWindow( FIRST_TIME, - SIXTH_TIME, - Duration.ofHours( -5 ), - Duration.ofHours( 20 ) ); - TimeWindowOuter second = TimeWindowOuter.of( secondInner ); - TimeWindow expectedInner = wres.statistics.MessageFactory.getTimeWindow( FIRST_TIME, - SEVENTH_TIME, - Duration.ofHours( -5 ), - Duration.ofHours( 25 ) ); - TimeWindowOuter expected = TimeWindowOuter.of( expectedInner ); - Set union = new HashSet<>(); - union.add( first ); - union.add( second ); - - TimeWindowOuter actual = TimeWindowOuter.unionOf( union ); - - assertEquals( expected, actual ); - - TimeWindow thirdInner = wres.statistics.MessageFactory.getTimeWindow( SECOND_TIME, - SEVENTH_TIME, - FIRST_TIME, - Instant.parse( "2019-12-31T11:59:59Z" ), - Duration.ofHours( 5 ), - Duration.ofHours( 21 ) ); - TimeWindowOuter third = TimeWindowOuter.of( thirdInner ); - TimeWindow fourthInner = wres.statistics.MessageFactory.getTimeWindow( FIRST_TIME, - SIXTH_TIME, - Instant.parse( "1982-01-01T00:00:00Z" ), - SEVENTH_TIME, - Duration.ZERO, - Duration.ofHours( 20 ) ); - TimeWindowOuter fourth = TimeWindowOuter.of( fourthInner ); - TimeWindow fifthInner = wres.statistics.MessageFactory.getTimeWindow( FIRST_TIME, - SEVENTH_TIME, - Instant.parse( "1982-01-01T00:00:00Z" ), - Instant.parse( "2019-12-31T11:59:59Z" ), - Duration.ZERO, - Duration.ofHours( 21 ) ); - TimeWindowOuter expectedTwo = TimeWindowOuter.of( fifthInner ); - Set unionTwo = new HashSet<>(); - unionTwo.add( third ); - unionTwo.add( fourth ); - - TimeWindowOuter actualTwo = TimeWindowOuter.unionOf( unionTwo ); - - assertEquals( expectedTwo, actualTwo ); - } - - @Test - public void testUnionWithThrowsExceptionOnEmptyInput() - { - Set emptySet = Set.of(); - IllegalArgumentException thrown = - assertThrows( IllegalArgumentException.class, - () -> TimeWindowOuter.unionOf( emptySet ) ); - - assertEquals( "Cannot determine the union of time windows for empty input.", thrown.getMessage() ); - } - - @Test - public void testUnionWithThrowsExceptionOnInputWithNull() - { - - Set nullInput = new HashSet<>(); - nullInput.add( null ); - - IllegalArgumentException thrown = - assertThrows( IllegalArgumentException.class, - () -> TimeWindowOuter.unionOf( nullInput ) ); - - assertEquals( "Cannot determine the union of time windows for input that contains one or more " - + "null time windows.", - thrown.getMessage() ); - } - - @Test - public void testUnionWithThrowsExceptionOnNullInput() - { - NullPointerException thrown = assertThrows( NullPointerException.class, () -> TimeWindowOuter.unionOf( null ) ); - - assertEquals( "Cannot determine the union of time windows for a null input.", thrown.getMessage() ); - } - } diff --git a/wres-datamodel/test/wres/datamodel/time/TimeWindowSlicerTest.java b/wres-datamodel/test/wres/datamodel/time/TimeWindowSlicerTest.java new file mode 100644 index 0000000000..5a8d20f45d --- /dev/null +++ b/wres-datamodel/test/wres/datamodel/time/TimeWindowSlicerTest.java @@ -0,0 +1,1535 @@ +package wres.datamodel.time; + +import java.time.Duration; +import java.time.Instant; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import wres.config.yaml.components.EvaluationDeclaration; +import wres.config.yaml.components.EvaluationDeclarationBuilder; +import wres.config.yaml.components.LeadTimeInterval; +import wres.config.yaml.components.LeadTimeIntervalBuilder; +import wres.config.yaml.components.TimeInterval; +import wres.config.yaml.components.TimeIntervalBuilder; +import wres.config.yaml.components.TimePools; +import wres.config.yaml.components.TimePoolsBuilder; +import wres.datamodel.scale.TimeScaleOuter; +import wres.statistics.MessageFactory; +import wres.statistics.generated.TimeScale; +import wres.statistics.generated.TimeWindow; + +/** + * Tests the {@link TimeWindowSlicer}. + */ +class TimeWindowSlicerTest +{ + private static final Instant FOURTH_TIME = Instant.parse( "2017-12-31T11:59:59Z" ); + private static final Instant THIRD_TIME = Instant.parse( "2015-12-31T11:59:59Z" ); + private static final Instant SECOND_TIME = Instant.parse( "1985-01-01T00:00:00Z" ); + private static final Instant FIRST_TIME = Instant.parse( "1983-01-01T00:00:00Z" ); + private static final String INSTANT_ONE = "2017-08-08T00:00:00Z"; + private static final String INSTANT_TWO = "2017-08-08T23:00:00Z"; + private static final String INSTANT_THREE = "2017-08-09T17:00:00Z"; + private static final String INSTANT_FOUR = "2017-08-08T01:00:00Z"; + private static final String INSTANT_FIVE = "2017-08-08T02:00:00Z"; + private static final String INSTANT_SIX = "2017-08-08T03:00:00Z"; + private static final String INSTANT_SEVEN = "2017-08-08T04:00:00Z"; + private static final String INSTANT_EIGHT = "2017-08-08T05:00:00Z"; + private static final String INSTANT_NINE = "2551-03-17T00:00:00Z"; + private static final String INSTANT_TEN = "2551-03-20T00:00:00Z"; + private static final String INSTANT_ELEVEN = "2551-03-19T00:00:00Z"; + private static final String INSTANT_TWELVE = "2551-03-17T13:00:00Z"; + private static final String INSTANT_THIRTEEN = "2551-03-17T07:00:00Z"; + private static final String INSTANT_FOURTEEN = "2551-03-17T20:00:00Z"; + private static final String INSTANT_FIFTEEN = "2551-03-17T14:00:00Z"; + private static final String INSTANT_SIXTEEN = "2551-03-18T03:00:00Z"; + private static final String INSTANT_SEVENTEEN = "2551-03-17T21:00:00Z"; + private static final String INSTANT_EIGHTEEN = "2551-03-18T10:00:00Z"; + private static final String INSTANT_NINETEEN = "2551-03-18T04:00:00Z"; + private static final String INSTANT_TWENTY = "2551-03-18T17:00:00Z"; + private static final String INSTANT_TWENTY_ONE = "2551-03-18T11:00:00Z"; + private static final String INSTANT_TWENTY_TWO = "2551-03-18T18:00:00Z"; + private static final String INSTANT_TWENTY_THREE = "2551-03-19T07:00:00Z"; + private static final String INSTANT_TWENTY_FOUR = "2551-03-19T01:00:00Z"; + private static final String INSTANT_TWENTY_FIVE = "2551-03-19T14:00:00Z"; + private static final String INSTANT_TWENTY_SIX = "2551-03-19T08:00:00Z"; + private static final String INSTANT_TWENTY_SEVEN = "2551-03-19T21:00:00Z"; + private static final String INSTANT_TWENTY_EIGHT = "2551-03-24T00:00:00Z"; + + /** + * Tests the {@link TimeWindowSlicer#union(Set)}. + */ + + @Test + void testUnion() + { + TimeWindow firstInner = wres.statistics.MessageFactory.getTimeWindow( SECOND_TIME, + FOURTH_TIME, + Duration.ofHours( 5 ), + Duration.ofHours( 25 ) ); + TimeWindowOuter first = TimeWindowOuter.of( firstInner ); + TimeWindow secondInner = wres.statistics.MessageFactory.getTimeWindow( FIRST_TIME, + THIRD_TIME, + Duration.ofHours( -5 ), + Duration.ofHours( 20 ) ); + TimeWindowOuter second = TimeWindowOuter.of( secondInner ); + TimeWindow expectedInner = wres.statistics.MessageFactory.getTimeWindow( FIRST_TIME, + FOURTH_TIME, + Duration.ofHours( -5 ), + Duration.ofHours( 25 ) ); + TimeWindowOuter expected = TimeWindowOuter.of( expectedInner ); + Set union = new HashSet<>(); + union.add( first ); + union.add( second ); + + TimeWindowOuter actual = TimeWindowSlicer.union( union ); + + assertEquals( expected, actual ); + + TimeWindow thirdInner = wres.statistics.MessageFactory.getTimeWindow( SECOND_TIME, + FOURTH_TIME, + FIRST_TIME, + Instant.parse( "2019-12-31T11:59:59Z" ), + Duration.ofHours( 5 ), + Duration.ofHours( 21 ) ); + TimeWindowOuter third = TimeWindowOuter.of( thirdInner ); + TimeWindow fourthInner = wres.statistics.MessageFactory.getTimeWindow( FIRST_TIME, + THIRD_TIME, + Instant.parse( "1982-01-01T00:00:00Z" ), + FOURTH_TIME, + Duration.ZERO, + Duration.ofHours( 20 ) ); + TimeWindowOuter fourth = TimeWindowOuter.of( fourthInner ); + TimeWindow fifthInner = wres.statistics.MessageFactory.getTimeWindow( FIRST_TIME, + FOURTH_TIME, + Instant.parse( "1982-01-01T00:00:00Z" ), + Instant.parse( "2019-12-31T11:59:59Z" ), + Duration.ZERO, + Duration.ofHours( 21 ) ); + TimeWindowOuter expectedTwo = TimeWindowOuter.of( fifthInner ); + Set unionTwo = new HashSet<>(); + unionTwo.add( third ); + unionTwo.add( fourth ); + + TimeWindowOuter actualTwo = TimeWindowSlicer.union( unionTwo ); + + assertEquals( expectedTwo, actualTwo ); + } + + @Test + void testUnionWithThrowsExceptionOnEmptyInput() + { + Set emptySet = Set.of(); + IllegalArgumentException thrown = + assertThrows( IllegalArgumentException.class, + () -> TimeWindowSlicer.union( emptySet ) ); + + assertEquals( "Cannot determine the union of time windows for empty input.", thrown.getMessage() ); + } + + @Test + void testUnionWithThrowsExceptionOnInputWithNull() + { + + Set nullInput = new HashSet<>(); + nullInput.add( null ); + + IllegalArgumentException thrown = + assertThrows( IllegalArgumentException.class, + () -> TimeWindowSlicer.union( nullInput ) ); + + assertEquals( "Cannot determine the union of time windows for input that contains one or more " + + "null time windows.", + thrown.getMessage() ); + } + + @Test + void testUnionWithThrowsExceptionOnNullInput() + { + NullPointerException thrown = assertThrows( NullPointerException.class, () -> TimeWindowSlicer.union( null ) ); + + assertEquals( "Cannot determine the union of time windows for a null input.", thrown.getMessage() ); + } + + @Test + void testIntersectionOnLeadDurationsOnly() + { + TimeWindow one = MessageFactory.getTimeWindow( Duration.ofHours( 1 ), + Duration.ofHours( 3 ) ); + TimeWindow two = MessageFactory.getTimeWindow( Duration.ofHours( 3 ), + Duration.ofHours( 5 ) ); + TimeWindow three = MessageFactory.getTimeWindow( Duration.ofHours( 1 ), + Duration.ofHours( 2 ) ); + TimeWindow four = MessageFactory.getTimeWindow( Duration.ofHours( 6 ), + Duration.ofHours( 7 ) ); + Set first = Set.of( TimeWindowOuter.of( one ), + TimeWindowOuter.of( two ) ); + Set second = Set.of( TimeWindowOuter.of( three ), + TimeWindowOuter.of( four ) ); + + Set actual = TimeWindowSlicer.intersection( first, second ); + Set expected = Set.of( TimeWindowOuter.of( one ), + TimeWindowOuter.of( three ) ); + + assertEquals( expected, actual ); + } + + @Test + void testAdjustByTimeScalePeriodWhenTimeScaleIsInstantaneous() + { + TimeWindow timeWindow = MessageFactory.getTimeWindow( Duration.ofHours( 1 ), + Duration.ofHours( 2 ) ); + TimeScale timeScale = TimeScale.newBuilder() + .setPeriod( com.google.protobuf.Duration.newBuilder() + .setSeconds( 1 ) ) + .build(); + TimeScaleOuter timeScaleOuter = TimeScaleOuter.of( timeScale ); + TimeWindowOuter timeWindowOuter = TimeWindowOuter.of( timeWindow ); + + TimeWindowOuter actual = TimeWindowSlicer.adjustTimeWindowForTimeScale( timeWindowOuter, timeScaleOuter ); + + assertEquals( timeWindowOuter, actual ); + } + + @Test + void testAdjustTimeWindowEarliestLeadDurationForTimeScale() + { + TimeWindow timeWindow = MessageFactory.getTimeWindow( Duration.ofHours( 1 ), + Duration.ofHours( 2 ) ); + TimeScale timeScale = TimeScale.newBuilder() + .setPeriod( com.google.protobuf.Duration.newBuilder() + .setSeconds( 1800 ) ) + .build(); + TimeScaleOuter timeScaleOuter = TimeScaleOuter.of( timeScale ); + TimeWindowOuter timeWindowOuter = TimeWindowOuter.of( timeWindow ); + + TimeWindowOuter actual = TimeWindowSlicer.adjustTimeWindowForTimeScale( timeWindowOuter, timeScaleOuter ); + TimeWindow expectedInner = MessageFactory.getTimeWindow( Duration.ofMinutes( 30 ), + Duration.ofHours( 2 ) ); + TimeWindowOuter expected = TimeWindowOuter.of( expectedInner ); + + assertEquals( expected, actual ); + } + + @Test + void testAdjustTimeWindowEarliestValidTimeForTimeScale() + { + Instant earliest = Instant.parse( "2055-03-23T00:00:00Z" ); + Instant latest = Instant.parse( "2055-03-24T00:00:00Z" ); + + TimeWindow timeWindow = MessageFactory.getTimeWindow( earliest, latest ); + TimeScale timeScale = TimeScale.newBuilder() + .setPeriod( com.google.protobuf.Duration.newBuilder() + .setSeconds( 86400 ) ) + .build(); + TimeScaleOuter timeScaleOuter = TimeScaleOuter.of( timeScale ); + TimeWindowOuter timeWindowOuter = TimeWindowOuter.of( timeWindow ); + + TimeWindowOuter actual = TimeWindowSlicer.adjustTimeWindowForTimeScale( timeWindowOuter, timeScaleOuter ); + TimeWindow expectedInner = MessageFactory.getTimeWindow( Instant.parse( "2055-03-22T00:00:00Z" ), + latest ); + TimeWindowOuter expected = TimeWindowOuter.of( expectedInner ); + + assertEquals( expected, actual ); + } + + /** + *

Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} where the project declaration + * includes a lead_times and a lead_time_pools. Expects twenty-four time windows + * with prescribed characteristics. + * + *

The project declaration from this test scenario matches (in all important ways) the declaration associated + * with system test scenario017, as of commit fa548da9da85b16631f238f78b358d85ddbebed5. + */ + + @Test + void testGetTimeWindowsWithLeadTimesAndLeadTimePoolsReturnsTwentyFourWindows() + { + LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() + .minimum( Duration.ofHours( 0 ) ) + .maximum( Duration.ofHours( 24 ) ) + .build(); + TimePools leadTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 1 ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .leadTimes( leadTimes ) + .leadTimePools( leadTimePools ) + .build(); + + // Generate the expected windows + Set expectedTimeWindows = new HashSet<>( 24 ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 0 ), + Duration.ofHours( 1 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 1 ), + Duration.ofHours( 2 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 2 ), + Duration.ofHours( 3 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 3 ), + Duration.ofHours( 4 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 4 ), + Duration.ofHours( 5 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 5 ), + Duration.ofHours( 6 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 6 ), + Duration.ofHours( 7 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 7 ), + Duration.ofHours( 8 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 8 ), + Duration.ofHours( 9 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 9 ), + Duration.ofHours( 10 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 10 ), + Duration.ofHours( 11 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 11 ), + Duration.ofHours( 12 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 12 ), + Duration.ofHours( 13 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 13 ), + Duration.ofHours( 14 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 14 ), + Duration.ofHours( 15 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 15 ), + Duration.ofHours( 16 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 16 ), + Duration.ofHours( 17 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 17 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 18 ), + Duration.ofHours( 19 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 19 ), + Duration.ofHours( 20 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 20 ), + Duration.ofHours( 21 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 21 ), + Duration.ofHours( 22 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 22 ), + Duration.ofHours( 23 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 23 ), + Duration.ofHours( 24 ) ) ); + + // Generate the actual windows + Set actualTimeWindows = TimeWindowSlicer.getTimeWindows( declaration ); + + // Assert the expected cardinality + assertEquals( 24, actualTimeWindows.size() ); + + // Assert that the expected and actual are equal + assertEquals( expectedTimeWindows.stream() + .map( TimeWindowOuter::of ) + .collect( Collectors.toSet() ), + actualTimeWindows ); + } + + /** + *

Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} where the project declaration + * includes a lead_times and a lead_time_pools. Expects two time windows with + * prescribed characteristics. + * + *

The project declaration from this test scenario matches (in all important ways) the declaration associated + * with system test scenario403, as of commit fa548da9da85b16631f238f78b358d85ddbebed5. + */ + + @Test + void testGetTimeWindowsWithLeadTimesAndLeadTimePoolsReturnsTwoWindows() + { + LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() + .minimum( Duration.ofHours( 0 ) ) + .maximum( Duration.ofHours( 48 ) ) + .build(); + TimePools leadTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 24 ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .leadTimes( leadTimes ) + .leadTimePools( leadTimePools ) + .build(); + + // Generate the expected windows + Set expectedTimeWindows = new HashSet<>( 2 ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 0 ), + Duration.ofHours( 24 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Duration.ofHours( 24 ), + Duration.ofHours( 48 ) ) ); + + // Generate the actual windows + Set actualTimeWindows = TimeWindowSlicer.getTimeWindows( declaration ); + + // Assert the expected cardinality + assertEquals( 2, actualTimeWindows.size() ); + + // Assert that the expected and actual are equal + assertEquals( expectedTimeWindows.stream() + .map( TimeWindowOuter::of ) + .collect( Collectors.toSet() ), + actualTimeWindows ); + } + + /** + *

Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} where the project declaration + * includes a lead_times, a lead_time_pools, an reference_dates, and an + * referenceDatesPoolingWindow. Expects eighteen time windows with prescribed characteristics. + * + *

The project declaration from this test scenario matches (in all important ways) the declaration associated + * with system test scenario505, which is in development as of commit 766c6d0b4ad96f191bcafb8f2a357c0f2e6a2d3c. + */ + + @Test + void testGetTimeWindowsWithLeadTimesReferenceDatesLeadTimePoolsAndReferenceDatesPoolingWindowReturnsEighteenWindows() + { + LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() + .minimum( Duration.ofHours( 0 ) ) + .maximum( Duration.ofHours( 40 ) ) + .build(); + TimePools leadTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 23 ) ) + .frequency( Duration.ofHours( 17 ) ) + .build(); + TimeInterval referenceDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_NINE ) ) + .maximum( Instant.parse( INSTANT_TEN ) ) + .build(); + TimePools referenceTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 13 ) ) + .frequency( Duration.ofHours( 7 ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .leadTimes( leadTimes ) + .leadTimePools( leadTimePools ) + .referenceDates( referenceDates ) + .referenceDatePools( referenceTimePools ) + .build(); + + // Generate the expected windows + Set expectedTimeWindows = new HashSet<>( 18 ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINE ), + //2551-03-17T00:00:00Z + Instant.parse( INSTANT_TWELVE ), + //2551-03-17T13:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 23 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINE ), + //2551-03-17T00:00:00Z + Instant.parse( INSTANT_TWELVE ), + //2551-03-17T13:00:00Z + Duration.ofHours( 17 ), + Duration.ofHours( 40 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_THIRTEEN ), + //2551-03-17T07:00:00Z + Instant.parse( INSTANT_FOURTEEN ), + //2551-03-17T20:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 23 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_THIRTEEN ), + //2551-03-17T07:00:00Z + Instant.parse( INSTANT_FOURTEEN ), + //2551-03-17T20:00:00Z + Duration.ofHours( 17 ), + Duration.ofHours( 40 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_FIFTEEN ), + //2551-03-17T14:00:00Z + Instant.parse( INSTANT_SIXTEEN ), + //2551-03-18T03:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 23 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_FIFTEEN ), + //2551-03-17T14:00:00Z + Instant.parse( INSTANT_SIXTEEN ), + //2551-03-18T03:00:00Z + Duration.ofHours( 17 ), + Duration.ofHours( 40 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_SEVENTEEN ), + //2551-03-17T21:00:00Z + Instant.parse( INSTANT_EIGHTEEN ), + //2551-03-18T10:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 23 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_SEVENTEEN ), + //2551-03-17T21:00:00Z + Instant.parse( INSTANT_EIGHTEEN ), + //2551-03-18T10:00:00Z + Duration.ofHours( 17 ), + Duration.ofHours( 40 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINETEEN ), + //2551-03-18T04:00:00Z + Instant.parse( INSTANT_TWENTY ), + //2551-03-18T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 23 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINETEEN ), + //2551-03-18T04:00:00Z + Instant.parse( INSTANT_TWENTY ), + //2551-03-18T17:00:00Z + Duration.ofHours( 17 ), + Duration.ofHours( 40 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_ONE ), + //2551-03-18T11:00:00Z + Instant.parse( INSTANT_ELEVEN ), + //2551-03-19T00:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 23 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_ONE ), + //2551-03-18T11:00:00Z + Instant.parse( INSTANT_ELEVEN ), + //2551-03-19T00:00:00Z + Duration.ofHours( 17 ), + Duration.ofHours( 40 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_TWO ), + //2551-03-18T18:00:00Z + Instant.parse( INSTANT_TWENTY_THREE ), + //2551-03-19T07:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 23 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_TWO ), + //2551-03-18T18:00:00Z + Instant.parse( INSTANT_TWENTY_THREE ), + //2551-03-19T07:00:00Z + Duration.ofHours( 17 ), + Duration.ofHours( 40 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_FOUR ), + //2551-03-19T01:00:00Z + Instant.parse( INSTANT_TWENTY_FIVE ), + //2551-03-19T14:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 23 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_FOUR ), + //2551-03-19T01:00:00Z + Instant.parse( INSTANT_TWENTY_FIVE ), + //2551-03-19T14:00:00Z + Duration.ofHours( 17 ), + Duration.ofHours( 40 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_SIX ), + //2551-03-19T08:00:00Z + Instant.parse( INSTANT_TWENTY_SEVEN ), + //2551-03-19T21:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 23 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_SIX ), + //2551-03-19T08:00:00Z + Instant.parse( INSTANT_TWENTY_SEVEN ), + //2551-03-19T21:00:00Z + Duration.ofHours( 17 ), + Duration.ofHours( 40 ) ) ); + + // Generate the actual windows + Set actualTimeWindows = TimeWindowSlicer.getTimeWindows( declaration ); + + // Assert the expected cardinality + assertEquals( 18, actualTimeWindows.size() ); + + // Assert that the expected and actual are equal + assertEquals( expectedTimeWindows.stream() + .map( TimeWindowOuter::of ) + .collect( Collectors.toSet() ), + actualTimeWindows ); + } + + /** + *

Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} where the project declaration + * includes a lead_times, a lead_time_pools, a dates and an + * reference_dates. Expects one time window with prescribed characteristics. + */ + + @Test + void testGetTimeWindowsWithLeadTimesValidDatesReferenceDatesAndLeadTimePoolsReturnsOneWindow() + { + LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() + .minimum( Duration.ofHours( 1 ) ) + .maximum( Duration.ofHours( 48 ) ) + .build(); + TimePools leadTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 24 ) ) + .build(); + TimeInterval referenceDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_NINE ) ) + .maximum( Instant.parse( INSTANT_TEN ) ) + .build(); + TimeInterval validDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_ELEVEN ) ) + .maximum( Instant.parse( INSTANT_TWENTY_EIGHT ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .leadTimes( leadTimes ) + .validDates( validDates ) + .leadTimePools( leadTimePools ) + .referenceDates( referenceDates ) + .build(); + + // Generate the expected windows + Set expectedTimeWindows = new HashSet<>( 1 ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINE ), + //2551-03-17T00:00:00Z + Instant.parse( INSTANT_TEN ), + //2551-03-20T00:00:00Z + Instant.parse( INSTANT_ELEVEN ), + //2551-03-19T00:00:00Z + Instant.parse( INSTANT_TWENTY_EIGHT ), + //2551-03-24T00:00:00Z + Duration.ofHours( 1 ), + Duration.ofHours( 25 ) ) ); + + // Generate the actual windows + Set actualTimeWindows = TimeWindowSlicer.getTimeWindows( declaration ); + + // Assert the expected cardinality + assertEquals( 1, actualTimeWindows.size() ); + + // Assert that the expected and actual are equal + assertEquals( expectedTimeWindows.stream() + .map( TimeWindowOuter::of ) + .collect( Collectors.toSet() ), + actualTimeWindows ); + } + + /** + *

Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} where the project declaration + * does not include any constraints on time, aka "one big pool". + * + *

This is analogous to system test scenario508, as of commit b9a7214ec22999482784119a8527149348c80119. + */ + + @Test + void testGetTimeWindowsFromUnconstrainedDeclarationReturnsOneWindow() + { + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .build(); + + // Generate the expected windows + Set expectedTimeWindows = new HashSet<>( 1 ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.MIN, + Instant.MAX, + Instant.MIN, + Instant.MAX, + MessageFactory.DURATION_MIN, + MessageFactory.DURATION_MAX ) ); + + // Generate the actual windows + Set actualTimeWindows = TimeWindowSlicer.getTimeWindows( declaration ); + + // Assert the expected cardinality + assertEquals( 1, actualTimeWindows.size() ); + + // Assert that the expected and actual are equal + assertEquals( expectedTimeWindows.stream() + .map( TimeWindowOuter::of ) + .collect( Collectors.toSet() ), + actualTimeWindows ); + } + + /** + *

Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} where the project declaration + * includes a referenceDatesPoolingWindow and a lead_time_pools. Expects twenty-three + * time windows. Tests both an explicit and implicit declaration of the frequency. + * + *

The project declaration from this test matches the declaration associated + * with system test scenario704, as of commit da07c16148429740496b8cc6df89a73e3697f17c, + * except the period is 1.0 time units here. + */ + + @Test + void testGetTimeWindowsWithLeadTimesValidDatesReferenceDatesReferenceDatesPoolingWindowAndLeadTimePoolsReturnsTwentyThreeWindows() + { + LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() + .minimum( Duration.ofHours( 0 ) ) + .maximum( Duration.ofHours( 18 ) ) + .build(); + TimePools leadTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 18 ) ) + .build(); + TimeInterval referenceDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_ONE ) ) + .maximum( Instant.parse( INSTANT_TWO ) ) + .build(); + TimePools referenceTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 1 ) ) + .frequency( Duration.ofHours( 1 ) ) + .build(); + TimeInterval validDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_ONE ) ) + .maximum( Instant.parse( INSTANT_THREE ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .leadTimes( leadTimes ) + .leadTimePools( leadTimePools ) + .referenceDates( referenceDates ) + .referenceDatePools( referenceTimePools ) + .validDates( validDates ) + .build(); + + // Generate the expected time windows + Set expectedTimeWindows = new HashSet<>( 23 ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_FOUR ), + //2017-08-08T01:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_FOUR ), + //2017-08-08T01:00:00Z + Instant.parse( INSTANT_FIVE ), + //2017-08-08T02:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_FIVE ), + //2017-08-08T02:00:00Z + Instant.parse( INSTANT_SIX ), + //2017-08-08T03:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_SIX ), + //2017-08-08T03:00:00Z + Instant.parse( INSTANT_SEVEN ), + //2017-08-08T04:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_SEVEN ), + //2017-08-08T04:00:00Z + Instant.parse( INSTANT_EIGHT ), + //2017-08-08T05:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_EIGHT ), + //2017-08-08T05:00:00Z + Instant.parse( "2017-08-08T06:00:00Z" ), + //2017-08-08T06:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T06:00:00Z" ), + //2017-08-08T06:00:00Z + Instant.parse( "2017-08-08T07:00:00Z" ), + //2017-08-08T07:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T07:00:00Z" ), + //2017-08-08T07:00:00Z + Instant.parse( "2017-08-08T08:00:00Z" ), + //2017-08-08T08:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T08:00:00Z" ), + //2017-08-08T08:00:00Z + Instant.parse( "2017-08-08T09:00:00Z" ), + //2017-08-08T09:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T09:00:00Z" ), + //2017-08-08T09:00:00Z + Instant.parse( "2017-08-08T10:00:00Z" ), + //2017-08-08T10:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T10:00:00Z" ), + //2017-08-08T10:00:00Z + Instant.parse( "2017-08-08T11:00:00Z" ), + //2017-08-08T11:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T11:00:00Z" ), + //2017-08-08T11:00:00Z + Instant.parse( "2017-08-08T12:00:00Z" ), + //2017-08-08T12:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T12:00:00Z" ), + //2017-08-08T12:00:00Z + Instant.parse( "2017-08-08T13:00:00Z" ), + //2017-08-08T13:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T13:00:00Z" ), + //2017-08-08T13:00:00Z + Instant.parse( "2017-08-08T14:00:00Z" ), + //2017-08-08T14:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T14:00:00Z" ), + //2017-08-08T14:00:00Z + Instant.parse( "2017-08-08T15:00:00Z" ), + //2017-08-08T15:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T15:00:00Z" ), + //2017-08-08T15:00:00Z + Instant.parse( "2017-08-08T16:00:00Z" ), + //2017-08-08T16:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T16:00:00Z" ), + //2017-08-08T16:00:00Z + Instant.parse( "2017-08-08T17:00:00Z" ), + //2017-08-08T17:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T17:00:00Z" ), + //2017-08-08T17:00:00Z + Instant.parse( "2017-08-08T18:00:00Z" ), + //2017-08-08T18:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T18:00:00Z" ), + //2017-08-08T18:00:00Z + Instant.parse( "2017-08-08T19:00:00Z" ), + //2017-08-08T19:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T19:00:00Z" ), + //2017-08-08T19:00:00Z + Instant.parse( "2017-08-08T20:00:00Z" ), + //2017-08-08T20:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T20:00:00Z" ), + //2017-08-08T20:00:00Z + Instant.parse( "2017-08-08T21:00:00Z" ), + //2017-08-08T21:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T21:00:00Z" ), + //2017-08-08T21:00:00Z + Instant.parse( "2017-08-08T22:00:00Z" ), + //2017-08-08T22:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T22:00:00Z" ), + //2017-08-08T22:00:00Z + Instant.parse( INSTANT_TWO ), + //2017-08-08T23:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + + // Generate the actual time windows for the explicit test + Set actualTimeWindows = TimeWindowSlicer.getTimeWindows( declaration ); + + // Assert the expected cardinality + assertEquals( 23, actualTimeWindows.size() ); + + // Assert that the expected and actual time windows are equal + assertEquals( expectedTimeWindows.stream() + .map( TimeWindowOuter::of ) + .collect( Collectors.toSet() ), + actualTimeWindows ); + + // Declare the same version of this test with implicit frequency + TimePools referenceTimePoolsNoFreq = TimePoolsBuilder.builder() + .period( Duration.ofHours( 1 ) ) + .build(); + EvaluationDeclaration declarationNoFreq = EvaluationDeclarationBuilder.builder() + .leadTimes( leadTimes ) + .leadTimePools( leadTimePools ) + .referenceDates( referenceDates ) + .referenceDatePools( + referenceTimePoolsNoFreq ) + .validDates( validDates ) + .build(); + + // Generate the actual time windows for the implicit test + Set actualTimeWindowsNoFreq = TimeWindowSlicer.getTimeWindows( declarationNoFreq ); + + // Assert that the expected and actual time windows are equal + assertEquals( expectedTimeWindows.stream() + .map( TimeWindowOuter::of ) + .collect( Collectors.toSet() ), + actualTimeWindowsNoFreq ); + } + + /** + *

Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} where the project declaration + * does not include any explicit time windows, but is constrained by lead_times, + * reference_dates and dates, aka "one big pool" with constraints. + */ + + @Test + void testGetTimeWindowsWithLeadTimesValidDatesAndReferenceDatesReturnsOneWindow() + { + LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() + .minimum( Duration.ofHours( 0 ) ) + .maximum( Duration.ofHours( 18 ) ) + .build(); + TimeInterval referenceDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_ONE ) ) + .maximum( Instant.parse( INSTANT_TWO ) ) + .build(); + TimeInterval validDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_ONE ) ) + .maximum( Instant.parse( INSTANT_THREE ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .leadTimes( leadTimes ) + .referenceDates( referenceDates ) + .validDates( validDates ) + .build(); + + // Generate the expected windows + Set expectedTimeWindows = new HashSet<>( 1 ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_TWO ), + //2017-08-08T23:00:00Z + Instant.parse( INSTANT_ONE ), + //2017-08-08T00:00:00Z + Instant.parse( INSTANT_THREE ), + //2017-08-09T17:00:00Z + Duration.ofHours( 0 ), + Duration.ofHours( 18 ) ) ); + + // Generate the actual windows + Set actualTimeWindows = TimeWindowSlicer.getTimeWindows( declaration ); + + // Assert the expected cardinality + assertEquals( 1, actualTimeWindows.size() ); + + // Assert that the expected and actual are equal + assertEquals( expectedTimeWindows.stream() + .map( TimeWindowOuter::of ) + .collect( Collectors.toSet() ), + actualTimeWindows ); + } + + /** + *

Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} where the project declaration + * includes a lead_times, a an reference_dates, and an referenceDatesPoolingWindow. + * Expects nine time windows with prescribed characteristics. + * + *

The project declaration from this test scenario is similar to the declaration associated + * with system test scenario505, as of commit c8def0cf2d608c0617786f7cb4f28b563960d667, but without + * a lead_time_pools. + */ + + @Test + void testGetTimeWindowsWithLeadTimesReferenceDatesAndReferenceDatesPoolingWindowAndNoLeadTimePoolsReturnsNineWindows() + { + LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() + .minimum( Duration.ofHours( 0 ) ) + .maximum( Duration.ofHours( 40 ) ) + .build(); + TimeInterval referenceDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_NINE ) ) + .maximum( Instant.parse( INSTANT_TEN ) ) + .build(); + TimePools referenceTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 13 ) ) + .frequency( Duration.ofHours( 7 ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .leadTimes( leadTimes ) + .referenceDates( referenceDates ) + .referenceDatePools( referenceTimePools ) + .build(); + + // Generate the expected windows + Set expectedTimeWindows = new HashSet<>( 9 ); + + Duration first = Duration.ofHours( 0 ); + Duration last = Duration.ofHours( 40 ); + + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINE ), + //2551-03-17T00:00:00Z + Instant.parse( INSTANT_TWELVE ), + //2551-03-17T13:00:00Z + first, + last ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_THIRTEEN ), + //2551-03-17T07:00:00Z + Instant.parse( INSTANT_FOURTEEN ), + //2551-03-17T20:00:00Z + first, + last ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_FIFTEEN ), + //2551-03-17T14:00:00Z + Instant.parse( INSTANT_SIXTEEN ), + //2551-03-18T03:00:00Z + first, + last ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_SEVENTEEN ), + //2551-03-17T21:00:00Z + Instant.parse( INSTANT_EIGHTEEN ), + //2551-03-18T10:00:00Z + first, + last ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_NINETEEN ), + //2551-03-18T04:00:00Z + Instant.parse( INSTANT_TWENTY ), + //2551-03-18T17:00:00Z + first, + last ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_ONE ), + //2551-03-18T11:00:00Z + Instant.parse( INSTANT_ELEVEN ), + //2551-03-19T00:00:00Z + first, + last ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_TWO ), + //2551-03-18T18:00:00Z + Instant.parse( INSTANT_TWENTY_THREE ), + //2551-03-19T07:00:00Z + first, + last ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_FOUR ), + //2551-03-19T01:00:00Z + Instant.parse( INSTANT_TWENTY_FIVE ), + //2551-03-19T14:00:00Z + first, + last ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( INSTANT_TWENTY_SIX ), + //2551-03-19T08:00:00Z + Instant.parse( INSTANT_TWENTY_SEVEN ), + //2551-03-19T21:00:00Z + first, + last ) ); + + // Generate the actual windows + Set actualTimeWindows = TimeWindowSlicer.getTimeWindows( declaration ); + + // Assert the expected cardinality + assertEquals( 9, actualTimeWindows.size() ); + + // Assert that the expected and actual are equal + assertEquals( expectedTimeWindows.stream() + .map( TimeWindowOuter::of ) + .collect( Collectors.toSet() ), + actualTimeWindows ); + } + + /** + *

Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} where the project declaration + * includes a lead_times and an lead_time_pools and the minimum and + * maximum lead hours are the same value and the period associated with the + * lead_time_pools is zero wide. This is equivalent to system test scenario010 as of commit + * 8480aa4d4ddc09275746fe590623ecfd83e452ae and is used to check that a zero-wide pool centered on a single lead + * duration does not increment infinitely. + */ + + @Test + void testGetTimeWindowsWithZeroWideLeadTimesAndLeadTimePoolsWithZeroPeriodReturnsOneWindow() + { + LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() + .minimum( Duration.ofHours( 43 ) ) + .maximum( Duration.ofHours( 43 ) ) + .build(); + TimePools leadTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 0 ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .leadTimes( leadTimes ) + .leadTimePools( leadTimePools ) + .build(); + + // Generate the expected windows + Set expectedTimeWindows = new HashSet<>( 1 ); + + Duration first = Duration.ofHours( 43 ); + Duration last = Duration.ofHours( 43 ); + + TimeWindow inner = MessageFactory.getTimeWindow( first, + last ); + expectedTimeWindows.add( inner ); + + // Generate the actual windows + Set actualTimeWindows = TimeWindowSlicer.getTimeWindows( declaration ); + + // Assert the expected cardinality + assertEquals( 1, actualTimeWindows.size() ); + + // Assert that the expected and actual are equal + assertEquals( expectedTimeWindows.stream() + .map( TimeWindowOuter::of ) + .collect( Collectors.toSet() ), + actualTimeWindows ); + } + + @Test + void testGetTimeWindowsWithValidDatesAndValidDatePoolsReturnsTwoWindows() + { + TimeInterval validDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_ONE ) ) + .maximum( Instant.parse( INSTANT_TWO ) ) + .build(); + TimePools validTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 13 ) ) + .frequency( Duration.ofHours( 7 ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .validDates( validDates ) + .validDatePools( validTimePools ) + .build(); + + // Generate the actual windows + Set actualTimeWindows = TimeWindowSlicer.getTimeWindows( declaration ); + + // Assert the expected cardinality + assertEquals( 2, actualTimeWindows.size() ); + + // Generate the expected windows + Set expectedTimeWindows = new HashSet<>( 2 ); + + TimeWindow innerOne = MessageFactory.getTimeWindow( Instant.parse( INSTANT_ONE ), + Instant.parse( "2017-08-08T13:00:00Z" ) ); + TimeWindow innerTwo = MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T07:00:00Z" ), + Instant.parse( "2017-08-08T20:00:00Z" ) ); + + expectedTimeWindows.add( TimeWindowOuter.of( innerOne ) ); + expectedTimeWindows.add( TimeWindowOuter.of( innerTwo ) ); + + // Assert that the expected and actual are equal + assertEquals( expectedTimeWindows, actualTimeWindows ); + } + + @Test + void testGetTimeWindowsWithLeadTimesAndLeadTimePoolsAndValidDatesAndValidDatePoolsReturnsFourWindows() + { + LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() + .minimum( Duration.ofHours( 19 ) ) + .maximum( Duration.ofHours( 34 ) ) + .build(); + TimePools leadTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 8 ) ) + .frequency( Duration.ofHours( 7 ) ) + .build(); + TimeInterval validDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_ONE ) ) + .maximum( Instant.parse( INSTANT_TWO ) ) + .build(); + TimePools validTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 13 ) ) + .frequency( Duration.ofHours( 7 ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .validDates( validDates ) + .validDatePools( validTimePools ) + .leadTimes( leadTimes ) + .leadTimePools( leadTimePools ) + .build(); + + // Generate the actual windows + Set actualTimeWindows = TimeWindowSlicer.getTimeWindows( declaration ); + + // Assert the expected cardinality + assertEquals( 4, actualTimeWindows.size() ); + + // Generate the expected windows + Set expectedTimeWindows = new HashSet<>( 4 ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.MIN, + Instant.MAX, + Instant.parse( INSTANT_ONE ), + Instant.parse( "2017-08-08T13:00:00Z" ), + Duration.ofHours( 19 ), + Duration.ofHours( 27 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.MIN, + Instant.MAX, + Instant.parse( "2017-08-08T07:00:00Z" ), + Instant.parse( "2017-08-08T20:00:00Z" ), + Duration.ofHours( 19 ), + Duration.ofHours( 27 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.MIN, + Instant.MAX, + Instant.parse( INSTANT_ONE ), + Instant.parse( "2017-08-08T13:00:00Z" ), + Duration.ofHours( 26 ), + Duration.ofHours( 34 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.MIN, + Instant.MAX, + Instant.parse( "2017-08-08T07:00:00Z" ), + Instant.parse( "2017-08-08T20:00:00Z" ), + Duration.ofHours( 26 ), + Duration.ofHours( 34 ) ) ); + + // Assert that the expected and actual are equal + assertEquals( expectedTimeWindows.stream() + .map( TimeWindowOuter::of ) + .collect( Collectors.toSet() ), + actualTimeWindows ); + } + + @Test + void testGetTimeWindowsWithLeadTimesAndLeadTimePoolsAndValidDatesAndValidDatePoolsAndReferenceDatesAndReferenceDatePoolsReturnsFourWindows() + { + LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() + .minimum( Duration.ofHours( 19 ) ) + .maximum( Duration.ofHours( 34 ) ) + .build(); + TimePools leadTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 8 ) ) + .frequency( Duration.ofHours( 7 ) ) + .build(); + TimeInterval referenceDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_TWO ) ) + .maximum( Instant.parse( INSTANT_THREE ) ) + .build(); + TimePools referenceTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 17 ) ) + .frequency( Duration.ofHours( 23 ) ) + .build(); + TimeInterval validDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_ONE ) ) + .maximum( Instant.parse( INSTANT_TWO ) ) + .build(); + TimePools validTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 13 ) ) + .frequency( Duration.ofHours( 7 ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .validDates( validDates ) + .validDatePools( validTimePools ) + .referenceDates( referenceDates ) + .referenceDatePools( referenceTimePools ) + .leadTimes( leadTimes ) + .leadTimePools( leadTimePools ) + .build(); + + // Generate the actual windows + Set actualTimeWindows = TimeWindowSlicer.getTimeWindows( declaration ); + + // Assert the expected cardinality + assertEquals( 4, actualTimeWindows.size() ); + + // Generate the expected windows + Set expectedTimeWindows = new HashSet<>( 4 ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T23:00:00Z" ), + Instant.parse( "2017-08-09T16:00:00Z" ), + Instant.parse( INSTANT_ONE ), + Instant.parse( "2017-08-08T13:00:00Z" ), + Duration.ofHours( 19 ), + Duration.ofHours( 27 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T23:00:00Z" ), + Instant.parse( "2017-08-09T16:00:00Z" ), + Instant.parse( "2017-08-08T07:00:00Z" ), + Instant.parse( "2017-08-08T20:00:00Z" ), + Duration.ofHours( 19 ), + Duration.ofHours( 27 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T23:00:00Z" ), + Instant.parse( "2017-08-09T16:00:00Z" ), + Instant.parse( INSTANT_ONE ), + Instant.parse( "2017-08-08T13:00:00Z" ), + Duration.ofHours( 26 ), + Duration.ofHours( 34 ) ) ); + expectedTimeWindows.add( MessageFactory.getTimeWindow( Instant.parse( "2017-08-08T23:00:00Z" ), + Instant.parse( "2017-08-09T16:00:00Z" ), + Instant.parse( "2017-08-08T07:00:00Z" ), + Instant.parse( "2017-08-08T20:00:00Z" ), + Duration.ofHours( 26 ), + Duration.ofHours( 34 ) ) ); + + // Assert that the expected and actual are equal + assertEquals( expectedTimeWindows.stream() + .map( TimeWindowOuter::of ) + .collect( Collectors.toSet() ), + actualTimeWindows ); + } + + /** + * Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} for an expected exception when + * lead_times are required but missing. + */ + + @Test + void testGetTimeWindowsThrowsNullPointerExceptionWhenLeadTimesExpectedButMissing() + { + TimePools leadTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 18 ) ) + .build(); + + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .leadTimePools( leadTimePools ) + .build(); + + NullPointerException thrown = assertThrows( NullPointerException.class, + () -> TimeWindowSlicer.getTimeWindows( declaration ) ); + + assertEquals( "Cannot determine lead duration time windows without 'lead_times'.", + thrown.getMessage() ); + } + + /** + * Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} for an expected exception when the + * minimum lead_times is required but missing. + */ + + @Test + void testGetTimeWindowsThrowsNullPointerExceptionWhenLeadTimesMinimumExpectedButMissing() + { + LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() + .maximum( Duration.ofHours( 40 ) ) + .build(); + TimePools leadTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 18 ) ) + .build(); + + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .leadTimes( leadTimes ) + .leadTimePools( leadTimePools ) + .build(); + + NullPointerException thrown = assertThrows( NullPointerException.class, + () -> TimeWindowSlicer.getTimeWindows( declaration ) ); + + assertEquals( "Cannot determine lead duration time windows without a 'minimum' value for 'lead_times'.", + thrown.getMessage() ); + } + + /** + * Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} for an expected exception when the + * maximum lead_times is required but missing. + */ + + @Test + void testGetTimeWindowsThrowsNullPointerExceptionWhenLeadTimesMaximumExpectedButMissing() + { + LeadTimeInterval leadTimes = LeadTimeIntervalBuilder.builder() + .minimum( Duration.ofHours( 0 ) ) + .build(); + TimePools leadTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 18 ) ) + .build(); + + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .leadTimes( leadTimes ) + .leadTimePools( leadTimePools ) + .build(); + + NullPointerException thrown = assertThrows( NullPointerException.class, + () -> TimeWindowSlicer.getTimeWindows( declaration ) ); + + assertEquals( "Cannot determine lead duration time windows without a 'maximum' value for 'lead_times'.", + thrown.getMessage() ); + } + + /** + * Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} for an expected exception when + * reference_dates is required but missing. + */ + + @Test + void testGetTimeWindowsThrowsNullPointerExceptionWhenReferenceDatesExpectedButMissing() + { + TimePools referenceTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 13 ) ) + .frequency( Duration.ofHours( 7 ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .referenceDatePools( referenceTimePools ) + .build(); + + NullPointerException thrown = assertThrows( NullPointerException.class, + () -> TimeWindowSlicer.getTimeWindows( declaration ) ); + + assertEquals( "Cannot determine reference time windows without 'reference_dates'.", + thrown.getMessage() ); + } + + /** + * Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} for an expected exception when the + * minimum reference_dates is required but missing. + */ + + @Test + void testGetTimeWindowsThrowsNullPointerExceptionWhenReferenceDatesEarliestExpectedButMissing() + { + TimeInterval referenceDates = TimeIntervalBuilder.builder() + .maximum( Instant.parse( INSTANT_ONE ) ) + .build(); + TimePools referenceTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 13 ) ) + .frequency( Duration.ofHours( 7 ) ) + .build(); + + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .referenceDates( referenceDates ) + .referenceDatePools( referenceTimePools ) + .build(); + + NullPointerException thrown = assertThrows( NullPointerException.class, + () -> TimeWindowSlicer.getTimeWindows( declaration ) ); + + assertEquals( "Cannot determine reference time windows without the 'minimum' for the 'reference_dates'.", + thrown.getMessage() ); + } + + /** + * Tests the {@link TimeWindowSlicer#getTimeWindows(EvaluationDeclaration)} for an expected exception when the + * maximum reference_dates is required but missing. + */ + + @Test + void testGetTimeWindowsThrowsNullPointerExceptionWhenReferenceDatesLatestExpectedButMissing() + { + TimeInterval referenceDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_ONE ) ) + .build(); + TimePools referenceTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 13 ) ) + .frequency( Duration.ofHours( 7 ) ) + .build(); + + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .referenceDates( referenceDates ) + .referenceDatePools( referenceTimePools ) + .build(); + + NullPointerException thrown = assertThrows( NullPointerException.class, + () -> TimeWindowSlicer.getTimeWindows( declaration ) ); + + assertEquals( "Cannot determine reference time windows without the 'maximum' for the 'reference_dates'.", + thrown.getMessage() ); + } + + @Test + void testGetTimeWindowsThrowsNullPointerExceptionWhenValidDatesExpectedButMissing() + { + TimePools validTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 13 ) ) + .frequency( Duration.ofHours( 7 ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .validDatePools( validTimePools ) + .build(); + + NullPointerException thrown = assertThrows( NullPointerException.class, + () -> TimeWindowSlicer.getTimeWindows( declaration ) ); + + assertEquals( "Cannot determine valid time windows without 'valid_dates'.", + thrown.getMessage() ); + } + + @Test + void testGetTimeWindowsThrowsNullPointerExceptionWhenValidDatesEarliestExpectedButMissing() + { + TimeInterval validDates = TimeIntervalBuilder.builder() + .maximum( Instant.parse( INSTANT_ONE ) ) + .build(); + TimePools validTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 13 ) ) + .frequency( Duration.ofHours( 7 ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .validDates( validDates ) + .validDatePools( validTimePools ) + .build(); + + NullPointerException thrown = assertThrows( NullPointerException.class, + () -> TimeWindowSlicer.getTimeWindows( declaration ) ); + + assertEquals( "Cannot determine valid time windows without the 'minimum' for the 'valid_dates'.", + thrown.getMessage() ); + } + + @Test + void testGetTimeWindowsThrowsNullPointerExceptionWhenValidDatesLatestExpectedButMissing() + { + TimeInterval validDates = TimeIntervalBuilder.builder() + .minimum( Instant.parse( INSTANT_ONE ) ) + .build(); + TimePools validTimePools = TimePoolsBuilder.builder() + .period( Duration.ofHours( 13 ) ) + .frequency( Duration.ofHours( 7 ) ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() + .validDates( validDates ) + .validDatePools( validTimePools ) + .build(); + + NullPointerException thrown = assertThrows( NullPointerException.class, + () -> TimeWindowSlicer.getTimeWindows( declaration ) ); + + assertEquals( "Cannot determine valid time windows without the 'maximum' for the 'valid_dates'.", + thrown.getMessage() ); + } + +} diff --git a/wres-eventdetection/src/wres/eventdetection/ReginaOgdenEventDetector.java b/wres-eventdetection/src/wres/eventdetection/ReginaOgdenEventDetector.java index 517d1cebe8..aded7c506c 100644 --- a/wres-eventdetection/src/wres/eventdetection/ReginaOgdenEventDetector.java +++ b/wres-eventdetection/src/wres/eventdetection/ReginaOgdenEventDetector.java @@ -189,19 +189,19 @@ private void setSeriesSpecificParameterDefaults( EventDetectionParametersBuilder if ( Objects.nonNull( builder.windowSize() ) ) { builder.halfLife( builder.windowSize() - .dividedBy( 10 ) ); + .dividedBy( 20 ) ); LOGGER.warn( "When performing event detection with the Regina-Ogden method, the half-life was " + "undefined. However, the window size was defined. The default half life is {}, " - + "which is one tenth of the window size. This default may not be appropriate and it " - + "is strongly recommended that you set the half life explicitly using the " + + "which is one twentieth of the window size. This default may not be appropriate and " + + "it is strongly recommended that you set the half life explicitly using the " + "'half_life' parameter.", builder.halfLife() ); } else { - builder.halfLife( averageTimestep.multipliedBy( 10 ) ); + builder.halfLife( averageTimestep.multipliedBy( 20 ) ); LOGGER.warn( "When performing event detection with the Regina-Ogden method, the half life was " - + "undefined. The default half life is {}, which is ten times the modal time-step " + + "undefined. The default half life is {}, which is twenty times the modal time-step " + "associated with the time-series used for event detection. This default may not be " + "appropriate and it is strongly recommended that you set the half life explicitly " + "using the 'half_life' parameter.", @@ -215,20 +215,20 @@ private void setSeriesSpecificParameterDefaults( EventDetectionParametersBuilder if ( Objects.nonNull( parameters.halfLife() ) ) { builder.windowSize( parameters.halfLife() - .multipliedBy( 10 ) ); - LOGGER.warn( "When performing event detection with the Regina-Ogden method, the window wize for " + .multipliedBy( 20 ) ); + LOGGER.warn( "When performing event detection with the Regina-Ogden method, the window size for " + "smoothing and detecting trends was undefined. However, the half life was defined. " - + "The default window size is {}, which is ten times the half-life. This default may " - + "not be appropriate and it is strongly recommended that you set the window size " + + "The default window size is {}, which is twenty times the half-life. This default " + + "may not be appropriate and it is strongly recommended that you set the window size " + "explicitly using the 'window_size' parameter.", builder.windowSize() ); } else { - builder.windowSize( averageTimestep.multipliedBy( 100 ) ); - LOGGER.warn( "When performing event detection with the Regina-Ogden method, the window wize for " + builder.windowSize( averageTimestep.multipliedBy( 200 ) ); + LOGGER.warn( "When performing event detection with the Regina-Ogden method, the window size for " + "smoothing and detecting trends was undefined. The default window size is {}, which " - + "is one hundred times the modal time-step associated with the time-series used for " + + "is two hundred times the modal time-step associated with the time-series used for " + "event detection. This default may not be appropriate and it is strongly " + "recommended that you set the window size explicitly using the 'window_size' " + "parameter.", diff --git a/wres-io/src/wres/io/retrieving/RetrieverUtilities.java b/wres-io/src/wres/io/retrieving/RetrieverUtilities.java index 724e98a3d4..89a24386dd 100644 --- a/wres-io/src/wres/io/retrieving/RetrieverUtilities.java +++ b/wres-io/src/wres/io/retrieving/RetrieverUtilities.java @@ -26,6 +26,7 @@ import wres.datamodel.time.TimeSeries; import wres.datamodel.time.TimeSeriesSlicer; import wres.datamodel.time.TimeWindowOuter; +import wres.datamodel.time.TimeWindowSlicer; import wres.statistics.MessageFactory; import wres.statistics.generated.TimeWindow; import wres.statistics.generated.ReferenceTime.ReferenceTimeType; @@ -185,7 +186,7 @@ public static TimeWindowOuter getTimeWindowWithUnconditionalLeadTimes( TimeWindo .setLatestLeadDuration( upper ) .build(); - return TimeSeriesSlicer.adjustTimeWindowForTimeScale( TimeWindowOuter.of( inner ), + return TimeWindowSlicer.adjustTimeWindowForTimeScale( TimeWindowOuter.of( inner ), timeScale ); } diff --git a/wres-io/src/wres/io/retrieving/database/TimeSeriesRetriever.java b/wres-io/src/wres/io/retrieving/database/TimeSeriesRetriever.java index f069ab3eab..fa5efe5dbc 100644 --- a/wres-io/src/wres/io/retrieving/database/TimeSeriesRetriever.java +++ b/wres-io/src/wres/io/retrieving/database/TimeSeriesRetriever.java @@ -38,9 +38,9 @@ import wres.datamodel.time.TimeSeries; import wres.datamodel.time.TimeSeriesMetadata; -import wres.datamodel.time.TimeSeriesSlicer; import wres.datamodel.time.TimeWindowOuter; import wres.datamodel.DataProvider; +import wres.datamodel.time.TimeWindowSlicer; import wres.io.database.caching.Features; import wres.io.database.caching.MeasurementUnits; import wres.io.database.DataScripter; @@ -524,7 +524,7 @@ void addTimeWindowClause( DataScripter script ) // Subtract any non-instantaneous desired timescale period from the lower bound of the lead duration and // valid time - filter = TimeSeriesSlicer.adjustTimeWindowForTimeScale( filter, this.desiredTimeScale ); + filter = TimeWindowSlicer.adjustTimeWindowForTimeScale( filter, this.desiredTimeScale ); // Forecasts? if ( this.isForecast() ) diff --git a/wres-io/src/wres/io/retrieving/memory/EnsembleRetrieverFactoryInMemory.java b/wres-io/src/wres/io/retrieving/memory/EnsembleRetrieverFactoryInMemory.java index 056a6efa1c..f55d17a9cf 100644 --- a/wres-io/src/wres/io/retrieving/memory/EnsembleRetrieverFactoryInMemory.java +++ b/wres-io/src/wres/io/retrieving/memory/EnsembleRetrieverFactoryInMemory.java @@ -11,10 +11,10 @@ import wres.config.yaml.components.Dataset; import wres.config.yaml.components.DatasetOrientation; import wres.config.yaml.components.Variable; +import wres.datamodel.time.TimeWindowSlicer; import wres.datamodel.types.Ensemble; import wres.datamodel.space.Feature; import wres.datamodel.time.TimeSeries; -import wres.datamodel.time.TimeSeriesSlicer; import wres.datamodel.time.TimeSeriesStore; import wres.datamodel.time.TimeWindowOuter; import wres.io.project.Project; @@ -126,7 +126,7 @@ public Supplier>> getRightRetriever( Set fe Objects.requireNonNull( features ); Objects.requireNonNull( timeWindow ); - TimeWindowOuter adjustedWindow = TimeSeriesSlicer.adjustTimeWindowForTimeScale( timeWindow, + TimeWindowOuter adjustedWindow = TimeWindowSlicer.adjustTimeWindowForTimeScale( timeWindow, this.project.getDesiredTimeScale() ); Dataset data = DeclarationUtilities.getDeclaredDataset( this.project.getDeclaration(), @@ -169,7 +169,7 @@ public Supplier>> getBaselineRetriever( Set Objects.requireNonNull( features ); Objects.requireNonNull( timeWindow ); - TimeWindowOuter adjustedWindow = TimeSeriesSlicer.adjustTimeWindowForTimeScale( timeWindow, + TimeWindowOuter adjustedWindow = TimeWindowSlicer.adjustTimeWindowForTimeScale( timeWindow, this.project.getDesiredTimeScale() ); Dataset data = DeclarationUtilities.getDeclaredDataset( this.project.getDeclaration(), diff --git a/wres-io/src/wres/io/retrieving/memory/EnsembleSingleValuedRetrieverFactoryInMemory.java b/wres-io/src/wres/io/retrieving/memory/EnsembleSingleValuedRetrieverFactoryInMemory.java index bd4af6689b..4c3d38cd8f 100644 --- a/wres-io/src/wres/io/retrieving/memory/EnsembleSingleValuedRetrieverFactoryInMemory.java +++ b/wres-io/src/wres/io/retrieving/memory/EnsembleSingleValuedRetrieverFactoryInMemory.java @@ -11,10 +11,10 @@ import wres.config.yaml.components.Dataset; import wres.config.yaml.components.DatasetOrientation; import wres.config.yaml.components.Variable; +import wres.datamodel.time.TimeWindowSlicer; import wres.datamodel.types.Ensemble; import wres.datamodel.space.Feature; import wres.datamodel.time.TimeSeries; -import wres.datamodel.time.TimeSeriesSlicer; import wres.datamodel.time.TimeSeriesStore; import wres.datamodel.time.TimeWindowOuter; import wres.io.project.Project; @@ -128,7 +128,7 @@ public Supplier>> getLeftRetriever( Set featu public Supplier>> getRightRetriever( Set features, TimeWindowOuter timeWindow ) { - TimeWindowOuter adjustedWindow = TimeSeriesSlicer.adjustTimeWindowForTimeScale( timeWindow, + TimeWindowOuter adjustedWindow = TimeWindowSlicer.adjustTimeWindowForTimeScale( timeWindow, this.project.getDesiredTimeScale() ); Dataset data = DeclarationUtilities.getDeclaredDataset( this.project.getDeclaration(), @@ -169,7 +169,7 @@ public Supplier>> getBaselineRetriever( Set f public Supplier>> getBaselineRetriever( Set features, TimeWindowOuter timeWindow ) { - TimeWindowOuter adjustedWindow = TimeSeriesSlicer.adjustTimeWindowForTimeScale( timeWindow, + TimeWindowOuter adjustedWindow = TimeWindowSlicer.adjustTimeWindowForTimeScale( timeWindow, this.project.getDesiredTimeScale() ); Dataset data = DeclarationUtilities.getDeclaredDataset( this.project.getDeclaration(), diff --git a/wres-io/src/wres/io/retrieving/memory/SingleValuedRetrieverFactoryInMemory.java b/wres-io/src/wres/io/retrieving/memory/SingleValuedRetrieverFactoryInMemory.java index 5ae1c147a7..13f01938e0 100644 --- a/wres-io/src/wres/io/retrieving/memory/SingleValuedRetrieverFactoryInMemory.java +++ b/wres-io/src/wres/io/retrieving/memory/SingleValuedRetrieverFactoryInMemory.java @@ -21,6 +21,7 @@ import wres.datamodel.time.TimeSeriesSlicer; import wres.datamodel.time.TimeSeriesStore; import wres.datamodel.time.TimeWindowOuter; +import wres.datamodel.time.TimeWindowSlicer; import wres.io.project.Project; import wres.io.retrieving.CachingRetriever; import wres.io.retrieving.DuplicatePolicy; @@ -121,7 +122,7 @@ public Supplier>> getLeftRetriever( Set featu public Supplier>> getRightRetriever( Set features, TimeWindowOuter timeWindow ) { - TimeWindowOuter adjustedWindow = TimeSeriesSlicer.adjustTimeWindowForTimeScale( timeWindow, + TimeWindowOuter adjustedWindow = TimeWindowSlicer.adjustTimeWindowForTimeScale( timeWindow, this.project.getDesiredTimeScale() ); Dataset data = DeclarationUtilities.getDeclaredDataset( this.project.getDeclaration(), @@ -159,7 +160,7 @@ public Supplier>> getBaselineRetriever( Set f public Supplier>> getBaselineRetriever( Set features, TimeWindowOuter timeWindow ) { - TimeWindowOuter adjustedWindow = TimeSeriesSlicer.adjustTimeWindowForTimeScale( timeWindow, + TimeWindowOuter adjustedWindow = TimeWindowSlicer.adjustTimeWindowForTimeScale( timeWindow, this.project.getDesiredTimeScale() ); Dataset data = DeclarationUtilities.getDeclaredDataset( this.project.getDeclaration(), diff --git a/wres-reading/src/wres/reading/netcdf/nwm/NwmGridReader.java b/wres-reading/src/wres/reading/netcdf/nwm/NwmGridReader.java index dd11ee5ab3..d7f343d670 100644 --- a/wres-reading/src/wres/reading/netcdf/nwm/NwmGridReader.java +++ b/wres-reading/src/wres/reading/netcdf/nwm/NwmGridReader.java @@ -25,6 +25,7 @@ import wres.datamodel.space.Feature; import wres.datamodel.time.TimeSeries; import wres.datamodel.time.TimeWindowOuter; +import wres.datamodel.time.TimeWindowSlicer; import wres.reading.netcdf.grid.GridRequest; import wres.reading.netcdf.grid.GridReader; import wres.reading.netcdf.grid.GriddedFeatures; @@ -34,7 +35,6 @@ import wres.reading.TimeSeriesReader; import wres.reading.TimeSeriesTuple; import wres.reading.DataSource.DataDisposition; -import wres.statistics.generated.TimeWindow; /** *

A reader of time-series data from a gridded Netcdf source of National Water Model (NWM) forecasts, simulations or @@ -180,8 +180,7 @@ private Stream readNetcdf( DataSource dataSource, NetcdfFile fi } // Time window constrained only by the pair declaration - TimeWindow timeWindowInner = DeclarationUtilities.getOneBigTimeWindow( this.getDeclaration() ); - TimeWindowOuter timeWindow = TimeWindowOuter.of( timeWindowInner ); + TimeWindowOuter timeWindow = TimeWindowSlicer.getOneBigTimeWindow( this.getDeclaration() ); // The gridded reader requires a hint about the data type, specifically whether it is a forecast type. This is // unlike most other readers where the type can be inferred directly from the structure of the time-series data, diff --git a/wres-writing/src/wres/writing/netcdf/NetcdfOutputWriter.java b/wres-writing/src/wres/writing/netcdf/NetcdfOutputWriter.java index 09190d564c..68abe11f09 100644 --- a/wres-writing/src/wres/writing/netcdf/NetcdfOutputWriter.java +++ b/wres-writing/src/wres/writing/netcdf/NetcdfOutputWriter.java @@ -61,6 +61,7 @@ import wres.datamodel.pools.PoolMetadata; import wres.datamodel.DataUtilities; import wres.datamodel.MissingValues; +import wres.datamodel.time.TimeWindowSlicer; import wres.datamodel.types.OneOrTwoDoubles; import wres.config.MetricConstants; import wres.config.MetricConstants.MetricGroup; @@ -421,10 +422,7 @@ public void createBlobsForWriting( Set featureGroups, } // Time windows - Set timeWindows = DeclarationUtilities.getTimeWindows( this.getDeclaration() ) - .stream() - .map( TimeWindowOuter::of ) - .collect( Collectors.toSet() ); + Set timeWindows = TimeWindowSlicer.getTimeWindows( this.getDeclaration() ); // Find the thresholds-by-metric for which blobs should be created