diff --git a/test/wres/pipeline/pooling/CovariateFilterTest.java b/test/wres/pipeline/pooling/CovariateFilterTest.java index 5bb53ec5a9..374844f590 100644 --- a/test/wres/pipeline/pooling/CovariateFilterTest.java +++ b/test/wres/pipeline/pooling/CovariateFilterTest.java @@ -13,6 +13,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import wres.config.yaml.components.CovariateDataset; +import wres.config.yaml.components.CovariateDatasetBuilder; import wres.config.yaml.components.DataType; import wres.config.yaml.components.Dataset; import wres.config.yaml.components.DatasetBuilder; @@ -52,8 +53,10 @@ void testUnconditionalFilter() .name( "foo" ) .build() ) .build(); - CovariateDataset covariateDataset = - new CovariateDataset( covariateData, null, null, DatasetOrientation.LEFT, null ); + CovariateDataset covariateDataset = CovariateDatasetBuilder.builder() + .dataset( covariateData ) + .featureNameOrientation( DatasetOrientation.LEFT ) + .build(); // Unconditional filter Predicate filter = d -> true; @@ -149,8 +152,10 @@ void testFilterWithTimeShift() // Add 4 hours .timeShift( Duration.ofHours( 4 ) ) .build(); - CovariateDataset covariateDataset = - new CovariateDataset( covariateData, null, null, DatasetOrientation.LEFT, null ); + CovariateDataset covariateDataset = CovariateDatasetBuilder.builder() + .dataset( covariateData ) + .featureNameOrientation( DatasetOrientation.LEFT ) + .build(); Predicate filter = d -> d > 0.5; @@ -250,9 +255,10 @@ void testFilterWithUpscaling() .build() ) .timeScale( existingTimeScale ) .build(); - CovariateDataset covariateDataset = - new CovariateDataset( covariateData, null, null, DatasetOrientation.LEFT, null ); - + CovariateDataset covariateDataset = CovariateDatasetBuilder.builder() + .dataset( covariateData ) + .featureNameOrientation( DatasetOrientation.LEFT ) + .build(); Predicate filter = d -> d > 0.5; TimeScaleOuter desiredTimeScale = TimeScaleOuter.of( Duration.ofHours( 2 ), diff --git a/wres-config/nonsrc/schema.yml b/wres-config/nonsrc/schema.yml index 4852b5cf99..8298f79bb2 100644 --- a/wres-config/nonsrc/schema.yml +++ b/wres-config/nonsrc/schema.yml @@ -100,6 +100,10 @@ definitions: "$ref": "#/definitions/Dates" time_pools: "$ref": "#/definitions/TimePools" + event_detection: + anyOf: + - "$ref": "#/definitions/EventDetection" + - "$ref": "#/definitions/DatasetEnum" reference_date_pools: "$ref": "#/definitions/TimePoolSequence" valid_dates: @@ -381,8 +385,9 @@ definitions: Covariate: title: An evaluation covariate. - description: "A covariate is cross-paired with the main variates (observed, - predicted and baseline) and used to filter these variates." + description: "A covariate is an associated source of time-series data, which + may be used to filter the main variates (observed, predicted and baseline) + or conduct event detection." "$ref": "#/definitions/BasicDataset" # Close this schema to unevaluated properties unevaluatedProperties: false @@ -390,12 +395,21 @@ definitions: # The minimum value of the covariate minimum: type: number - # The minimum value of the covariate + # The maximum value of the covariate maximum: type: number # Target time-scale function, if not the evaluation time-scale function rescale_function: "$ref": "#/definitions/TimeScaleFunctionEnum" + # Purpose(s) of the covariate + purpose: + anyOf: + - "$ref": "#/definitions/CovariatePurposeEnum" + - type: array + items: + "$ref": "#/definitions/CovariatePurposeEnum" + uniqueItems: true + minItems: 1 Source: title: A data source. @@ -560,6 +574,24 @@ definitions: - period - unit + EventDetection: + title: Time-series event detection + description: "Performs time-series event detection using a declared dataset + and optional detection parameters." + type: object + additionalProperties: false + properties: + dataset: + anyOf: + - "$ref": "#/definitions/DatasetEnum" + - type: array + items: + "$ref": "#/definitions/DatasetEnum" + uniqueItems: true + minItems: 1 + required: + - dataset + CrossPair: title: Cross-pairing of time-series for consistency description: "Applies cross-pairing to the time-series within an evaluation @@ -1490,6 +1522,20 @@ definitions: - minimum - maximum + CovariatePurposeEnum: + type: string + enum: + - filter + - detect + + DatasetEnum: + type: string + enum: + - observed + - predicted + - baseline + - covariates + TimeScaleLenienceEnum: title: The lenience to apply when rescaling time-series. description: "The side(s) of data where lenience should be applied when diff --git a/wres-config/src/wres/config/yaml/components/CovariateDataset.java b/wres-config/src/wres/config/yaml/components/CovariateDataset.java index 0356c8121d..aff944eb37 100644 --- a/wres-config/src/wres/config/yaml/components/CovariateDataset.java +++ b/wres-config/src/wres/config/yaml/components/CovariateDataset.java @@ -1,6 +1,7 @@ package wres.config.yaml.components; import java.util.Objects; +import java.util.Set; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @@ -16,6 +17,7 @@ * @param maximum the maximum value, optional * @param featureNameOrientation the orientation of the feature names used by the covariate dataset, optional * @param rescaleFunction the timescale function to use when it differs from the evaluation timescale function + * @param purposes the purposes or applications of the covariate dataset, optional */ @RecordBuilder @JsonDeserialize( using = CovariateDatasetDeserializer.class ) @@ -23,7 +25,8 @@ public record CovariateDataset( Dataset dataset, @JsonProperty( "minimum" ) Double minimum, @JsonProperty( "maximum" ) Double maximum, DatasetOrientation featureNameOrientation, - @JsonProperty( "rescale_function" ) TimeScale.TimeScaleFunction rescaleFunction ) + @JsonProperty( "rescale_function" ) TimeScale.TimeScaleFunction rescaleFunction, + @JsonProperty( "purpose" ) Set purposes ) { /** * Creates an instance. @@ -32,9 +35,16 @@ public record CovariateDataset( Dataset dataset, * @param maximum the maximum value, optional * @param featureNameOrientation the orientation of the feature names used by the covariate dataset, optional * @param rescaleFunction the timescale function to use when it differs from the evaluation timescale function + * @param purposes the purposes or applications of the covariate dataset, optional */ public CovariateDataset { Objects.requireNonNull( dataset, "The covariate dataset cannot be null." ); + + if ( Objects.isNull( purposes ) + || purposes.isEmpty() ) + { + purposes = Set.of( CovariatePurpose.FILTER ); + } } } diff --git a/wres-config/src/wres/config/yaml/components/CovariatePurpose.java b/wres-config/src/wres/config/yaml/components/CovariatePurpose.java new file mode 100644 index 0000000000..dd06fd7c9b --- /dev/null +++ b/wres-config/src/wres/config/yaml/components/CovariatePurpose.java @@ -0,0 +1,15 @@ +package wres.config.yaml.components; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * The purpose or application of a covariate dataset within an evaluation. + * @author James Brown + */ +public enum CovariatePurpose +{ + /** Use the covariate dataset to filter the pairs. */ + @JsonProperty( "filter" ) FILTER, + /** Use the covariate dataset to detect events for evaluation. */ + @JsonProperty( "detect" ) DETECT +} \ No newline at end of file diff --git a/wres-config/src/wres/config/yaml/components/EvaluationDeclaration.java b/wres-config/src/wres/config/yaml/components/EvaluationDeclaration.java index 2678721b0e..8b89b9c837 100644 --- a/wres-config/src/wres/config/yaml/components/EvaluationDeclaration.java +++ b/wres-config/src/wres/config/yaml/components/EvaluationDeclaration.java @@ -57,6 +57,7 @@ * @param leadTimes lead times * @param analysisTimes analysis durations * @param leadTimePools lead time pools + * @param eventDetection event detection * @param timeScale the evaluation timescale * @param rescaleLenience whether rescaling should admit periods with missing values * @param pairFrequency the frequency of the paired data @@ -98,6 +99,7 @@ public record EvaluationDeclaration( @JsonProperty( "label" ) String label, @JsonProperty( "valid_date_pools" ) TimePools validDatePools, @JsonProperty( "lead_times" ) LeadTimeInterval leadTimes, @JsonProperty( "lead_time_pools" ) TimePools leadTimePools, + @JsonProperty( "event_detection" ) EventDetection eventDetection, @JsonProperty( "analysis_times" ) AnalysisTimes analysisTimes, @JsonProperty( "time_scale" ) TimeScale timeScale, @JsonProperty( "rescale_lenience" ) TimeScaleLenience rescaleLenience, @@ -161,6 +163,7 @@ public record EvaluationDeclaration( @JsonProperty( "label" ) String label, * @param leadTimes lead times * @param analysisTimes analysis durations * @param leadTimePools lead time pools + * @param eventDetection event detection * @param timeScale the evaluation timescale * @param rescaleLenience whether rescaling should admit periods with missing values * @param pairFrequency the frequency of the paired data diff --git a/wres-config/src/wres/config/yaml/components/EventDetection.java b/wres-config/src/wres/config/yaml/components/EventDetection.java new file mode 100644 index 0000000000..988a8dd0ec --- /dev/null +++ b/wres-config/src/wres/config/yaml/components/EventDetection.java @@ -0,0 +1,33 @@ +package wres.config.yaml.components; + +import java.util.Objects; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import io.soabase.recordbuilder.core.RecordBuilder; + +import wres.config.yaml.deserializers.EventDetectionDeserializer; + +/** + * Used for the detection of discrete events within time-series. + * @param datasets the datasets to use for event detection + */ +@RecordBuilder +@JsonDeserialize( using = EventDetectionDeserializer.class ) +public record EventDetection( @JsonProperty( "dataset" ) Set datasets ) +{ + /** + * Creates an instance. + * @param datasets the datasets to use for event detection + */ + public EventDetection + { + Objects.requireNonNull( datasets, "The event detection dataset cannot be null." ); + + if ( datasets.isEmpty() ) + { + throw new IllegalArgumentException( "Declare at least one dataset for event detection." ); + } + } +} \ No newline at end of file diff --git a/wres-config/src/wres/config/yaml/components/EventDetectionDataset.java b/wres-config/src/wres/config/yaml/components/EventDetectionDataset.java new file mode 100644 index 0000000000..a97f272c5f --- /dev/null +++ b/wres-config/src/wres/config/yaml/components/EventDetectionDataset.java @@ -0,0 +1,19 @@ +package wres.config.yaml.components; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * The context of a dataset to use for event detection. + * @author James Brown + */ +public enum EventDetectionDataset +{ + /** Observed dataset. */ + @JsonProperty( "observed" ) OBSERVED, + /** Predicted dataset. */ + @JsonProperty( "predicted" ) PREDICTED, + /** Baseline dataset. */ + @JsonProperty( "baseline" ) BASELINE, + /** Covariates dataset. */ + @JsonProperty( "covariates" ) COVARIATES +} \ No newline at end of file diff --git a/wres-config/src/wres/config/yaml/deserializers/CovariateDatasetDeserializer.java b/wres-config/src/wres/config/yaml/deserializers/CovariateDatasetDeserializer.java index 50ab6d5edc..bf8fd91381 100644 --- a/wres-config/src/wres/config/yaml/deserializers/CovariateDatasetDeserializer.java +++ b/wres-config/src/wres/config/yaml/deserializers/CovariateDatasetDeserializer.java @@ -1,14 +1,20 @@ package wres.config.yaml.deserializers; import java.io.IOException; +import java.util.Collections; import java.util.Objects; +import java.util.Set; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectReader; import wres.config.yaml.components.CovariateDataset; +import wres.config.yaml.components.CovariateDatasetBuilder; +import wres.config.yaml.components.CovariatePurpose; import wres.config.yaml.components.Dataset; import wres.config.yaml.components.DatasetOrientation; import wres.statistics.generated.TimeScale; @@ -32,6 +38,7 @@ public CovariateDataset deserialize( JsonParser jp, DeserializationContext conte Double minimum = null; Double maximum = null; TimeScale.TimeScaleFunction rescaleFunction = null; + Set purposes = Collections.singleton( CovariatePurpose.FILTER ); // Not part of the declaration language, just used internally DatasetOrientation featureNameOrientation = null; @@ -59,8 +66,35 @@ public CovariateDataset deserialize( JsonParser jp, DeserializationContext conte .toUpperCase(); rescaleFunction = TimeScale.TimeScaleFunction.valueOf( functionString ); } + + if ( lastNode.has( "purpose" ) ) + { + JsonNode purposeNode = lastNode.get( "purpose" ); + + if ( purposeNode.isTextual() ) + { + String purposeString = purposeNode.asText() + .toUpperCase(); + CovariatePurpose purposeEnum = CovariatePurpose.valueOf( purposeString ); + purposes = Collections.singleton( purposeEnum ); + } + else if ( purposeNode.isArray() ) + { + ObjectReader reader = ( ObjectReader ) jp.getCodec(); + JavaType type = reader.getTypeFactory() + .constructCollectionType( Set.class, CovariatePurpose.class ); + JsonParser parser = reader.treeAsTokens( purposeNode ); + purposes = reader.readValue( parser, type ); + } + } } - return new CovariateDataset( basicDataset, minimum, maximum, featureNameOrientation, rescaleFunction ); + return CovariateDatasetBuilder.builder().dataset( basicDataset ) + .minimum( minimum ) + .maximum( maximum ) + .featureNameOrientation( featureNameOrientation ) + .rescaleFunction( rescaleFunction ) + .purposes( purposes ) + .build(); } } \ No newline at end of file diff --git a/wres-config/src/wres/config/yaml/deserializers/DatasetDeserializer.java b/wres-config/src/wres/config/yaml/deserializers/DatasetDeserializer.java index 913d33ba85..7bd4abb74b 100644 --- a/wres-config/src/wres/config/yaml/deserializers/DatasetDeserializer.java +++ b/wres-config/src/wres/config/yaml/deserializers/DatasetDeserializer.java @@ -304,8 +304,9 @@ private EnsembleFilter getEnsembleFilter( ObjectReader reader, JsonNode node ) t { JsonNode memberNode = filterNode.get( "members" ); members = this.getMembers( reader, memberNode ); - exclude = filterNode.has( "exclude" ) && filterNode.get( "exclude" ) - .asBoolean(); + exclude = filterNode.has( "exclude" ) + && filterNode.get( "exclude" ) + .asBoolean(); } // Ordinary member declaration diff --git a/wres-config/src/wres/config/yaml/deserializers/EventDetectionDeserializer.java b/wres-config/src/wres/config/yaml/deserializers/EventDetectionDeserializer.java new file mode 100644 index 0000000000..924387d5e5 --- /dev/null +++ b/wres-config/src/wres/config/yaml/deserializers/EventDetectionDeserializer.java @@ -0,0 +1,69 @@ +package wres.config.yaml.deserializers; + +import java.io.IOException; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectReader; + +import wres.config.yaml.components.EventDetection; +import wres.config.yaml.components.EventDetectionBuilder; +import wres.config.yaml.components.EventDetectionDataset; + +/** + * Custom deserializer for a {@link EventDetection}. + * + * @author James Brown + */ +public class EventDetectionDeserializer extends JsonDeserializer +{ + @Override + public EventDetection deserialize( JsonParser jp, DeserializationContext context ) + throws IOException + { + Objects.requireNonNull( jp ); + + ObjectReader reader = ( ObjectReader ) jp.getCodec(); + JsonNode node = reader.readTree( jp ); + + Set datasets = Set.of(); + + // Single string + if ( node.isTextual() ) + { + String datasetString = node.asText() + .toUpperCase(); + EventDetectionDataset dataset = EventDetectionDataset.valueOf( datasetString ); + datasets = Collections.singleton( dataset ); + } + else if ( node.has( "dataset" ) ) + { + JsonNode datasetNode = node.get( "dataset" ); + if ( datasetNode.isTextual() ) + { + String datasetString = datasetNode.asText() + .toUpperCase(); + EventDetectionDataset dataset = EventDetectionDataset.valueOf( datasetString ); + datasets = Collections.singleton( dataset ); + } + else if ( datasetNode.isArray() ) + { + JavaType type = reader.getTypeFactory() + .constructCollectionType( Set.class, EventDetectionDataset.class ); + JsonParser parser = reader.treeAsTokens( datasetNode ); + datasets = reader.readValue( parser, type ); + } + } + + return EventDetectionBuilder.builder() + .datasets( datasets ) + .build(); + } + +} \ No newline at end of file diff --git a/wres-config/test/wres/config/yaml/DeclarationFactoryTest.java b/wres-config/test/wres/config/yaml/DeclarationFactoryTest.java index 4d6897a945..f14902fccb 100644 --- a/wres-config/test/wres/config/yaml/DeclarationFactoryTest.java +++ b/wres-config/test/wres/config/yaml/DeclarationFactoryTest.java @@ -33,6 +33,8 @@ import wres.config.yaml.components.AnalysisTimes; import wres.config.yaml.components.BaselineDataset; import wres.config.yaml.components.CovariateDataset; +import wres.config.yaml.components.CovariateDatasetBuilder; +import wres.config.yaml.components.CovariatePurpose; import wres.config.yaml.components.CrossPair; import wres.config.yaml.components.CrossPairMethod; import wres.config.yaml.components.CrossPairScope; @@ -43,6 +45,9 @@ import wres.config.yaml.components.EnsembleFilter; import wres.config.yaml.components.EnsembleFilterBuilder; import wres.config.yaml.components.EvaluationDeclaration; +import wres.config.yaml.components.EventDetection; +import wres.config.yaml.components.EventDetectionBuilder; +import wres.config.yaml.components.EventDetectionDataset; import wres.config.yaml.components.FeatureGroups; import wres.config.yaml.components.FeatureGroupsBuilder; import wres.config.yaml.components.FeatureService; @@ -2248,8 +2253,10 @@ void testDeserializeWithCovariates() throws IOException .sources( covariateOneSources ) .variable( new Variable( "precipitation", null, Set.of() ) ) .build(); - - CovariateDataset covariateOne = new CovariateDataset( covariateOneDataset, 0.25, null, null, null ); + CovariateDataset covariateOne = CovariateDatasetBuilder.builder() + .dataset( covariateOneDataset ) + .minimum( 0.25 ) + .build(); URI covariateTwoUri = URI.create( "temperature.tgz" ); Source covariateTwoSource = SourceBuilder.builder() @@ -2263,8 +2270,11 @@ void testDeserializeWithCovariates() throws IOException .variable( new Variable( "temperature", null, Set.of() ) ) .build(); - CovariateDataset covariateTwo = - new CovariateDataset( covariateTwoDataset, null, 0.0, null, TimeScale.TimeScaleFunction.MEAN ); + CovariateDataset covariateTwo = CovariateDatasetBuilder.builder() + .dataset( covariateTwoDataset ) + .maximum( 0.0 ) + .rescaleFunction( TimeScale.TimeScaleFunction.MEAN ) + .build(); List covariateDatasets = List.of( covariateOne, covariateTwo ); @@ -2387,6 +2397,112 @@ void testDeserializeWithExplicitTimePoolsThatAreSparselyDeclared() throws IOExce assertEquals( expected, actual.timeWindows() ); } + @Test + void testDeserializeWithEventDetection() throws IOException + { + String yaml = """ + observed: some_file.csv + predicted: another_file.csv + event_detection: observed + """; + + EvaluationDeclaration actual = DeclarationFactory.from( yaml ); + + EventDetection eventDetection = EventDetectionBuilder.builder() + .datasets( Set.of( EventDetectionDataset.OBSERVED ) ) + .build(); + + EvaluationDeclaration expected = EvaluationDeclarationBuilder.builder() + .left( this.observedDataset ) + .right( this.predictedDataset ) + .eventDetection( eventDetection ) + .build(); + + assertEquals( expected, actual ); + } + + @Test + void testDeserializeWithEventDetectionUsingExplicitDataset() throws IOException + { + String yaml = """ + observed: some_file.csv + predicted: another_file.csv + event_detection: + dataset: observed + """; + + EvaluationDeclaration actual = DeclarationFactory.from( yaml ); + + EventDetection eventDetection = EventDetectionBuilder.builder() + .datasets( Set.of( EventDetectionDataset.OBSERVED ) ) + .build(); + + EvaluationDeclaration expected = EvaluationDeclarationBuilder.builder() + .left( this.observedDataset ) + .right( this.predictedDataset ) + .eventDetection( eventDetection ) + .build(); + + assertEquals( expected, actual ); + } + + @Test + void testDeserializeWithEventDetectionUsingExplicitDatasetAndCovariates() throws IOException + { + String yaml = """ + observed: + - some_file.csv + predicted: + sources: another_file.csv + covariates: + - sources: covariate.csv + purpose: + - detect + - filter + event_detection: + dataset: + - observed + - predicted + - covariates + """; + + EvaluationDeclaration actual = DeclarationFactory.from( yaml ); + + URI covariateUri = URI.create( "covariate.csv" ); + Source covariateSource = SourceBuilder.builder() + .uri( covariateUri ) + .build(); + + List covariateSources = List.of( covariateSource ); + + Dataset covariate = DatasetBuilder.builder() + .sources( covariateSources ) + .build(); + + CovariateDataset covariateDataset = CovariateDatasetBuilder.builder() + .dataset( covariate ) + .purposes( Set.of( CovariatePurpose.FILTER, + CovariatePurpose.DETECT ) ) + .build(); + + List covariateDatasets = List.of( covariateDataset ); + + EventDetection eventDetection = EventDetectionBuilder.builder() + .datasets( Set.of( EventDetectionDataset.OBSERVED, + EventDetectionDataset.PREDICTED, + EventDetectionDataset.COVARIATES ) ) + .build(); + + EvaluationDeclaration expected = EvaluationDeclarationBuilder.builder() + .left( this.observedDataset ) + .right( this.predictedDataset ) + .covariates( covariateDatasets ) + .eventDetection( eventDetection ) + .build(); + + assertEquals( expected, actual ); + } + @Test void testDeserializeThrowsExpectedExceptionWhenDuplicateKeysEncountered() { diff --git a/wres-config/test/wres/config/yaml/DeclarationInterpolatorTest.java b/wres-config/test/wres/config/yaml/DeclarationInterpolatorTest.java index 071cad7b41..b18665f453 100644 --- a/wres-config/test/wres/config/yaml/DeclarationInterpolatorTest.java +++ b/wres-config/test/wres/config/yaml/DeclarationInterpolatorTest.java @@ -30,6 +30,7 @@ import wres.config.yaml.components.BaselineDataset; import wres.config.yaml.components.BaselineDatasetBuilder; import wres.config.yaml.components.CovariateDataset; +import wres.config.yaml.components.CovariateDatasetBuilder; import wres.config.yaml.components.DataType; import wres.config.yaml.components.Dataset; import wres.config.yaml.components.DatasetBuilder; @@ -1117,7 +1118,10 @@ void testInterpolateDataTypesWhenUndeclaredAndIngestTypesSupplied() .dataset( this.predictedDataset ) .build(); - CovariateDataset covariate = new CovariateDataset( this.observedDataset, null, null, null, null ); + CovariateDataset covariate = CovariateDatasetBuilder.builder() + .dataset( this.observedDataset ) + .build(); + List covariates = List.of( covariate ); EvaluationDeclaration evaluation = EvaluationDeclarationBuilder.builder() @@ -1335,7 +1339,9 @@ void testInterpolateDimensionForSummaryStatistics() void testInterpolateVariableNames() { BaselineDataset baseline = new BaselineDataset( this.observedDataset, null, null ); - CovariateDataset covariate = new CovariateDataset( this.observedDataset, null, null, null, null ); + CovariateDataset covariate = CovariateDatasetBuilder.builder() + .dataset( this.observedDataset ) + .build(); List covariates = List.of( covariate ); EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() @@ -1378,7 +1384,9 @@ void testInterpolateVariableNames() void testInterpolateVariableNamesForTooManyDeclaredCovariatesProducesError() { BaselineDataset baseline = new BaselineDataset( this.observedDataset, null, null ); - CovariateDataset covariate = new CovariateDataset( this.observedDataset, null, null, null, null ); + CovariateDataset covariate = CovariateDatasetBuilder.builder() + .dataset( this.observedDataset ) + .build(); List covariates = List.of( covariate, covariate ); EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() @@ -1411,7 +1419,9 @@ void testInterpolateVariableNamesForTooManyDeclaredCovariatesProducesError() void testInterpolateVariableNamesForTooManyIngestedCovariatesProducesError() { BaselineDataset baseline = new BaselineDataset( this.observedDataset, null, null ); - CovariateDataset covariate = new CovariateDataset( this.observedDataset, null, null, null, null ); + CovariateDataset covariate = CovariateDatasetBuilder.builder() + .dataset( this.observedDataset ) + .build(); List covariates = List.of( covariate ); EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() @@ -1485,8 +1495,10 @@ void testInterpolateFeatureOrientationForCovariate() Dataset rightData = DatasetBuilder.builder( this.observedDataset ) .sources( List.of( source ) ) .build(); + CovariateDataset covariate = CovariateDatasetBuilder.builder() + .dataset( rightData ) + .build(); - CovariateDataset covariate = new CovariateDataset( rightData, null, null, null, null ); List covariates = List.of( covariate ); EvaluationDeclaration evaluation = EvaluationDeclarationBuilder.builder() diff --git a/wres-config/test/wres/config/yaml/DeclarationValidatorTest.java b/wres-config/test/wres/config/yaml/DeclarationValidatorTest.java index 64dc124763..c1a944c3bc 100644 --- a/wres-config/test/wres/config/yaml/DeclarationValidatorTest.java +++ b/wres-config/test/wres/config/yaml/DeclarationValidatorTest.java @@ -136,7 +136,10 @@ void testVariablesAreDeclaredWhenRequiredResultsInError() BaselineDataset baseline = BaselineDatasetBuilder.builder() .dataset( dataset ) .build(); - List covariates = List.of( new CovariateDataset( dataset, null, null, null, null ) ); + CovariateDataset covariate = CovariateDatasetBuilder.builder() + .dataset( dataset ) + .build(); + List covariates = List.of( covariate ); EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() .left( dataset ) .right( dataset ) @@ -190,11 +193,15 @@ void testNonUniqueVariableNamesForCovariatesResultsInError() Dataset dataOne = DatasetBuilder.builder( this.defaultDataset ) .variable( one ) .build(); - CovariateDataset covariateOne = new CovariateDataset( dataOne, null, null, null, null ); + CovariateDataset covariateOne = CovariateDatasetBuilder.builder() + .dataset( dataOne ) + .build(); Dataset dataTwo = DatasetBuilder.builder( this.defaultDataset ) .variable( two ) .build(); - CovariateDataset covariateTwo = new CovariateDataset( dataTwo, null, null, null, null ); + CovariateDataset covariateTwo = CovariateDatasetBuilder.builder() + .dataset( dataTwo ) + .build(); List covariates = List.of( covariateOne, covariateTwo ); EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() @@ -216,11 +223,10 @@ void testEvaluationWithoutTimeScaleAndCovariateWithRescaleFunctionProducesError( Dataset dataOne = DatasetBuilder.builder( this.defaultDataset ) .variable( one ) .build(); - CovariateDataset covariateOne = new CovariateDataset( dataOne, - null, - null, - null, - TimeScale.TimeScaleFunction.TOTAL ); + CovariateDataset covariateOne = CovariateDatasetBuilder.builder() + .dataset( dataOne ) + .rescaleFunction( TimeScale.TimeScaleFunction.TOTAL ) + .build(); List covariates = List.of( covariateOne ); EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() @@ -243,11 +249,15 @@ void testForecastDataTypeForCovariatesResultsInError() Dataset dataOne = DatasetBuilder.builder( this.defaultDataset ) .type( DataType.OBSERVATIONS ) .build(); - CovariateDataset covariateOne = new CovariateDataset( dataOne, null, null, null, null ); + CovariateDataset covariateOne = CovariateDatasetBuilder.builder() + .dataset( dataOne ) + .build(); Dataset dataTwo = DatasetBuilder.builder( this.defaultDataset ) .type( DataType.SINGLE_VALUED_FORECASTS ) .build(); - CovariateDataset covariateTwo = new CovariateDataset( dataTwo, null, null, null, null ); + CovariateDataset covariateTwo = CovariateDatasetBuilder.builder() + .dataset( dataTwo ) + .build(); List covariates = List.of( covariateOne, covariateTwo ); EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() @@ -268,7 +278,9 @@ void testEvaluationWithTimeScaleAndCovariateWithoutRescaleFunctionResultsInWarni Dataset dataOne = DatasetBuilder.builder( this.defaultDataset ) .type( DataType.OBSERVATIONS ) .build(); - CovariateDataset covariate = new CovariateDataset( dataOne, null, null, null, null ); + CovariateDataset covariate = CovariateDatasetBuilder.builder() + .dataset( dataOne ) + .build(); TimeScale timeScaleInner = TimeScale.newBuilder() .setPeriod( Duration.newBuilder() @@ -320,7 +332,10 @@ void testDatesAreDeclaredForWebServiceSourcesResultsInErrors() .dataset( baselineInner ) .build(); - CovariateDataset covariate = new CovariateDataset( left, null, null, null, null ); + CovariateDataset covariate = CovariateDatasetBuilder.builder() + .dataset( left ) + .build(); + List covariates = List.of( covariate ); EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() @@ -406,7 +421,9 @@ void testConflictingTimeZoneForSourceResultsInWarningsAndError() .timeZoneOffset( ZoneOffset.ofHours( -7 ) ) .build(); - CovariateDataset covariateDataset = new CovariateDataset( covariate, null, null, null, null ); + CovariateDataset covariateDataset = CovariateDatasetBuilder.builder() + .dataset( covariate ) + .build(); List covariates = List.of( covariateDataset ); @@ -466,7 +483,9 @@ void testConflictingUnitsForSourceResultsInWarningsAndError() .unit( "baz" ) .build(); - CovariateDataset covariateDataset = new CovariateDataset( covariate, null, null, null, null ); + CovariateDataset covariateDataset = CovariateDatasetBuilder.builder() + .dataset( covariate ) + .build(); List covariates = List.of( covariateDataset ); @@ -736,7 +755,10 @@ void testEvaluationTimeScaleIsConsistentWithDatasetTimeScalesResultsInWarningsAn .timeScale( timeScaleCovariate ) .build(); - CovariateDataset covariateDataset = new CovariateDataset( covariate, null, null, null, null ); + CovariateDataset covariateDataset = CovariateDatasetBuilder.builder() + .dataset( covariate ) + .build(); + List covariates = List.of( covariateDataset ); TimeScale timeScaleInner = TimeScale.newBuilder() @@ -789,8 +811,11 @@ void testEvaluationTimeScaleIsConsistentWithDatasetTimeScalesForCovariateWithRes .timeScale( timeScaleCovariate ) .build(); - CovariateDataset covariateDataset = - new CovariateDataset( covariate, null, null, null, TimeScale.TimeScaleFunction.TOTAL ); + CovariateDataset covariateDataset = CovariateDatasetBuilder.builder() + .dataset( covariate ) + .rescaleFunction( TimeScale.TimeScaleFunction.TOTAL ) + .build(); + List covariates = List.of( covariateDataset ); TimeScale timeScaleInner = TimeScale.newBuilder() @@ -1124,7 +1149,10 @@ void testCovariatesWithInconsistentFeatureAuthoritiesProducesError() .geometries( geometries ) .build(); - CovariateDataset covariateDataset = new CovariateDataset( covariate, null, null, null, null ); + CovariateDataset covariateDataset = CovariateDatasetBuilder.builder() + .dataset( covariate ) + .build(); + List covariates = List.of( covariateDataset ); EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() @@ -2557,7 +2585,10 @@ void testTwoCovariatesWithoutVariableNamesProducesError() .sources( List.of( source, anotherSource ) ) .type( DataType.OBSERVATIONS ) .build(); - CovariateDataset covariate = new CovariateDataset( dataset, null, null, null, null ); + CovariateDataset covariate = CovariateDatasetBuilder.builder() + .dataset( dataset ) + .build(); + List covariates = List.of( covariate, covariate ); EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() .covariates( covariates ) @@ -2577,15 +2608,22 @@ void testCovariatesWithInvalidFiltersProducesErrors() .type( DataType.OBSERVATIONS ) .variable( new Variable( "foo", null, Set.of() ) ) .build(); - CovariateDataset covariate = new CovariateDataset( dataset, 5.0, 1.0, null, null ); + + CovariateDataset covariate = CovariateDatasetBuilder.builder() + .dataset( dataset ) + .minimum( 5.0 ) + .maximum( 1.0 ) + .build(); Dataset anotherDataset = DatasetBuilder.builder() .type( DataType.OBSERVATIONS ) .build(); - CovariateDataset anotherCovariate = new CovariateDataset( anotherDataset, - 6.3, - 6.2999, - null, null ); + + CovariateDataset anotherCovariate = CovariateDatasetBuilder.builder() + .dataset( anotherDataset ) + .minimum( 6.3 ) + .maximum( 6.2999 ) + .build(); List covariates = List.of( covariate, anotherCovariate ); diff --git a/wres-io/test/wres/io/retrieving/database/SingleValuedRetrieverFactoryTest.java b/wres-io/test/wres/io/retrieving/database/SingleValuedRetrieverFactoryTest.java index ace7a35067..c6a016ef8b 100644 --- a/wres-io/test/wres/io/retrieving/database/SingleValuedRetrieverFactoryTest.java +++ b/wres-io/test/wres/io/retrieving/database/SingleValuedRetrieverFactoryTest.java @@ -35,6 +35,7 @@ import wres.config.yaml.components.BaselineDataset; import wres.config.yaml.components.BaselineDatasetBuilder; import wres.config.yaml.components.CovariateDataset; +import wres.config.yaml.components.CovariateDatasetBuilder; import wres.config.yaml.components.DataType; import wres.config.yaml.components.Dataset; import wres.config.yaml.components.DatasetBuilder; @@ -497,11 +498,11 @@ private void addTwoForecastTimeSeriesEachWithFiveEventsToTheDatabase() throws SQ .type( DataType.OBSERVATIONS ) .variable( new Variable( VARIABLE_NAME, null, Set.of() ) ) .build(); - CovariateDataset covariateDataset = new CovariateDataset( covariate, - null, - null, - DatasetOrientation.LEFT, - null ); + CovariateDataset covariateDataset = CovariateDatasetBuilder.builder() + .dataset( covariate ) + .featureNameOrientation( DatasetOrientation.LEFT ) + .build(); + BaselineDataset baseline = BaselineDatasetBuilder.builder() .dataset( right ) .build(); diff --git a/wres-io/test/wres/io/retrieving/memory/EnsembleRetrieverFactoryInMemoryTest.java b/wres-io/test/wres/io/retrieving/memory/EnsembleRetrieverFactoryInMemoryTest.java index 9e0e3ee46b..999ba688d8 100644 --- a/wres-io/test/wres/io/retrieving/memory/EnsembleRetrieverFactoryInMemoryTest.java +++ b/wres-io/test/wres/io/retrieving/memory/EnsembleRetrieverFactoryInMemoryTest.java @@ -16,6 +16,7 @@ import wres.config.yaml.components.BaselineDataset; import wres.config.yaml.components.BaselineDatasetBuilder; import wres.config.yaml.components.CovariateDataset; +import wres.config.yaml.components.CovariateDatasetBuilder; import wres.config.yaml.components.DataType; import wres.config.yaml.components.Dataset; import wres.config.yaml.components.DatasetBuilder; @@ -163,8 +164,12 @@ void runBeforeEachTest() .name( VARIABLE_NAME ) .build() ) .build(); - CovariateDataset covariateDataset = - new CovariateDataset( covariate, null, null, DatasetOrientation.LEFT, null ); + + CovariateDataset covariateDataset = CovariateDatasetBuilder.builder() + .dataset( covariate ) + .featureNameOrientation( DatasetOrientation.LEFT ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() .left( left ) diff --git a/wres-io/test/wres/io/retrieving/memory/SingleValuedRetrieverFactoryInMemoryTest.java b/wres-io/test/wres/io/retrieving/memory/SingleValuedRetrieverFactoryInMemoryTest.java index b971de6a13..b489f6e432 100644 --- a/wres-io/test/wres/io/retrieving/memory/SingleValuedRetrieverFactoryInMemoryTest.java +++ b/wres-io/test/wres/io/retrieving/memory/SingleValuedRetrieverFactoryInMemoryTest.java @@ -16,6 +16,7 @@ import wres.config.yaml.components.BaselineDataset; import wres.config.yaml.components.BaselineDatasetBuilder; import wres.config.yaml.components.CovariateDataset; +import wres.config.yaml.components.CovariateDatasetBuilder; import wres.config.yaml.components.DataType; import wres.config.yaml.components.Dataset; import wres.config.yaml.components.DatasetBuilder; @@ -161,8 +162,12 @@ void runBeforeEachTest() .name( VARIABLE_NAME ) .build() ) .build(); - CovariateDataset covariateDataset = - new CovariateDataset( covariate, null, null, DatasetOrientation.LEFT, null ); + + CovariateDataset covariateDataset = CovariateDatasetBuilder.builder() + .dataset( covariate ) + .featureNameOrientation( DatasetOrientation.LEFT ) + .build(); + EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() .left( left )