diff --git a/casdk-docs/docs/architecture/decisions/0017-watt-time-signal-type.md b/casdk-docs/docs/architecture/decisions/0017-watt-time-signal-type.md new file mode 100644 index 000000000..920d22bbd --- /dev/null +++ b/casdk-docs/docs/architecture/decisions/0017-watt-time-signal-type.md @@ -0,0 +1,50 @@ +# 0017. Signal Type in WattTime v3 Data Source + +## Status + +Proposed + +## Context + +WattTime v3 API has been supported since [Carbon Aware SDK v1.5.0](https://carbon-aware-sdk.greensoftware.foundation/blog/release-v1.5). As we mentioned in [ADR-0015](https://carbon-aware-sdk.greensoftware.foundation/docs/architecture/decisions/watt-time-v3), `signal_type` has been added in each endpoints which the SDK will access since v3 API. We should be able to set following parameters to it, but it can't in Carbo Aware SDK. + +https://watttime.org/data-science/data-signals/ + +| Signal Type | Description | +|---|---| +| `co2_moer` | Marginal Operating Emissions Rate of carbon dioxide. | +| `co2_aoer` | Average Operating Emissions Rate of carbon dioxide. | + +According to [Green Software Practitioners](https://learn.greensoftware.foundation/carbon-awareness#marginal-carbon-intensity), "marginal" means the carbon intensity of the power plant that would have to be employed to meet any new demand. On the other hand, "average" means the average of all of power plants. It should be chosen by Carbon Aware SDK user because which value is needed depends on the user. + +`co2_moer` is hard-coded until Carbon Aware SDK v1.7.0 (at least). + +## Decision + +The proposal is for adding a new parameter for Signal Type in WattTime Data Source. + +## Update Changes + +We will introduce new parameter for data source configuration of WattTime as following. + +### appsettings.json + +```json +"WattTime": { + "Type": "WattTime", + "Username": "username", + "Password": "password", + "BaseURL": "https://api.watttime.org/v3/", + "SignalType": "co2_aoer" +} +``` + +### environment variable + +```bash +DataSources__Configurations__WattTime__SignalType=co2_aoer +``` + +## Green Impact + +Neutral diff --git a/casdk-docs/docs/tutorial-extras/configuration.md b/casdk-docs/docs/tutorial-extras/configuration.md index 416dce2ed..61bfe2c92 100644 --- a/casdk-docs/docs/tutorial-extras/configuration.md +++ b/casdk-docs/docs/tutorial-extras/configuration.md @@ -9,6 +9,7 @@ - [baseUrl](#baseurl) - [Proxy](#proxy) - [WattTime Caching BalancingAuthority](#watttime-caching-balancingauthority) + - [SignalType](#signaltype) - [Json Configuration](#json-configuration) - [ElectricityMaps Configuration](#electricitymaps-configuration) - [API Token Header](#api-token-header) @@ -111,7 +112,8 @@ data provider must also be supplied. "url": "http://10.10.10.1", "username": "proxyUsername", "password": "proxyPassword" - } + }, + "SignalType": "co2_aoer" }, "ElectricityMaps": { "Type": "ElectricityMaps", @@ -187,6 +189,20 @@ recommends not caching for longer than 1 month. DataSources__Configurations__WattTime__BalancingAuthorityCacheTTL="90" ``` +#### SignalType + +WattTime supports 2 signal type. They can be set as a parameter. + +* `co2_moer`: Marginal operating emissions rate +* `co2_aoer`: Average operating emissions rate + +If values other than these are set, an error occurs. +See [WattTime documentation](https://watttime.org/data-science/data-signals/) for details. + +```bash +DataSources__Configurations__WattTime__SignalType=co2_aoer +``` + ### Json Configuration By setting diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Client/WattTimeClient.cs b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Client/WattTimeClient.cs index 3dba1c647..5f4abdf8a 100644 --- a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Client/WattTimeClient.cs +++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Client/WattTimeClient.cs @@ -64,7 +64,7 @@ public async Task GetDataAsync(string regionAbbreviat { QueryStrings.Region, regionAbbreviation }, { QueryStrings.StartTime, startTime.ToUniversalTime().ToString("O", CultureInfo.InvariantCulture) }, { QueryStrings.EndTime, endTime.ToUniversalTime().ToString("O", CultureInfo.InvariantCulture) }, - { QueryStrings.SignalType, SignalTypes.co2_moer}, + { QueryStrings.SignalType, _configuration.SignalType.ToString()}, }; var tags = new Dictionary() @@ -93,7 +93,7 @@ public async Task GetCurrentForecastAsync(string var parameters = new Dictionary() { { QueryStrings.Region, region }, - { QueryStrings.SignalType, SignalTypes.co2_moer } + { QueryStrings.SignalType, _configuration.SignalType.ToString() } }; var tags = new Dictionary() @@ -124,7 +124,7 @@ public Task GetCurrentForecastAsync(RegionRespons { QueryStrings.Region, region }, { QueryStrings.StartTime, requestedAt.ToUniversalTime().ToString("O", CultureInfo.InvariantCulture) }, { QueryStrings.EndTime, requestedAt.ToUniversalTime().ToString("O", CultureInfo.InvariantCulture) }, - { QueryStrings.SignalType, SignalTypes.co2_moer } + { QueryStrings.SignalType, _configuration.SignalType.ToString() } }; var tags = new Dictionary() @@ -302,14 +302,14 @@ private async Task GetRegionFromCacheAsync(string latitude, stri { { QueryStrings.Latitude, latitude }, { QueryStrings.Longitude, longitude }, - { QueryStrings.SignalType, SignalTypes.co2_moer} + { QueryStrings.SignalType, _configuration.SignalType.ToString()} }; var tags = new Dictionary() { { QueryStrings.Latitude, latitude }, { QueryStrings.Longitude, longitude }, - { QueryStrings.SignalType, SignalTypes.co2_moer } + { QueryStrings.SignalType, _configuration.SignalType.ToString() } }; var result = await this.MakeRequestGetStreamAsync(Paths.RegionFromLocation, parameters, tags); var regionResponse = await JsonSerializer.DeserializeAsync(result, _options) ?? throw new WattTimeClientException($"Error getting Region for latitude {latitude} and longitude {longitude}"); diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Configuration/ServiceCollectionExtensions.cs b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Configuration/ServiceCollectionExtensions.cs index d05f1a33a..39c025b5d 100644 --- a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Configuration/ServiceCollectionExtensions.cs +++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Configuration/ServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ using CarbonAware.Configuration; using CarbonAware.DataSources.WattTime.Client; +using CarbonAware.DataSources.WattTime.Constants; using CarbonAware.Exceptions; using CarbonAware.Interfaces; using Microsoft.Extensions.Configuration; @@ -24,7 +25,7 @@ public static IServiceCollection AddWattTimeEmissionsDataSource(this IServiceCol services.TryAddSingleton(); return services; } - + private static void AddDependencies(IServiceCollection services, IConfigurationSection configSection) { AddWattTimeClient(services, configSection); @@ -33,10 +34,10 @@ private static void AddDependencies(IServiceCollection services, IConfigurationS private static void AddWattTimeClient(IServiceCollection services, IConfigurationSection configSection) { - services.Configure(c => - { - configSection.Bind(c); - }); + services.AddOptions() + .Bind(configSection) + .Validate(config => Enum.IsDefined(typeof(SignalTypes), config.SignalType), "Invalid SignalType") + .ValidateOnStart(); var httpClientBuilder = services.AddHttpClient(IWattTimeClient.NamedClient); var authenticationClientBuilder = services.AddHttpClient(IWattTimeClient.NamedAuthenticationClient); diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Configuration/WattTimeClientConfiguration.cs b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Configuration/WattTimeClientConfiguration.cs index a586523ee..5cc063db0 100644 --- a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Configuration/WattTimeClientConfiguration.cs +++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Configuration/WattTimeClientConfiguration.cs @@ -1,3 +1,4 @@ +using CarbonAware.DataSources.WattTime.Constants; using CarbonAware.Exceptions; using System.Text; @@ -25,6 +26,11 @@ internal class WattTimeClientConfiguration /// public string BaseUrl { get; set; } = "https://api.watttime.org/v3/"; + /// + /// Gets or sets the signal type to use: co2_moer or co2_aoer + /// + public SignalTypes SignalType { get; set; } = SignalTypes.co2_moer; + /// /// Authentication base url. This changed between v2 and v3 /// to be different to the API base url. diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Constants/SignalTypes.cs b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Constants/SignalTypes.cs index 3ff905da5..e7f3a7510 100644 --- a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Constants/SignalTypes.cs +++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Constants/SignalTypes.cs @@ -1,6 +1,7 @@ namespace CarbonAware.DataSources.WattTime.Constants; -internal class SignalTypes +public enum SignalTypes { - public const string co2_moer = "co2_moer"; + co2_moer, + co2_aoer } diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Model/GridEmissionsMetaData.cs b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Model/GridEmissionsMetaData.cs index d5abb4117..6743516b0 100644 --- a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Model/GridEmissionsMetaData.cs +++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Model/GridEmissionsMetaData.cs @@ -1,4 +1,5 @@ -using System.Text.Json.Serialization; +using CarbonAware.DataSources.WattTime.Constants; +using System.Text.Json.Serialization; namespace CarbonAware.DataSources.WattTime.Model; @@ -18,7 +19,7 @@ internal record GridEmissionsMetaData /// Signal Type. eg MOER /// [JsonPropertyName("signal_type")] - public string? SignalType { get; set; } + public SignalTypes? SignalType { get; set; } [JsonPropertyName("model")] public GridEmissionsModelData? Model { get; set; } diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Model/RegionResponse.cs b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Model/RegionResponse.cs index ee334c67e..c36be2f38 100644 --- a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Model/RegionResponse.cs +++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/Model/RegionResponse.cs @@ -1,4 +1,5 @@ -using System.Text.Json.Serialization; +using CarbonAware.DataSources.WattTime.Constants; +using System.Text.Json.Serialization; namespace CarbonAware.DataSources.WattTime.Model; @@ -18,7 +19,7 @@ internal record RegionResponse /// Signal Type /// [JsonPropertyName("signal_type")] - public string SignalType { get; set; } = string.Empty; + public SignalTypes SignalType { get; set; } /// /// Human readable name/description for the region. diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/test/Client/WattTimeTestData.cs b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/test/Client/WattTimeTestData.cs index d46143e4f..411adb510 100644 --- a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/test/Client/WattTimeTestData.cs +++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/test/Client/WattTimeTestData.cs @@ -18,7 +18,7 @@ public class Constants public static DateTime Date = new DateTime(2099, 1, 1, 0, 0, 0); public const float Value = 999.99f; public const string Version = "1.0"; - public const string SignalType = SignalTypes.co2_moer; + public const SignalTypes SignalType = SignalTypes.co2_moer; public const int Frequency = 300; } @@ -46,11 +46,11 @@ private static GridEmissionsMetaData _GetGridDataMetaResponse() Model = new GridEmissionsModelData() { Date = Constants.Date, - Type = SignalTypes.co2_moer + Type = "binned_regression" // from a response example of WattTime API: https://docs.watttime.org/#tag/GET-Historical/operation/get_historical_datapoint_v3_historical_get }, DataPointPeriodSeconds = 30, SignalType = SignalTypes.co2_moer, - Units = "co2_moer" + Units = "lbs_co2_per_mwh" // from a response example of WattTime API: https://docs.watttime.org/#tag/GET-Historical/operation/get_historical_datapoint_v3_historical_get }; return gridEmissionsMetaData;