diff --git a/docs/apis/json/atn-params-brickstorageheater.json b/docs/apis/json/atn-params-brickstorageheater.json index 31e1c2d..2dd582f 100644 --- a/docs/apis/json/atn-params-brickstorageheater.json +++ b/docs/apis/json/atn-params-brickstorageheater.json @@ -25,13 +25,19 @@ }, { "const": "2127aba6", - "title": "VersantStorageHeatTariff", - "description": "" + "title": "VersantA1StorageHeatTariff", + "url": "https://github.com/thegridelectric/gridworks-ps/blob/dev/input_data/electricity_prices/isone/distp__w.isone.stetson__2022__gw.me.versant.a1.res.ets.csv", + "description": "Versant is a utility serving customers in Maine, and in particular serves much of the area behind the Keene Rd Constraint in the [GridWorks Millinocket Demo](https://gridworks.readthedocs.io/en/latest/millinocket-demo.html#background). Alternately known as the 'Home Eco Rate With Bonus Meter, Time-of-Use.' Look for rate A1 in Versant [rate schedules](https://www.versantpower.com/residential/rates/rates-schedules/); details are also available [here](https://drive.google.com/drive/u/0/folders/1mhIeNj2JWVyIJrQnSHmBDOkBpNnRRVKB). More: Service under this rate will be available to residential customers with thermal energy storage devices, electric battery storage devices, and/or vehicle chargers who agree to install a second metered point of delivery. The customer will be subject to inspections to ensure that the thermal storage device, electric battery storage device, and electric vehicle charger(s) are sized appropriately for residential use. If the thermal storage device, electric battery storage device, and electric vehicle charger(s) do not pass Company inspection, then the service will be denied. Service will be single-phase, alternating current, 60 hertz, at one standard secondary distribution voltage. Customers taking service under this rate schedule are responsible for paying both Distribution Service and Stranded Cost." }, { "const": "ea5c675a", "title": "VersantATariff", - "description": "" + "description": "Versant is a utility serving customers in Maine, and in particular serves much of the area behind the Keene Rd Constraint in the [GridWorks Millinocket Demo](https://gridworks.readthedocs.io/en/latest/millinocket-demo.html#background). The A Tariff is their standard residential tariff. Look for rate A in Versant [rate schedules](https://www.versantpower.com/residential/rates/rates-schedules/)" + }, + { + "const": "54aec3a7", + "title": "VersantA20HeatTariff", + "description": "Versant is a utility serving customers in Maine, and in particular serves much of the area behind the Keene Rd Constraint in the [GridWorks Millinocket Demo](https://gridworks.readthedocs.io/en/latest/millinocket-demo.html#background). This is an alternative tariff available for electric heat." } ] }, diff --git a/docs/apis/json/atn-params-simpleresistivehydronic.json b/docs/apis/json/atn-params-simpleresistivehydronic.json new file mode 100644 index 0000000..416e516 --- /dev/null +++ b/docs/apis/json/atn-params-simpleresistivehydronic.json @@ -0,0 +1,210 @@ +{ + "gwapi": "001", + "type_name": "atn.params.simpleresistivehydronic", + "version": "000", + "owner": "gridworks@gridworks-consulting.com", + "description": "", + "formats": { + "LeftRightDot": { + "type": "string", + "description": "Lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character.", + "example": "dw1.isone.me.freedom.apple" + } + }, + "enums": { + "DistributionTariff000": { + "type": "string", + "name": "distribution.tariff.000", + "description": "Name of distribution tariff of local network company/utility", + "oneOf": [ + { + "const": "00000000", + "title": "Unknown", + "description": "" + }, + { + "const": "2127aba6", + "title": "VersantA1StorageHeatTariff", + "url": "https://github.com/thegridelectric/gridworks-ps/blob/dev/input_data/electricity_prices/isone/distp__w.isone.stetson__2022__gw.me.versant.a1.res.ets.csv", + "description": "Versant is a utility serving customers in Maine, and in particular serves much of the area behind the Keene Rd Constraint in the [GridWorks Millinocket Demo](https://gridworks.readthedocs.io/en/latest/millinocket-demo.html#background). Alternately known as the 'Home Eco Rate With Bonus Meter, Time-of-Use.' Look for rate A1 in Versant [rate schedules](https://www.versantpower.com/residential/rates/rates-schedules/); details are also available [here](https://drive.google.com/drive/u/0/folders/1mhIeNj2JWVyIJrQnSHmBDOkBpNnRRVKB). More: Service under this rate will be available to residential customers with thermal energy storage devices, electric battery storage devices, and/or vehicle chargers who agree to install a second metered point of delivery. The customer will be subject to inspections to ensure that the thermal storage device, electric battery storage device, and electric vehicle charger(s) are sized appropriately for residential use. If the thermal storage device, electric battery storage device, and electric vehicle charger(s) do not pass Company inspection, then the service will be denied. Service will be single-phase, alternating current, 60 hertz, at one standard secondary distribution voltage. Customers taking service under this rate schedule are responsible for paying both Distribution Service and Stranded Cost." + }, + { + "const": "ea5c675a", + "title": "VersantATariff", + "description": "Versant is a utility serving customers in Maine, and in particular serves much of the area behind the Keene Rd Constraint in the [GridWorks Millinocket Demo](https://gridworks.readthedocs.io/en/latest/millinocket-demo.html#background). The A Tariff is their standard residential tariff. Look for rate A in Versant [rate schedules](https://www.versantpower.com/residential/rates/rates-schedules/)" + }, + { + "const": "54aec3a7", + "title": "VersantA20HeatTariff", + "description": "Versant is a utility serving customers in Maine, and in particular serves much of the area behind the Keene Rd Constraint in the [GridWorks Millinocket Demo](https://gridworks.readthedocs.io/en/latest/millinocket-demo.html#background). This is an alternative tariff available for electric heat." + } + ] + }, + "RecognizedCurrencyUnit000": { + "type": "string", + "name": "recognized.currency.unit.000", + "description": "Unit of currency", + "oneOf": [ + { + "const": "00000000", + "title": "Unknown", + "description": "" + }, + { + "const": "e57c5143", + "title": "USD", + "description": "US Dollar" + }, + { + "const": "f7b38fc5", + "title": "GBP", + "description": "Pounds sterling" + } + ] + }, + "EnergySupplyType000": { + "type": "string", + "name": "energy.supply.type.000", + "description": "", + "oneOf": [ + { + "const": "00000000", + "title": "Unknown", + "description": "" + }, + { + "const": "cb18f937", + "title": "StandardOffer", + "description": "" + }, + { + "const": "e9dc99a6", + "title": "RealtimeLocalLmp", + "description": "" + } + ] + } + }, + "properties": { + "GNodeAlias": { + "type": "string", + "format": "LeftRightDot", + "title": "", + "required": true + }, + "HomeCity": { + "type": "string", + "title": "", + "required": true + }, + "TimezoneString": { + "type": "string", + "title": "", + "required": true + }, + "StorageSteps": { + "type": "integer", + "title": "", + "required": true + }, + "FloSlices": { + "type": "integer", + "title": "", + "required": true + }, + "SliceDurationMinutes": { + "type": "integer", + "title": "", + "required": true + }, + "CurrencyUnit": { + "type": "string", + "format": "RecognizedCurrencyUnit000", + "title": "", + "required": true + }, + "Tariff": { + "type": "string", + "format": "DistributionTariff000", + "title": "", + "required": true + }, + "EnergyType": { + "type": "string", + "format": "EnergySupplyType000", + "title": "", + "required": true + }, + "StandardOfferPriceDollarsPerMwh": { + "type": "integer", + "title": "", + "required": true + }, + "DistributionTariffDollarsPerMwh": { + "type": "integer", + "title": "", + "required": true + }, + "StoreSizeGallons": { + "type": "integer", + "title": "", + "required": true + }, + "MaxStoreTempF": { + "type": "integer", + "title": "", + "required": true + }, + "ElementMaxPowerKw": { + "type": "number", + "title": "", + "required": true + }, + "RequiredSourceWaterTempF": { + "type": "integer", + "title": "", + "required": true + }, + "FixedPumpGpm": { + "type": "number", + "title": "", + "required": true + }, + "ReturnWaterFixedDeltaT": { + "type": "integer", + "title": "", + "required": true + }, + "AnnualHvacKwhTh": { + "type": "integer", + "title": "", + "required": true + }, + "AmbientPowerInKw": { + "type": "number", + "title": "", + "required": true + }, + "HouseWorstCaseTempF": { + "type": "integer", + "title": "", + "required": true + }, + "RoomTempF": { + "type": "integer", + "title": "", + "required": true + }, + "TypeName": { + "type": "string", + "value": "atn.params.simpleresistivehydronic.000", + "title": "The type name" + }, + "Version": { + "type": "string", + "title": "The type version", + "default": "000", + "required": true + } + } +} diff --git a/docs/apis/json/flo-params-brickstorageheater.json b/docs/apis/json/flo-params-brickstorageheater.json index c159c83..1787b3e 100644 --- a/docs/apis/json/flo-params-brickstorageheater.json +++ b/docs/apis/json/flo-params-brickstorageheater.json @@ -129,7 +129,7 @@ "C": { "type": "number", "title": "", - "required": false + "required": true }, "RealtimeElectricityPrice": { "type": "number", @@ -167,7 +167,7 @@ "type": "string", "format": "UuidCanonicalTextual", "title": "", - "required": false + "required": true }, "RegPriceUid": { "type": "string", diff --git a/docs/apis/json/flo-params-simpleresistivehydronic.json b/docs/apis/json/flo-params-simpleresistivehydronic.json new file mode 100644 index 0000000..effd61d --- /dev/null +++ b/docs/apis/json/flo-params-simpleresistivehydronic.json @@ -0,0 +1,182 @@ +{ + "gwapi": "001", + "type_name": "flo.params.simpleresistivehydronic", + "version": "000", + "owner": "gridworks@gridworks-consulting.com", + "description": "", + "formats": { + "UuidCanonicalTextual": { + "type": "string", + "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", + "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" + }, + "LeftRightDot": { + "type": "string", + "description": "Lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character.", + "example": "dw1.isone.me.freedom.apple" + } + }, + "enums": { + "RecognizedCurrencyUnit000": { + "type": "string", + "name": "recognized.currency.unit.000", + "description": "Unit of currency", + "oneOf": [ + { + "const": "00000000", + "title": "Unknown", + "description": "" + }, + { + "const": "e57c5143", + "title": "USD", + "description": "US Dollar" + }, + { + "const": "f7b38fc5", + "title": "GBP", + "description": "Pounds sterling" + } + ] + } + }, + "properties": { + "GNodeAlias": { + "type": "string", + "format": "LeftRightDot", + "title": "", + "required": true + }, + "FloParamsUid": { + "type": "string", + "format": "UuidCanonicalTextual", + "title": "", + "required": true + }, + "HomeCity": { + "type": "string", + "title": "", + "required": true + }, + "TimezoneString": { + "type": "string", + "title": "", + "required": true + }, + "StartYearUtc": { + "type": "integer", + "title": "", + "required": true + }, + "StartMonthUtc": { + "type": "integer", + "title": "", + "required": true + }, + "StartDayUtc": { + "type": "integer", + "title": "", + "required": true + }, + "StartHourUtc": { + "type": "integer", + "title": "", + "required": true + }, + "StartMinuteUtc": { + "type": "integer", + "title": "", + "required": true + }, + "StoreSizeGallons": { + "type": "integer", + "title": "", + "required": true + }, + "MaxStoreTempF": { + "type": "integer", + "title": "", + "required": true + }, + "ElementMaxPowerKw": { + "type": "number", + "title": "", + "required": true + }, + "RequiredSourceWaterTempF": { + "type": "integer", + "title": "", + "required": true + }, + "FixedPumpGpm": { + "type": "number", + "title": "", + "required": true + }, + "ReturnWaterFixedDeltaT": { + "type": "integer", + "title": "", + "required": true + }, + "SliceDurationMinutes": { + "type": "integer", + "title": "", + "required": true + }, + "PowerLostFromHouseKwList": { + "type": "number", + "title": "", + "required": true + }, + "OutsideTempF": { + "type": "number", + "title": "", + "required": true + }, + "DistributionPrice": { + "type": "number", + "title": "", + "required": true + }, + "RealtimeElectricityPrice": { + "type": "number", + "title": "", + "required": true + }, + "RtElecPriceUid": { + "type": "string", + "format": "UuidCanonicalTextual", + "title": "", + "required": true + }, + "WeatherUid": { + "type": "string", + "format": "UuidCanonicalTextual", + "title": "", + "required": true + }, + "DistPriceUid": { + "type": "string", + "format": "UuidCanonicalTextual", + "title": "", + "required": true + }, + "CurrencyUnit": { + "type": "string", + "format": "RecognizedCurrencyUnit000", + "title": "", + "required": true + }, + "TypeName": { + "type": "string", + "value": "flo.params.simpleresistivehydronic.000", + "title": "The type name" + }, + "Version": { + "type": "string", + "title": "The type version", + "default": "000", + "required": true + } + } +} diff --git a/docs/apis/types.rst b/docs/apis/types.rst index e51f58d..688956f 100644 --- a/docs/apis/types.rst +++ b/docs/apis/types.rst @@ -22,6 +22,10 @@ AtnParamsReport ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. literalinclude:: json/atn-params-report.json +AtnParamsSimpleresistivehydronic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. literalinclude:: json/atn-params-simpleresistivehydronic.json + BasegnodeScadaCreate ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. literalinclude:: json/basegnode-scada-create.json @@ -46,6 +50,10 @@ FloParamsReport ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. literalinclude:: json/flo-params-report.json +FloParamsSimpleresistivehydronic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. literalinclude:: json/flo-params-simpleresistivehydronic.json + InitialTadeedAlgoCreate ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. literalinclude:: json/initial-tadeed-algo-create.json diff --git a/docs/sdk-types.rst b/docs/sdk-types.rst index 804894e..a90d86c 100644 --- a/docs/sdk-types.rst +++ b/docs/sdk-types.rst @@ -23,12 +23,14 @@ forth between type instances and Python objects. AtnParams AtnParamsBrickstorageheater AtnParamsReport + AtnParamsSimpleresistivehydronic BasegnodeScadaCreate DiscoverycertAlgoCreate DispatchContractConfirmed FloParams FloParamsBrickstorageheater FloParamsReport + FloParamsSimpleresistivehydronic InitialTadeedAlgoCreate InitialTadeedAlgoOptin InitialTadeedAlgoTransfer diff --git a/docs/types/atn-params-simpleresistivehydronic.rst b/docs/types/atn-params-simpleresistivehydronic.rst new file mode 100644 index 0000000..1bc6cf8 --- /dev/null +++ b/docs/types/atn-params-simpleresistivehydronic.rst @@ -0,0 +1,77 @@ +AtnParamsSimpleresistivehydronic +========================== +Python pydantic class corresponding to json type ```atn.params.simpleresistivehydronic```. + +.. autoclass:: gwatn.types.AtnParamsSimpleresistivehydronic + :members: + +**GNodeAlias**: + - Description: + - Format: LeftRightDot + +**HomeCity**: + - Description: + +**TimezoneString**: + - Description: + +**StorageSteps**: + - Description: + +**FloSlices**: + - Description: + +**SliceDurationMinutes**: + - Description: + +**CurrencyUnit**: + - Description: + +**Tariff**: + - Description: + +**EnergyType**: + - Description: + +**StandardOfferPriceDollarsPerMwh**: + - Description: + +**DistributionTariffDollarsPerMwh**: + - Description: + +**StoreSizeGallons**: + - Description: + +**MaxStoreTempF**: + - Description: + +**ElementMaxPowerKw**: + - Description: + +**RequiredSourceWaterTempF**: + - Description: + +**FixedPumpGpm**: + - Description: + +**ReturnWaterFixedDeltaT**: + - Description: + +**AnnualHvacKwhTh**: + - Description: + +**AmbientPowerInKw**: + - Description: + +**HouseWorstCaseTempF**: + - Description: + +**RoomTempF**: + - Description: + +.. autoclass:: gwatn.types.atn_params_simpleresistivehydronic.check_is_left_right_dot + :members: + + +.. autoclass:: gwatn.types.AtnParamsSimpleresistivehydronic_Maker + :members: diff --git a/docs/types/flo-params-simpleresistivehydronic.rst b/docs/types/flo-params-simpleresistivehydronic.rst new file mode 100644 index 0000000..2d755df --- /dev/null +++ b/docs/types/flo-params-simpleresistivehydronic.rst @@ -0,0 +1,94 @@ +FloParamsSimpleresistivehydronic +========================== +Python pydantic class corresponding to json type ```flo.params.simpleresistivehydronic```. + +.. autoclass:: gwatn.types.FloParamsSimpleresistivehydronic + :members: + +**GNodeAlias**: + - Description: + - Format: LeftRightDot + +**FloParamsUid**: + - Description: + - Format: UuidCanonicalTextual + +**HomeCity**: + - Description: + +**TimezoneString**: + - Description: + +**StartYearUtc**: + - Description: + +**StartMonthUtc**: + - Description: + +**StartDayUtc**: + - Description: + +**StartHourUtc**: + - Description: + +**StartMinuteUtc**: + - Description: + +**StoreSizeGallons**: + - Description: + +**MaxStoreTempF**: + - Description: + +**ElementMaxPowerKw**: + - Description: + +**RequiredSourceWaterTempF**: + - Description: + +**FixedPumpGpm**: + - Description: + +**ReturnWaterFixedDeltaT**: + - Description: + +**SliceDurationMinutes**: + - Description: + +**PowerLostFromHouseKwList**: + - Description: + +**OutsideTempF**: + - Description: + +**DistributionPrice**: + - Description: + +**RealtimeElectricityPrice**: + - Description: + +**RtElecPriceUid**: + - Description: + - Format: UuidCanonicalTextual + +**WeatherUid**: + - Description: + - Format: UuidCanonicalTextual + +**DistPriceUid**: + - Description: + - Format: UuidCanonicalTextual + +**CurrencyUnit**: + - Description: + +.. autoclass:: gwatn.types.flo_params_simpleresistivehydronic.check_is_uuid_canonical_textual + :members: + + +.. autoclass:: gwatn.types.flo_params_simpleresistivehydronic.check_is_left_right_dot + :members: + + +.. autoclass:: gwatn.types.FloParamsSimpleresistivehydronic_Maker + :members: diff --git a/pyproject.toml b/pyproject.toml index 9fa9a4f..a366e10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "gridworks-atn" -version = "0.3.7" +version = "0.3.8" description = "Gridworks Atn Spaceheat" authors = ["GridWorks "] license = "None" diff --git a/src/gwatn/api_types.py b/src/gwatn/api_types.py index 8cb0c83..5fc25d1 100644 --- a/src/gwatn/api_types.py +++ b/src/gwatn/api_types.py @@ -8,6 +8,7 @@ from gwatn.types import AtnParams_Maker from gwatn.types import AtnParamsBrickstorageheater_Maker from gwatn.types import AtnParamsReport_Maker +from gwatn.types import AtnParamsSimpleresistivehydronic_Maker from gwatn.types import BaseGNodeGt_Maker from gwatn.types import BasegnodeScadaCreate_Maker from gwatn.types import ComponentAttributeClassGt_Maker @@ -22,6 +23,7 @@ from gwatn.types import FloParams_Maker from gwatn.types import FloParamsBrickstorageheater_Maker from gwatn.types import FloParamsReport_Maker +from gwatn.types import FloParamsSimpleresistivehydronic_Maker from gwatn.types import GNodeGt_Maker from gwatn.types import GNodeInstanceGt_Maker from gwatn.types import GtDispatchBoolean_Maker @@ -91,6 +93,7 @@ def type_makers() -> List[HeartbeatA_Maker]: AtnParams_Maker, AtnParamsBrickstorageheater_Maker, AtnParamsReport_Maker, + AtnParamsSimpleresistivehydronic_Maker, BaseGNodeGt_Maker, BasegnodeScadaCreate_Maker, ComponentAttributeClassGt_Maker, @@ -105,6 +108,7 @@ def type_makers() -> List[HeartbeatA_Maker]: FloParams_Maker, FloParamsBrickstorageheater_Maker, FloParamsReport_Maker, + FloParamsSimpleresistivehydronic_Maker, GNodeGt_Maker, GNodeInstanceGt_Maker, GtDispatchBoolean_Maker, @@ -180,6 +184,7 @@ def version_by_type_name() -> Dict[str, str]: "atn.params": "000", "atn.params.brickstorageheater": "000", "atn.params.report": "000", + "atn.params.simpleresistivehydronic": "000", "base.g.node.gt": "002", "basegnode.scada.create": "000", "component.attribute.class.gt": "000", @@ -194,6 +199,7 @@ def version_by_type_name() -> Dict[str, str]: "flo.params": "000", "flo.params.brickstorageheater": "000", "flo.params.report": "000", + "flo.params.simpleresistivehydronic": "000", "g.node.gt": "002", "g.node.instance.gt": "000", "gt.dispatch.boolean": "110", @@ -262,11 +268,12 @@ def status_by_versioned_type_name() -> Dict[str, str]: """ v: Dict[str, str] = { - "accepted.bid.000": "Pending", - "atn.bid.001": "Pending", + "accepted.bid.000": "Active", + "atn.bid.001": "Active", "atn.params.000": "Active", - "atn.params.brickstorageheater.000": "Pending", + "atn.params.brickstorageheater.000": "Active", "atn.params.report.000": "Active", + "atn.params.simpleresistivehydronic.000": "Pending", "base.g.node.gt.002": "Active", "basegnode.scada.create.000": "Active", "component.attribute.class.gt.000": "Active", @@ -281,6 +288,7 @@ def status_by_versioned_type_name() -> Dict[str, str]: "flo.params.000": "Active", "flo.params.brickstorageheater.000": "Active", "flo.params.report.000": "Active", + "flo.params.simpleresistivehydronic.000": "Pending", "g.node.gt.002": "Active", "g.node.instance.gt.000": "Active", "gt.dispatch.boolean.110": "Active", @@ -300,9 +308,9 @@ def status_by_versioned_type_name() -> Dict[str, str]: "initial.tadeed.algo.optin.002": "Active", "initial.tadeed.algo.transfer.000": "Active", "join.dispatch.contract.000": "Active", - "latest.price.000": "Pending", - "market.slot.000": "Pending", - "market.type.gt.000": "Pending", + "latest.price.000": "Active", + "market.slot.000": "Active", + "market.type.gt.000": "Active", "multipurpose.sensor.cac.gt.000": "Active", "multipurpose.sensor.component.gt.000": "Active", "new.tadeed.algo.optin.000": "Active", @@ -311,32 +319,32 @@ def status_by_versioned_type_name() -> Dict[str, str]: "pipe.flow.sensor.cac.gt.000": "Active", "pipe.flow.sensor.component.gt.000": "Active", "power.watts.000": "Active", - "price.quantity.000": "Pending", - "price.quantity.unitless.000": "Pending", + "price.quantity.000": "Active", + "price.quantity.unitless.000": "Active", "ready.001": "Active", "relay.cac.gt.000": "Active", "relay.component.gt.000": "Active", "resistive.heater.cac.gt.000": "Active", "resistive.heater.component.gt.000": "Active", - "scada.cert.transfer.000": "Pending", + "scada.cert.transfer.000": "Active", "sim.timestep.000": "Active", "simple.temp.sensor.cac.gt.000": "Active", "simple.temp.sensor.component.gt.000": "Active", - "simplesim.driver.data.000": "Pending", - "simplesim.driver.data.bsh.000": "Pending", - "simplesim.driver.report.000": "Pending", - "simplesim.snapshot.brickstorageheater.000": "Pending", - "sla.enter.000": "Pending", + "simplesim.driver.data.000": "Active", + "simplesim.driver.data.bsh.000": "Active", + "simplesim.driver.report.000": "Active", + "simplesim.snapshot.brickstorageheater.000": "Active", + "sla.enter.000": "Active", "snapshot.spaceheat.000": "Active", "spaceheat.node.gt.100": "Active", "super.starter.000": "Active", "supervisor.container.gt.000": "Active", - "tadeed.specs.hack.000": "Pending", + "tadeed.specs.hack.000": "Active", "tavalidatorcert.algo.create.000": "Active", "tavalidatorcert.algo.transfer.000": "Active", "telemetry.reporting.config.000": "Active", "telemetry.snapshot.spaceheat.000": "Active", - "terminalasset.certify.hack.000": "Pending", + "terminalasset.certify.hack.000": "Active", } return v diff --git a/src/gwatn/enums/distribution_tariff.py b/src/gwatn/enums/distribution_tariff.py index 83d7f2d..9046fb6 100644 --- a/src/gwatn/enums/distribution_tariff.py +++ b/src/gwatn/enums/distribution_tariff.py @@ -11,13 +11,15 @@ class DistributionTariff(StrEnum): Choices and descriptions: * Unknown: - * VersantStorageHeatTariff: - * VersantATariff: + * VersantA1StorageHeatTariff: Versant is a utility serving customers in Maine, and in particular serves much of the area behind the Keene Rd Constraint in the [GridWorks Millinocket Demo](https://gridworks.readthedocs.io/en/latest/millinocket-demo.html#background). Alternately known as the "Home Eco Rate With Bonus Meter, Time-of-Use". Look for rate A1 in Versant [rate schedules](https://www.versantpower.com/residential/rates/rates-schedules/); details are also available [here](https://drive.google.com/drive/u/0/folders/1mhIeNj2JWVyIJrQnSHmBDOkBpNnRRVKB). More: Service under this rate will be available to residential customers with thermal energy storage devices, electric battery storage devices, and/or vehicle chargers who agree to install a second metered point of delivery. The customer will be subject to inspections to ensure that the thermal storage device, electric battery storage device, and electric vehicle charger(s) are sized appropriately for residential use. If the thermal storage device, electric battery storage device, and electric vehicle charger(s) do not pass Company inspection, then the service will be denied. Service will be single-phase, alternating current, 60 hertz, at one standard secondary distribution voltage. Customers taking service under this rate schedule are responsible for paying both Distribution Service and Stranded Cost.. [More Info](https://github.com/thegridelectric/gridworks-ps/blob/dev/input_data/electricity_prices/isone/distp__w.isone.stetson__2020__gw.me.versant.a1.res.ets.csv). + * VersantATariff: Versant is a utility serving customers in Maine, and in particular serves much of the area behind the Keene Rd Constraint in the [GridWorks Millinocket Demo](https://gridworks.readthedocs.io/en/latest/millinocket-demo.html#background). The A Tariff is their standard residential tariff. Look for rate A in Versant [rate schedules](https://www.versantpower.com/residential/rates/rates-schedules/) + * VersantA20HeatTariff: """ Unknown = auto() - VersantStorageHeatTariff = auto() + VersantA1StorageHeatTariff = auto() VersantATariff = auto() + VersantA20HeatTariff = auto() @classmethod def default(cls) -> "DistributionTariff": diff --git a/src/gwatn/types/__init__.py b/src/gwatn/types/__init__.py index 49d8e7a..6c4be83 100644 --- a/src/gwatn/types/__init__.py +++ b/src/gwatn/types/__init__.py @@ -101,6 +101,12 @@ from gwatn.types.atn_params_brickstorageheater import AtnParamsBrickstorageheater_Maker from gwatn.types.atn_params_report import AtnParamsReport from gwatn.types.atn_params_report import AtnParamsReport_Maker +from gwatn.types.atn_params_simpleresistivehydronic import ( + AtnParamsSimpleresistivehydronic, +) +from gwatn.types.atn_params_simpleresistivehydronic import ( + AtnParamsSimpleresistivehydronic_Maker, +) from gwatn.types.basegnode_scada_create import BasegnodeScadaCreate from gwatn.types.basegnode_scada_create import BasegnodeScadaCreate_Maker from gwatn.types.discoverycert_algo_create import DiscoverycertAlgoCreate @@ -113,6 +119,12 @@ from gwatn.types.flo_params_brickstorageheater import FloParamsBrickstorageheater_Maker from gwatn.types.flo_params_report import FloParamsReport from gwatn.types.flo_params_report import FloParamsReport_Maker +from gwatn.types.flo_params_simpleresistivehydronic import ( + FloParamsSimpleresistivehydronic, +) +from gwatn.types.flo_params_simpleresistivehydronic import ( + FloParamsSimpleresistivehydronic_Maker, +) from gwatn.types.initial_tadeed_algo_create import InitialTadeedAlgoCreate from gwatn.types.initial_tadeed_algo_create import InitialTadeedAlgoCreate_Maker from gwatn.types.initial_tadeed_algo_optin import InitialTadeedAlgoOptin @@ -174,6 +186,8 @@ "AtnParamsBrickstorageheater_Maker", "AtnParamsReport", "AtnParamsReport_Maker", + "AtnParamsSimpleresistivehydronic", + "AtnParamsSimpleresistivehydronic_Maker", "BaseGNodeGt", "BaseGNodeGt_Maker", "BasegnodeScadaCreate", @@ -202,6 +216,8 @@ "FloParamsBrickstorageheater_Maker", "FloParamsReport", "FloParamsReport_Maker", + "FloParamsSimpleresistivehydronic", + "FloParamsSimpleresistivehydronic_Maker", "GNodeGt", "GNodeGt_Maker", "GNodeInstanceGt", diff --git a/src/gwatn/types/atn_params_brickstorageheater.py b/src/gwatn/types/atn_params_brickstorageheater.py index 6b48c9c..cec5691 100644 --- a/src/gwatn/types/atn_params_brickstorageheater.py +++ b/src/gwatn/types/atn_params_brickstorageheater.py @@ -25,6 +25,7 @@ class DistributionTariff000SchemaEnum: "00000000", "2127aba6", "ea5c675a", + "54aec3a7", ] @classmethod @@ -36,8 +37,9 @@ def is_symbol(cls, candidate: str) -> bool: class DistributionTariff000(StrEnum): Unknown = auto() - VersantStorageHeatTariff = auto() + VersantA1StorageHeatTariff = auto() VersantATariff = auto() + VersantA20HeatTariff = auto() @classmethod def default(cls) -> "DistributionTariff000": @@ -69,14 +71,16 @@ def local_to_type(cls, distribution_tariff: DistributionTariff) -> str: type_to_versioned_enum_dict: Dict[str, DistributionTariff000] = { "00000000": DistributionTariff000.Unknown, - "2127aba6": DistributionTariff000.VersantStorageHeatTariff, + "2127aba6": DistributionTariff000.VersantA1StorageHeatTariff, "ea5c675a": DistributionTariff000.VersantATariff, + "54aec3a7": DistributionTariff000.VersantA20HeatTariff, } versioned_enum_to_type_dict: Dict[DistributionTariff000, str] = { DistributionTariff000.Unknown: "00000000", - DistributionTariff000.VersantStorageHeatTariff: "2127aba6", + DistributionTariff000.VersantA1StorageHeatTariff: "2127aba6", DistributionTariff000.VersantATariff: "ea5c675a", + DistributionTariff000.VersantA20HeatTariff: "54aec3a7", } @@ -339,7 +343,7 @@ class AtnParamsBrickstorageheater(BaseModel): ) Tariff: DistributionTariff = Field( title="Tariff", - default=DistributionTariff.VersantStorageHeatTariff, + default=DistributionTariff.VersantA1StorageHeatTariff, ) EnergyType: EnergySupplyType = Field( title="EnergyType", diff --git a/src/gwatn/types/atn_params_simpleresistivehydronic.py b/src/gwatn/types/atn_params_simpleresistivehydronic.py new file mode 100644 index 0000000..0358cee --- /dev/null +++ b/src/gwatn/types/atn_params_simpleresistivehydronic.py @@ -0,0 +1,538 @@ +"""Type atn.params.simpleresistivehydronic, version 000""" +import json +from enum import auto +from typing import Any +from typing import Dict +from typing import List +from typing import Literal + +from fastapi_utils.enums import StrEnum +from gridworks.errors import SchemaError +from gridworks.message import as_enum +from pydantic import BaseModel +from pydantic import Field +from pydantic import validator + +from gwatn.enums import DistributionTariff +from gwatn.enums import EnergySupplyType +from gwatn.enums import RecognizedCurrencyUnit + + +class DistributionTariff000SchemaEnum: + enum_name: str = "distribution.tariff.000" + symbols: List[str] = [ + "00000000", + "2127aba6", + "ea5c675a", + "54aec3a7", + ] + + @classmethod + def is_symbol(cls, candidate: str) -> bool: + if candidate in cls.symbols: + return True + return False + + +class DistributionTariff000(StrEnum): + Unknown = auto() + VersantA1StorageHeatTariff = auto() + VersantATariff = auto() + VersantA20HeatTariff = auto() + + @classmethod + def default(cls) -> "DistributionTariff000": + return cls.Unknown + + @classmethod + def values(cls) -> List[str]: + return [elt.value for elt in cls] + + +class DistributionTariffMap: + @classmethod + def type_to_local(cls, symbol: str) -> DistributionTariff: + if not DistributionTariff000SchemaEnum.is_symbol(symbol): + raise SchemaError(f"{symbol} must belong to DistributionTariff000 symbols") + versioned_enum = cls.type_to_versioned_enum_dict[symbol] + return as_enum(versioned_enum, DistributionTariff, DistributionTariff.default()) + + @classmethod + def local_to_type(cls, distribution_tariff: DistributionTariff) -> str: + if not isinstance(distribution_tariff, DistributionTariff): + raise SchemaError( + f"{distribution_tariff} must be of type {DistributionTariff}" + ) + versioned_enum = as_enum( + distribution_tariff, DistributionTariff000, DistributionTariff000.default() + ) + return cls.versioned_enum_to_type_dict[versioned_enum] + + type_to_versioned_enum_dict: Dict[str, DistributionTariff000] = { + "00000000": DistributionTariff000.Unknown, + "2127aba6": DistributionTariff000.VersantA1StorageHeatTariff, + "ea5c675a": DistributionTariff000.VersantATariff, + "54aec3a7": DistributionTariff000.VersantA20HeatTariff, + } + + versioned_enum_to_type_dict: Dict[DistributionTariff000, str] = { + DistributionTariff000.Unknown: "00000000", + DistributionTariff000.VersantA1StorageHeatTariff: "2127aba6", + DistributionTariff000.VersantATariff: "ea5c675a", + DistributionTariff000.VersantA20HeatTariff: "54aec3a7", + } + + +class RecognizedCurrencyUnit000SchemaEnum: + enum_name: str = "recognized.currency.unit.000" + symbols: List[str] = [ + "00000000", + "e57c5143", + "f7b38fc5", + ] + + @classmethod + def is_symbol(cls, candidate: str) -> bool: + if candidate in cls.symbols: + return True + return False + + +class RecognizedCurrencyUnit000(StrEnum): + UNKNOWN = auto() + USD = auto() + GBP = auto() + + @classmethod + def default(cls) -> "RecognizedCurrencyUnit000": + return cls.UNKNOWN + + @classmethod + def values(cls) -> List[str]: + return [elt.value for elt in cls] + + +class RecognizedCurrencyUnitMap: + @classmethod + def type_to_local(cls, symbol: str) -> RecognizedCurrencyUnit: + if not RecognizedCurrencyUnit000SchemaEnum.is_symbol(symbol): + raise SchemaError( + f"{symbol} must belong to RecognizedCurrencyUnit000 symbols" + ) + versioned_enum = cls.type_to_versioned_enum_dict[symbol] + return as_enum( + versioned_enum, RecognizedCurrencyUnit, RecognizedCurrencyUnit.default() + ) + + @classmethod + def local_to_type(cls, recognized_currency_unit: RecognizedCurrencyUnit) -> str: + if not isinstance(recognized_currency_unit, RecognizedCurrencyUnit): + raise SchemaError( + f"{recognized_currency_unit} must be of type {RecognizedCurrencyUnit}" + ) + versioned_enum = as_enum( + recognized_currency_unit, + RecognizedCurrencyUnit000, + RecognizedCurrencyUnit000.default(), + ) + return cls.versioned_enum_to_type_dict[versioned_enum] + + type_to_versioned_enum_dict: Dict[str, RecognizedCurrencyUnit000] = { + "00000000": RecognizedCurrencyUnit000.UNKNOWN, + "e57c5143": RecognizedCurrencyUnit000.USD, + "f7b38fc5": RecognizedCurrencyUnit000.GBP, + } + + versioned_enum_to_type_dict: Dict[RecognizedCurrencyUnit000, str] = { + RecognizedCurrencyUnit000.UNKNOWN: "00000000", + RecognizedCurrencyUnit000.USD: "e57c5143", + RecognizedCurrencyUnit000.GBP: "f7b38fc5", + } + + +class EnergySupplyType000SchemaEnum: + enum_name: str = "energy.supply.type.000" + symbols: List[str] = [ + "00000000", + "cb18f937", + "e9dc99a6", + ] + + @classmethod + def is_symbol(cls, candidate: str) -> bool: + if candidate in cls.symbols: + return True + return False + + +class EnergySupplyType000(StrEnum): + Unknown = auto() + StandardOffer = auto() + RealtimeLocalLmp = auto() + + @classmethod + def default(cls) -> "EnergySupplyType000": + return cls.Unknown + + @classmethod + def values(cls) -> List[str]: + return [elt.value for elt in cls] + + +class EnergySupplyTypeMap: + @classmethod + def type_to_local(cls, symbol: str) -> EnergySupplyType: + if not EnergySupplyType000SchemaEnum.is_symbol(symbol): + raise SchemaError(f"{symbol} must belong to EnergySupplyType000 symbols") + versioned_enum = cls.type_to_versioned_enum_dict[symbol] + return as_enum(versioned_enum, EnergySupplyType, EnergySupplyType.default()) + + @classmethod + def local_to_type(cls, energy_supply_type: EnergySupplyType) -> str: + if not isinstance(energy_supply_type, EnergySupplyType): + raise SchemaError( + f"{energy_supply_type} must be of type {EnergySupplyType}" + ) + versioned_enum = as_enum( + energy_supply_type, EnergySupplyType000, EnergySupplyType000.default() + ) + return cls.versioned_enum_to_type_dict[versioned_enum] + + type_to_versioned_enum_dict: Dict[str, EnergySupplyType000] = { + "00000000": EnergySupplyType000.Unknown, + "cb18f937": EnergySupplyType000.StandardOffer, + "e9dc99a6": EnergySupplyType000.RealtimeLocalLmp, + } + + versioned_enum_to_type_dict: Dict[EnergySupplyType000, str] = { + EnergySupplyType000.Unknown: "00000000", + EnergySupplyType000.StandardOffer: "cb18f937", + EnergySupplyType000.RealtimeLocalLmp: "e9dc99a6", + } + + +def check_is_left_right_dot(v: str) -> None: + """ + LeftRightDot format: Lowercase alphanumeric words separated by periods, + most significant word (on the left) starting with an alphabet character. + + Raises: + ValueError: if not LeftRightDot format + """ + from typing import List + + try: + x: List[str] = v.split(".") + except: + raise ValueError(f"Failed to seperate {v} into words with split'.'") + first_word = x[0] + first_char = first_word[0] + if not first_char.isalpha(): + raise ValueError(f"Most significant word of {v} must start with alphabet char.") + for word in x: + if not word.isalnum(): + raise ValueError(f"words of {v} split by by '.' must be alphanumeric.") + if not v.islower(): + raise ValueError(f"All characters of {v} must be lowercase.") + + +class AtnParamsSimpleresistivehydronic(BaseModel): + """ """ + + GNodeAlias: str = Field( + title="GNodeAlias", + ) + HomeCity: str = Field( + title="HomeCity", + ) + TimezoneString: str = Field( + title="TimezoneString", + ) + StorageSteps: int = Field( + title="StorageSteps", + default=100, + ) + FloSlices: int = Field( + title="FloSlices", + default=48, + ) + SliceDurationMinutes: int = Field( + title="SliceDurationMinutes", + default=60, + ) + CurrencyUnit: RecognizedCurrencyUnit = Field( + title="CurrencyUnit", + default=RecognizedCurrencyUnit.USD, + ) + Tariff: DistributionTariff = Field( + title="Tariff", + default=DistributionTariff.VersantA1StorageHeatTariff, + ) + EnergyType: EnergySupplyType = Field( + title="EnergyType", + default=EnergySupplyType.RealtimeLocalLmp, + ) + StandardOfferPriceDollarsPerMwh: int = Field( + title="StandardOfferPriceDollarsPerMwh", + default=110, + ) + DistributionTariffDollarsPerMwh: int = Field( + title="DistributionTariffDollarsPerMwh", + default=113, + ) + StoreSizeGallons: int = Field( + title="StoreSizeGallons", + default=240, + ) + MaxStoreTempF: int = Field( + title="MaxStoreTempF", + default=210, + ) + ElementMaxPowerKw: float = Field( + title="ElementMaxPowerKw", + default=9.5, + ) + RequiredSourceWaterTempF: int = Field( + title="RequiredSourceWaterTempF", + default=120, + ) + FixedPumpGpm: float = Field( + title="FixedPumpGpm", + default=5.5, + ) + ReturnWaterFixedDeltaT: int = Field( + title="ReturnWaterFixedDeltaT", + default=20, + ) + AnnualHvacKwhTh: int = Field( + title="AnnualHvacKwhTh", + default=25000, + ) + AmbientPowerInKw: float = Field( + title="AmbientPowerInKw", + default=1.2, + ) + HouseWorstCaseTempF: int = Field( + title="HouseWorstCaseTempF", + default=-7, + ) + RoomTempF: int = Field( + title="RoomTempF", + default=68, + ) + TypeName: Literal[ + "atn.params.simpleresistivehydronic" + ] = "atn.params.simpleresistivehydronic" + Version: str = "000" + + @validator("GNodeAlias") + def _check_g_node_alias(cls, v: str) -> str: + try: + check_is_left_right_dot(v) + except ValueError as e: + raise ValueError(f"GNodeAlias failed LeftRightDot format validation: {e}") + return v + + @validator("CurrencyUnit") + def _check_currency_unit(cls, v: RecognizedCurrencyUnit) -> RecognizedCurrencyUnit: + return as_enum(v, RecognizedCurrencyUnit, RecognizedCurrencyUnit.UNKNOWN) + + @validator("Tariff") + def _check_tariff(cls, v: DistributionTariff) -> DistributionTariff: + return as_enum(v, DistributionTariff, DistributionTariff.Unknown) + + @validator("EnergyType") + def _check_energy_type(cls, v: EnergySupplyType) -> EnergySupplyType: + return as_enum(v, EnergySupplyType, EnergySupplyType.Unknown) + + def as_dict(self) -> Dict[str, Any]: + d = self.dict() + del d["CurrencyUnit"] + CurrencyUnit = as_enum( + self.CurrencyUnit, RecognizedCurrencyUnit, RecognizedCurrencyUnit.default() + ) + d["CurrencyUnitGtEnumSymbol"] = RecognizedCurrencyUnitMap.local_to_type( + CurrencyUnit + ) + del d["Tariff"] + Tariff = as_enum(self.Tariff, DistributionTariff, DistributionTariff.default()) + d["TariffGtEnumSymbol"] = DistributionTariffMap.local_to_type(Tariff) + del d["EnergyType"] + EnergyType = as_enum( + self.EnergyType, EnergySupplyType, EnergySupplyType.default() + ) + d["EnergyTypeGtEnumSymbol"] = EnergySupplyTypeMap.local_to_type(EnergyType) + return d + + def as_type(self) -> str: + return json.dumps(self.as_dict()) + + def __hash__(self): + return hash((type(self),) + tuple(self.__dict__.values())) # noqa + + +class AtnParamsSimpleresistivehydronic_Maker: + type_name = "atn.params.simpleresistivehydronic" + version = "000" + + def __init__( + self, + g_node_alias: str, + home_city: str, + timezone_string: str, + storage_steps: int, + flo_slices: int, + slice_duration_minutes: int, + currency_unit: RecognizedCurrencyUnit, + tariff: DistributionTariff, + energy_type: EnergySupplyType, + standard_offer_price_dollars_per_mwh: int, + distribution_tariff_dollars_per_mwh: int, + store_size_gallons: int, + max_store_temp_f: int, + element_max_power_kw: float, + required_source_water_temp_f: int, + fixed_pump_gpm: float, + return_water_fixed_delta_t: int, + annual_hvac_kwh_th: int, + ambient_power_in_kw: float, + house_worst_case_temp_f: int, + room_temp_f: int, + ): + self.tuple = AtnParamsSimpleresistivehydronic( + GNodeAlias=g_node_alias, + HomeCity=home_city, + TimezoneString=timezone_string, + StorageSteps=storage_steps, + FloSlices=flo_slices, + SliceDurationMinutes=slice_duration_minutes, + CurrencyUnit=currency_unit, + Tariff=tariff, + EnergyType=energy_type, + StandardOfferPriceDollarsPerMwh=standard_offer_price_dollars_per_mwh, + DistributionTariffDollarsPerMwh=distribution_tariff_dollars_per_mwh, + StoreSizeGallons=store_size_gallons, + MaxStoreTempF=max_store_temp_f, + ElementMaxPowerKw=element_max_power_kw, + RequiredSourceWaterTempF=required_source_water_temp_f, + FixedPumpGpm=fixed_pump_gpm, + ReturnWaterFixedDeltaT=return_water_fixed_delta_t, + AnnualHvacKwhTh=annual_hvac_kwh_th, + AmbientPowerInKw=ambient_power_in_kw, + HouseWorstCaseTempF=house_worst_case_temp_f, + RoomTempF=room_temp_f, + # + ) + + @classmethod + def tuple_to_type(cls, tuple: AtnParamsSimpleresistivehydronic) -> str: + """ + Given a Python class object, returns the serialized JSON type object + """ + return tuple.as_type() + + @classmethod + def type_to_tuple(cls, t: str) -> AtnParamsSimpleresistivehydronic: + """ + Given a serialized JSON type object, returns the Python class object + """ + try: + d = json.loads(t) + except TypeError: + raise SchemaError("Type must be string or bytes!") + if not isinstance(d, dict): + raise SchemaError(f"Deserializing {t} must result in dict!") + return cls.dict_to_tuple(d) + + @classmethod + def dict_to_tuple(cls, d: dict[str, Any]) -> AtnParamsSimpleresistivehydronic: + d2 = dict(d) + if "GNodeAlias" not in d2.keys(): + raise SchemaError(f"dict {d2} missing GNodeAlias") + if "HomeCity" not in d2.keys(): + raise SchemaError(f"dict {d2} missing HomeCity") + if "TimezoneString" not in d2.keys(): + raise SchemaError(f"dict {d2} missing TimezoneString") + if "StorageSteps" not in d2.keys(): + raise SchemaError(f"dict {d2} missing StorageSteps") + if "FloSlices" not in d2.keys(): + raise SchemaError(f"dict {d2} missing FloSlices") + if "SliceDurationMinutes" not in d2.keys(): + raise SchemaError(f"dict {d2} missing SliceDurationMinutes") + if "CurrencyUnitGtEnumSymbol" not in d2.keys(): + raise SchemaError(f"dict {d2} missing CurrencyUnitGtEnumSymbol") + if ( + d2["CurrencyUnitGtEnumSymbol"] + in RecognizedCurrencyUnit000SchemaEnum.symbols + ): + d2["CurrencyUnit"] = RecognizedCurrencyUnitMap.type_to_local( + d2["CurrencyUnitGtEnumSymbol"] + ) + else: + d2["CurrencyUnit"] = RecognizedCurrencyUnit.default() + if "TariffGtEnumSymbol" not in d2.keys(): + raise SchemaError(f"dict {d2} missing TariffGtEnumSymbol") + if d2["TariffGtEnumSymbol"] in DistributionTariff000SchemaEnum.symbols: + d2["Tariff"] = DistributionTariffMap.type_to_local(d2["TariffGtEnumSymbol"]) + else: + d2["Tariff"] = DistributionTariff.default() + if "EnergyTypeGtEnumSymbol" not in d2.keys(): + raise SchemaError(f"dict {d2} missing EnergyTypeGtEnumSymbol") + if d2["EnergyTypeGtEnumSymbol"] in EnergySupplyType000SchemaEnum.symbols: + d2["EnergyType"] = EnergySupplyTypeMap.type_to_local( + d2["EnergyTypeGtEnumSymbol"] + ) + else: + d2["EnergyType"] = EnergySupplyType.default() + if "StandardOfferPriceDollarsPerMwh" not in d2.keys(): + raise SchemaError(f"dict {d2} missing StandardOfferPriceDollarsPerMwh") + if "DistributionTariffDollarsPerMwh" not in d2.keys(): + raise SchemaError(f"dict {d2} missing DistributionTariffDollarsPerMwh") + if "StoreSizeGallons" not in d2.keys(): + raise SchemaError(f"dict {d2} missing StoreSizeGallons") + if "MaxStoreTempF" not in d2.keys(): + raise SchemaError(f"dict {d2} missing MaxStoreTempF") + if "ElementMaxPowerKw" not in d2.keys(): + raise SchemaError(f"dict {d2} missing ElementMaxPowerKw") + if "RequiredSourceWaterTempF" not in d2.keys(): + raise SchemaError(f"dict {d2} missing RequiredSourceWaterTempF") + if "FixedPumpGpm" not in d2.keys(): + raise SchemaError(f"dict {d2} missing FixedPumpGpm") + if "ReturnWaterFixedDeltaT" not in d2.keys(): + raise SchemaError(f"dict {d2} missing ReturnWaterFixedDeltaT") + if "AnnualHvacKwhTh" not in d2.keys(): + raise SchemaError(f"dict {d2} missing AnnualHvacKwhTh") + if "AmbientPowerInKw" not in d2.keys(): + raise SchemaError(f"dict {d2} missing AmbientPowerInKw") + if "HouseWorstCaseTempF" not in d2.keys(): + raise SchemaError(f"dict {d2} missing HouseWorstCaseTempF") + if "RoomTempF" not in d2.keys(): + raise SchemaError(f"dict {d2} missing RoomTempF") + if "TypeName" not in d2.keys(): + raise SchemaError(f"dict {d2} missing TypeName") + + return AtnParamsSimpleresistivehydronic( + GNodeAlias=d2["GNodeAlias"], + HomeCity=d2["HomeCity"], + TimezoneString=d2["TimezoneString"], + StorageSteps=d2["StorageSteps"], + FloSlices=d2["FloSlices"], + SliceDurationMinutes=d2["SliceDurationMinutes"], + CurrencyUnit=d2["CurrencyUnit"], + Tariff=d2["Tariff"], + EnergyType=d2["EnergyType"], + StandardOfferPriceDollarsPerMwh=d2["StandardOfferPriceDollarsPerMwh"], + DistributionTariffDollarsPerMwh=d2["DistributionTariffDollarsPerMwh"], + StoreSizeGallons=d2["StoreSizeGallons"], + MaxStoreTempF=d2["MaxStoreTempF"], + ElementMaxPowerKw=d2["ElementMaxPowerKw"], + RequiredSourceWaterTempF=d2["RequiredSourceWaterTempF"], + FixedPumpGpm=d2["FixedPumpGpm"], + ReturnWaterFixedDeltaT=d2["ReturnWaterFixedDeltaT"], + AnnualHvacKwhTh=d2["AnnualHvacKwhTh"], + AmbientPowerInKw=d2["AmbientPowerInKw"], + HouseWorstCaseTempF=d2["HouseWorstCaseTempF"], + RoomTempF=d2["RoomTempF"], + TypeName=d2["TypeName"], + Version="000", + ) diff --git a/src/gwatn/types/flo_params_brickstorageheater.py b/src/gwatn/types/flo_params_brickstorageheater.py index 1139a5c..2980720 100644 --- a/src/gwatn/types/flo_params_brickstorageheater.py +++ b/src/gwatn/types/flo_params_brickstorageheater.py @@ -267,7 +267,7 @@ class FloParamsBrickstorageheater(BaseModel): title="PowerRequiredByHouseFromSystemAvgKwList", default=[3.42], ) - C: Optional[float] = Field( + C: float = Field( title="C", default=200, ) @@ -293,9 +293,8 @@ class FloParamsBrickstorageheater(BaseModel): WeatherUid: str = Field( title="WeatherUid", ) - DistPriceUid: Optional[str] = Field( + DistPriceUid: str = Field( title="DistPriceUid", - default=None, ) RegPriceUid: Optional[str] = Field( title="RegPriceUid", @@ -373,9 +372,7 @@ def _check_weather_uid(cls, v: str) -> str: return v @validator("DistPriceUid") - def _check_dist_price_uid(cls, v: Optional[str]) -> Optional[str]: - if v is None: - return v + def _check_dist_price_uid(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) except ValueError as e: @@ -430,10 +427,6 @@ def as_dict(self) -> Dict[str, Any]: RecognizedTemperatureUnit.default(), ) d["TempUnitGtEnumSymbol"] = RecognizedTemperatureUnitMap.local_to_type(TempUnit) - if d["C"] is None: - del d["C"] - if d["DistPriceUid"] is None: - del d["DistPriceUid"] if d["RegPriceUid"] is None: del d["RegPriceUid"] return d @@ -464,14 +457,14 @@ def __init__( storage_steps: int, slice_duration_minutes: List[int], power_required_by_house_from_system_avg_kw_list: List[float], - c: Optional[float], + c: float, realtime_electricity_price: List[float], outside_temp_f: List[float], distribution_price: List[float], rt_elec_price_uid: str, regulation_price: List[float], weather_uid: str, - dist_price_uid: Optional[str], + dist_price_uid: str, reg_price_uid: Optional[str], start_year_utc: int, start_month_utc: int, @@ -587,7 +580,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> FloParamsBrickstorageheater: f"dict {d2} missing PowerRequiredByHouseFromSystemAvgKwList" ) if "C" not in d2.keys(): - d2["C"] = None + raise SchemaError(f"dict {d2} missing C") if "RealtimeElectricityPrice" not in d2.keys(): raise SchemaError(f"dict {d2} missing RealtimeElectricityPrice") if "OutsideTempF" not in d2.keys(): @@ -601,7 +594,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> FloParamsBrickstorageheater: if "WeatherUid" not in d2.keys(): raise SchemaError(f"dict {d2} missing WeatherUid") if "DistPriceUid" not in d2.keys(): - d2["DistPriceUid"] = None + raise SchemaError(f"dict {d2} missing DistPriceUid") if "RegPriceUid" not in d2.keys(): d2["RegPriceUid"] = None if "StartYearUtc" not in d2.keys(): diff --git a/src/gwatn/types/flo_params_simpleresistivehydronic.py b/src/gwatn/types/flo_params_simpleresistivehydronic.py new file mode 100644 index 0000000..3a76893 --- /dev/null +++ b/src/gwatn/types/flo_params_simpleresistivehydronic.py @@ -0,0 +1,480 @@ +"""Type flo.params.simpleresistivehydronic, version 000""" +import json +from enum import auto +from typing import Any +from typing import Dict +from typing import List +from typing import Literal + +from fastapi_utils.enums import StrEnum +from gridworks.errors import SchemaError +from gridworks.message import as_enum +from pydantic import BaseModel +from pydantic import Field +from pydantic import validator + +from gwatn.enums import RecognizedCurrencyUnit + + +class RecognizedCurrencyUnit000SchemaEnum: + enum_name: str = "recognized.currency.unit.000" + symbols: List[str] = [ + "00000000", + "e57c5143", + "f7b38fc5", + ] + + @classmethod + def is_symbol(cls, candidate: str) -> bool: + if candidate in cls.symbols: + return True + return False + + +class RecognizedCurrencyUnit000(StrEnum): + UNKNOWN = auto() + USD = auto() + GBP = auto() + + @classmethod + def default(cls) -> "RecognizedCurrencyUnit000": + return cls.UNKNOWN + + @classmethod + def values(cls) -> List[str]: + return [elt.value for elt in cls] + + +class RecognizedCurrencyUnitMap: + @classmethod + def type_to_local(cls, symbol: str) -> RecognizedCurrencyUnit: + if not RecognizedCurrencyUnit000SchemaEnum.is_symbol(symbol): + raise SchemaError( + f"{symbol} must belong to RecognizedCurrencyUnit000 symbols" + ) + versioned_enum = cls.type_to_versioned_enum_dict[symbol] + return as_enum( + versioned_enum, RecognizedCurrencyUnit, RecognizedCurrencyUnit.default() + ) + + @classmethod + def local_to_type(cls, recognized_currency_unit: RecognizedCurrencyUnit) -> str: + if not isinstance(recognized_currency_unit, RecognizedCurrencyUnit): + raise SchemaError( + f"{recognized_currency_unit} must be of type {RecognizedCurrencyUnit}" + ) + versioned_enum = as_enum( + recognized_currency_unit, + RecognizedCurrencyUnit000, + RecognizedCurrencyUnit000.default(), + ) + return cls.versioned_enum_to_type_dict[versioned_enum] + + type_to_versioned_enum_dict: Dict[str, RecognizedCurrencyUnit000] = { + "00000000": RecognizedCurrencyUnit000.UNKNOWN, + "e57c5143": RecognizedCurrencyUnit000.USD, + "f7b38fc5": RecognizedCurrencyUnit000.GBP, + } + + versioned_enum_to_type_dict: Dict[RecognizedCurrencyUnit000, str] = { + RecognizedCurrencyUnit000.UNKNOWN: "00000000", + RecognizedCurrencyUnit000.USD: "e57c5143", + RecognizedCurrencyUnit000.GBP: "f7b38fc5", + } + + +def check_is_uuid_canonical_textual(v: str) -> None: + """ + UuidCanonicalTextual format: A string of hex words separated by hyphens + of length 8-4-4-4-12. + + Raises: + ValueError: if not UuidCanonicalTextual format + """ + try: + x = v.split("-") + except AttributeError as e: + raise ValueError(f"Failed to split on -: {e}") + if len(x) != 5: + raise ValueError(f"{v} split by '-' did not have 5 words") + for hex_word in x: + try: + int(hex_word, 16) + except ValueError: + raise ValueError(f"Words of {v} are not all hex") + if len(x[0]) != 8: + raise ValueError(f"{v} word lengths not 8-4-4-4-12") + if len(x[1]) != 4: + raise ValueError(f"{v} word lengths not 8-4-4-4-12") + if len(x[2]) != 4: + raise ValueError(f"{v} word lengths not 8-4-4-4-12") + if len(x[3]) != 4: + raise ValueError(f"{v} word lengths not 8-4-4-4-12") + if len(x[4]) != 12: + raise ValueError(f"{v} word lengths not 8-4-4-4-12") + + +def check_is_left_right_dot(v: str) -> None: + """ + LeftRightDot format: Lowercase alphanumeric words separated by periods, + most significant word (on the left) starting with an alphabet character. + + Raises: + ValueError: if not LeftRightDot format + """ + from typing import List + + try: + x: List[str] = v.split(".") + except: + raise ValueError(f"Failed to seperate {v} into words with split'.'") + first_word = x[0] + first_char = first_word[0] + if not first_char.isalpha(): + raise ValueError(f"Most significant word of {v} must start with alphabet char.") + for word in x: + if not word.isalnum(): + raise ValueError(f"words of {v} split by by '.' must be alphanumeric.") + if not v.islower(): + raise ValueError(f"All characters of {v} must be lowercase.") + + +class FloParamsSimpleresistivehydronic(BaseModel): + """ """ + + GNodeAlias: str = Field( + title="GNodeAlias", + ) + FloParamsUid: str = Field( + title="FloParamsUid", + ) + HomeCity: str = Field( + title="HomeCity", + default="MILLINOCKET_ME", + ) + TimezoneString: str = Field( + title="TimezoneString", + default="US/Eastern", + ) + StartYearUtc: int = Field( + title="StartYearUtc", + default=2020, + ) + StartMonthUtc: int = Field( + title="StartMonthUtc", + default=1, + ) + StartDayUtc: int = Field( + title="StartDayUtc", + default=1, + ) + StartHourUtc: int = Field( + title="StartHourUtc", + default=0, + ) + StartMinuteUtc: int = Field( + title="StartMinuteUtc", + default=0, + ) + StoreSizeGallons: int = Field( + title="StoreSizeGallons", + default=240, + ) + MaxStoreTempF: int = Field( + title="MaxStoreTempF", + default=190, + ) + ElementMaxPowerKw: float = Field( + title="ElementMaxPowerKw", + default=9.5, + ) + RequiredSourceWaterTempF: int = Field( + title="RequiredSourceWaterTempF", + default=120, + ) + FixedPumpGpm: float = Field( + title="FixedPumpGpm", + default=4.5, + ) + ReturnWaterFixedDeltaT: int = Field( + title="ReturnWaterFixedDeltaT", + default=20, + ) + SliceDurationMinutes: List[int] = Field( + title="SliceDurationMinutes", + default=[60], + ) + PowerLostFromHouseKwList: List[float] = Field( + title="PowerLostFromHouseKwList", + default=[3.42], + ) + OutsideTempF: List[float] = Field( + title="OutsideTempF", + default=[-5.1], + ) + DistributionPrice: List[float] = Field( + title="DistributionPrice", + default=[40.0], + ) + RealtimeElectricityPrice: List[float] = Field( + title="RealtimeElectricityPrice", + default=[10.35], + ) + RtElecPriceUid: str = Field( + title="RtElecPriceUid", + ) + WeatherUid: str = Field( + title="WeatherUid", + ) + DistPriceUid: str = Field( + title="DistPriceUid", + ) + CurrencyUnit: RecognizedCurrencyUnit = Field( + title="CurrencyUnit", + default=RecognizedCurrencyUnit.USD, + ) + TypeName: Literal[ + "flo.params.simpleresistivehydronic" + ] = "flo.params.simpleresistivehydronic" + Version: str = "000" + + @validator("GNodeAlias") + def _check_g_node_alias(cls, v: str) -> str: + try: + check_is_left_right_dot(v) + except ValueError as e: + raise ValueError(f"GNodeAlias failed LeftRightDot format validation: {e}") + return v + + @validator("FloParamsUid") + def _check_flo_params_uid(cls, v: str) -> str: + try: + check_is_uuid_canonical_textual(v) + except ValueError as e: + raise ValueError( + f"FloParamsUid failed UuidCanonicalTextual format validation: {e}" + ) + return v + + @validator("RtElecPriceUid") + def _check_rt_elec_price_uid(cls, v: str) -> str: + try: + check_is_uuid_canonical_textual(v) + except ValueError as e: + raise ValueError( + f"RtElecPriceUid failed UuidCanonicalTextual format validation: {e}" + ) + return v + + @validator("WeatherUid") + def _check_weather_uid(cls, v: str) -> str: + try: + check_is_uuid_canonical_textual(v) + except ValueError as e: + raise ValueError( + f"WeatherUid failed UuidCanonicalTextual format validation: {e}" + ) + return v + + @validator("DistPriceUid") + def _check_dist_price_uid(cls, v: str) -> str: + try: + check_is_uuid_canonical_textual(v) + except ValueError as e: + raise ValueError( + f"DistPriceUid failed UuidCanonicalTextual format validation: {e}" + ) + return v + + @validator("CurrencyUnit") + def _check_currency_unit(cls, v: RecognizedCurrencyUnit) -> RecognizedCurrencyUnit: + return as_enum(v, RecognizedCurrencyUnit, RecognizedCurrencyUnit.UNKNOWN) + + def as_dict(self) -> Dict[str, Any]: + d = self.dict() + del d["CurrencyUnit"] + CurrencyUnit = as_enum( + self.CurrencyUnit, RecognizedCurrencyUnit, RecognizedCurrencyUnit.default() + ) + d["CurrencyUnitGtEnumSymbol"] = RecognizedCurrencyUnitMap.local_to_type( + CurrencyUnit + ) + return d + + def as_type(self) -> str: + return json.dumps(self.as_dict()) + + def __hash__(self): + return hash((type(self),) + tuple(self.__dict__.values())) # noqa + + +class FloParamsSimpleresistivehydronic_Maker: + type_name = "flo.params.simpleresistivehydronic" + version = "000" + + def __init__( + self, + g_node_alias: str, + flo_params_uid: str, + home_city: str, + timezone_string: str, + start_year_utc: int, + start_month_utc: int, + start_day_utc: int, + start_hour_utc: int, + start_minute_utc: int, + store_size_gallons: int, + max_store_temp_f: int, + element_max_power_kw: float, + required_source_water_temp_f: int, + fixed_pump_gpm: float, + return_water_fixed_delta_t: int, + slice_duration_minutes: List[int], + power_lost_from_house_kw_list: List[float], + outside_temp_f: List[float], + distribution_price: List[float], + realtime_electricity_price: List[float], + rt_elec_price_uid: str, + weather_uid: str, + dist_price_uid: str, + currency_unit: RecognizedCurrencyUnit, + ): + self.tuple = FloParamsSimpleresistivehydronic( + GNodeAlias=g_node_alias, + FloParamsUid=flo_params_uid, + HomeCity=home_city, + TimezoneString=timezone_string, + StartYearUtc=start_year_utc, + StartMonthUtc=start_month_utc, + StartDayUtc=start_day_utc, + StartHourUtc=start_hour_utc, + StartMinuteUtc=start_minute_utc, + StoreSizeGallons=store_size_gallons, + MaxStoreTempF=max_store_temp_f, + ElementMaxPowerKw=element_max_power_kw, + RequiredSourceWaterTempF=required_source_water_temp_f, + FixedPumpGpm=fixed_pump_gpm, + ReturnWaterFixedDeltaT=return_water_fixed_delta_t, + SliceDurationMinutes=slice_duration_minutes, + PowerLostFromHouseKwList=power_lost_from_house_kw_list, + OutsideTempF=outside_temp_f, + DistributionPrice=distribution_price, + RealtimeElectricityPrice=realtime_electricity_price, + RtElecPriceUid=rt_elec_price_uid, + WeatherUid=weather_uid, + DistPriceUid=dist_price_uid, + CurrencyUnit=currency_unit, + # + ) + + @classmethod + def tuple_to_type(cls, tuple: FloParamsSimpleresistivehydronic) -> str: + """ + Given a Python class object, returns the serialized JSON type object + """ + return tuple.as_type() + + @classmethod + def type_to_tuple(cls, t: str) -> FloParamsSimpleresistivehydronic: + """ + Given a serialized JSON type object, returns the Python class object + """ + try: + d = json.loads(t) + except TypeError: + raise SchemaError("Type must be string or bytes!") + if not isinstance(d, dict): + raise SchemaError(f"Deserializing {t} must result in dict!") + return cls.dict_to_tuple(d) + + @classmethod + def dict_to_tuple(cls, d: dict[str, Any]) -> FloParamsSimpleresistivehydronic: + d2 = dict(d) + if "GNodeAlias" not in d2.keys(): + raise SchemaError(f"dict {d2} missing GNodeAlias") + if "FloParamsUid" not in d2.keys(): + raise SchemaError(f"dict {d2} missing FloParamsUid") + if "HomeCity" not in d2.keys(): + raise SchemaError(f"dict {d2} missing HomeCity") + if "TimezoneString" not in d2.keys(): + raise SchemaError(f"dict {d2} missing TimezoneString") + if "StartYearUtc" not in d2.keys(): + raise SchemaError(f"dict {d2} missing StartYearUtc") + if "StartMonthUtc" not in d2.keys(): + raise SchemaError(f"dict {d2} missing StartMonthUtc") + if "StartDayUtc" not in d2.keys(): + raise SchemaError(f"dict {d2} missing StartDayUtc") + if "StartHourUtc" not in d2.keys(): + raise SchemaError(f"dict {d2} missing StartHourUtc") + if "StartMinuteUtc" not in d2.keys(): + raise SchemaError(f"dict {d2} missing StartMinuteUtc") + if "StoreSizeGallons" not in d2.keys(): + raise SchemaError(f"dict {d2} missing StoreSizeGallons") + if "MaxStoreTempF" not in d2.keys(): + raise SchemaError(f"dict {d2} missing MaxStoreTempF") + if "ElementMaxPowerKw" not in d2.keys(): + raise SchemaError(f"dict {d2} missing ElementMaxPowerKw") + if "RequiredSourceWaterTempF" not in d2.keys(): + raise SchemaError(f"dict {d2} missing RequiredSourceWaterTempF") + if "FixedPumpGpm" not in d2.keys(): + raise SchemaError(f"dict {d2} missing FixedPumpGpm") + if "ReturnWaterFixedDeltaT" not in d2.keys(): + raise SchemaError(f"dict {d2} missing ReturnWaterFixedDeltaT") + if "SliceDurationMinutes" not in d2.keys(): + raise SchemaError(f"dict {d2} missing SliceDurationMinutes") + if "PowerLostFromHouseKwList" not in d2.keys(): + raise SchemaError(f"dict {d2} missing PowerLostFromHouseKwList") + if "OutsideTempF" not in d2.keys(): + raise SchemaError(f"dict {d2} missing OutsideTempF") + if "DistributionPrice" not in d2.keys(): + raise SchemaError(f"dict {d2} missing DistributionPrice") + if "RealtimeElectricityPrice" not in d2.keys(): + raise SchemaError(f"dict {d2} missing RealtimeElectricityPrice") + if "RtElecPriceUid" not in d2.keys(): + raise SchemaError(f"dict {d2} missing RtElecPriceUid") + if "WeatherUid" not in d2.keys(): + raise SchemaError(f"dict {d2} missing WeatherUid") + if "DistPriceUid" not in d2.keys(): + raise SchemaError(f"dict {d2} missing DistPriceUid") + if "CurrencyUnitGtEnumSymbol" not in d2.keys(): + raise SchemaError(f"dict {d2} missing CurrencyUnitGtEnumSymbol") + if ( + d2["CurrencyUnitGtEnumSymbol"] + in RecognizedCurrencyUnit000SchemaEnum.symbols + ): + d2["CurrencyUnit"] = RecognizedCurrencyUnitMap.type_to_local( + d2["CurrencyUnitGtEnumSymbol"] + ) + else: + d2["CurrencyUnit"] = RecognizedCurrencyUnit.default() + if "TypeName" not in d2.keys(): + raise SchemaError(f"dict {d2} missing TypeName") + + return FloParamsSimpleresistivehydronic( + GNodeAlias=d2["GNodeAlias"], + FloParamsUid=d2["FloParamsUid"], + HomeCity=d2["HomeCity"], + TimezoneString=d2["TimezoneString"], + StartYearUtc=d2["StartYearUtc"], + StartMonthUtc=d2["StartMonthUtc"], + StartDayUtc=d2["StartDayUtc"], + StartHourUtc=d2["StartHourUtc"], + StartMinuteUtc=d2["StartMinuteUtc"], + StoreSizeGallons=d2["StoreSizeGallons"], + MaxStoreTempF=d2["MaxStoreTempF"], + ElementMaxPowerKw=d2["ElementMaxPowerKw"], + RequiredSourceWaterTempF=d2["RequiredSourceWaterTempF"], + FixedPumpGpm=d2["FixedPumpGpm"], + ReturnWaterFixedDeltaT=d2["ReturnWaterFixedDeltaT"], + SliceDurationMinutes=d2["SliceDurationMinutes"], + PowerLostFromHouseKwList=d2["PowerLostFromHouseKwList"], + OutsideTempF=d2["OutsideTempF"], + DistributionPrice=d2["DistributionPrice"], + RealtimeElectricityPrice=d2["RealtimeElectricityPrice"], + RtElecPriceUid=d2["RtElecPriceUid"], + WeatherUid=d2["WeatherUid"], + DistPriceUid=d2["DistPriceUid"], + CurrencyUnit=d2["CurrencyUnit"], + TypeName=d2["TypeName"], + Version="000", + ) diff --git a/tests/enums/distribution_tariff_test.py b/tests/enums/distribution_tariff_test.py index dbcc1d5..7aa417d 100644 --- a/tests/enums/distribution_tariff_test.py +++ b/tests/enums/distribution_tariff_test.py @@ -5,8 +5,9 @@ def test_distribution_tariff() -> None: assert set(DistributionTariff.values()) == { "Unknown", - "VersantStorageHeatTariff", + "VersantA1StorageHeatTariff", "VersantATariff", + "VersantA20HeatTariff", } assert DistributionTariff.default() == DistributionTariff.Unknown diff --git a/tests/types/test_atn_params_simpleresistivehydronic.py b/tests/types/test_atn_params_simpleresistivehydronic.py new file mode 100644 index 0000000..046fb2a --- /dev/null +++ b/tests/types/test_atn_params_simpleresistivehydronic.py @@ -0,0 +1,283 @@ +"""Tests atn.params.simpleresistivehydronic type, version 000""" +import json + +import pytest +from gridworks.errors import SchemaError +from pydantic import ValidationError + +from gwatn.enums import DistributionTariff +from gwatn.enums import EnergySupplyType +from gwatn.enums import RecognizedCurrencyUnit +from gwatn.types import AtnParamsSimpleresistivehydronic_Maker as Maker + + +def test_atn_params_simpleresistivehydronic_generated() -> None: + d = { + "GNodeAlias": "d1.isone.ver.keene.holly", + "HomeCity": "MILLINOCKET_ME", + "TimezoneString": "US/Eastern", + "StorageSteps": 100, + "FloSlices": 48, + "SliceDurationMinutes": 60, + "CurrencyUnitGtEnumSymbol": "e57c5143", + "TariffGtEnumSymbol": "2127aba6", + "EnergyTypeGtEnumSymbol": "e9dc99a6", + "StandardOfferPriceDollarsPerMwh": 110, + "DistributionTariffDollarsPerMwh": 113, + "StoreSizeGallons": 240, + "MaxStoreTempF": 210, + "ElementMaxPowerKw": 9.5, + "RequiredSourceWaterTempF": 120, + "FixedPumpGpm": 5.5, + "ReturnWaterFixedDeltaT": 20, + "AnnualHvacKwhTh": 25000, + "AmbientPowerInKw": 1.2, + "HouseWorstCaseTempF": -7, + "RoomTempF": 68, + "TypeName": "atn.params.simpleresistivehydronic", + "Version": "000", + } + + with pytest.raises(SchemaError): + Maker.type_to_tuple(d) + + with pytest.raises(SchemaError): + Maker.type_to_tuple('"not a dict"') + + # Test type_to_tuple + gtype = json.dumps(d) + gtuple = Maker.type_to_tuple(gtype) + + # test type_to_tuple and tuple_to_type maps + assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple + + # test Maker init + t = Maker( + g_node_alias=gtuple.GNodeAlias, + home_city=gtuple.HomeCity, + timezone_string=gtuple.TimezoneString, + storage_steps=gtuple.StorageSteps, + flo_slices=gtuple.FloSlices, + slice_duration_minutes=gtuple.SliceDurationMinutes, + currency_unit=gtuple.CurrencyUnit, + tariff=gtuple.Tariff, + energy_type=gtuple.EnergyType, + standard_offer_price_dollars_per_mwh=gtuple.StandardOfferPriceDollarsPerMwh, + distribution_tariff_dollars_per_mwh=gtuple.DistributionTariffDollarsPerMwh, + store_size_gallons=gtuple.StoreSizeGallons, + max_store_temp_f=gtuple.MaxStoreTempF, + element_max_power_kw=gtuple.ElementMaxPowerKw, + required_source_water_temp_f=gtuple.RequiredSourceWaterTempF, + fixed_pump_gpm=gtuple.FixedPumpGpm, + return_water_fixed_delta_t=gtuple.ReturnWaterFixedDeltaT, + annual_hvac_kwh_th=gtuple.AnnualHvacKwhTh, + ambient_power_in_kw=gtuple.AmbientPowerInKw, + house_worst_case_temp_f=gtuple.HouseWorstCaseTempF, + room_temp_f=gtuple.RoomTempF, + ).tuple + assert t == gtuple + + ###################################### + # SchemaError raised if missing a required attribute + ###################################### + + d2 = dict(d) + del d2["TypeName"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["GNodeAlias"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["HomeCity"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["TimezoneString"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["StorageSteps"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["FloSlices"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["SliceDurationMinutes"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["CurrencyUnitGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["TariffGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["EnergyTypeGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["StandardOfferPriceDollarsPerMwh"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["DistributionTariffDollarsPerMwh"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["StoreSizeGallons"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["MaxStoreTempF"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["ElementMaxPowerKw"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["RequiredSourceWaterTempF"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["FixedPumpGpm"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["ReturnWaterFixedDeltaT"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["AnnualHvacKwhTh"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["AmbientPowerInKw"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["HouseWorstCaseTempF"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["RoomTempF"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + ###################################### + # Behavior on incorrect types + ###################################### + + d2 = dict(d, StorageSteps="100.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, FloSlices="48.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, SliceDurationMinutes="60.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, CurrencyUnitGtEnumSymbol="hi") + Maker.dict_to_tuple(d2).CurrencyUnit = RecognizedCurrencyUnit.default() + + d2 = dict(d, TariffGtEnumSymbol="hi") + Maker.dict_to_tuple(d2).Tariff = DistributionTariff.default() + + d2 = dict(d, EnergyTypeGtEnumSymbol="hi") + Maker.dict_to_tuple(d2).EnergyType = EnergySupplyType.default() + + d2 = dict(d, StandardOfferPriceDollarsPerMwh="110.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, DistributionTariffDollarsPerMwh="113.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, StoreSizeGallons="240.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, MaxStoreTempF="210.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, ElementMaxPowerKw="this is not a float") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, RequiredSourceWaterTempF="120.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, FixedPumpGpm="this is not a float") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, ReturnWaterFixedDeltaT="20.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, AnnualHvacKwhTh="25000.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, AmbientPowerInKw="this is not a float") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, HouseWorstCaseTempF="-7.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, RoomTempF="68.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if TypeName is incorrect + ###################################### + + d2 = dict(d, TypeName="not the type alias") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if primitive attributes do not have appropriate property_format + ###################################### + + d2 = dict(d, GNodeAlias="a.b-h") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + # End of Test diff --git a/tests/types/test_flo_params_brickstorageheater.py b/tests/types/test_flo_params_brickstorageheater.py index ab1f7b2..46b74a5 100644 --- a/tests/types/test_flo_params_brickstorageheater.py +++ b/tests/types/test_flo_params_brickstorageheater.py @@ -172,6 +172,11 @@ def test_flo_params_brickstorageheater_generated() -> None: with pytest.raises(SchemaError): Maker.dict_to_tuple(d2) + d2 = dict(d) + del d2["C"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + d2 = dict(d) del d2["RealtimeElectricityPrice"] with pytest.raises(SchemaError): @@ -202,6 +207,11 @@ def test_flo_params_brickstorageheater_generated() -> None: with pytest.raises(SchemaError): Maker.dict_to_tuple(d2) + d2 = dict(d) + del d2["DistPriceUid"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + d2 = dict(d) del d2["StartYearUtc"] with pytest.raises(SchemaError): @@ -256,16 +266,6 @@ def test_flo_params_brickstorageheater_generated() -> None: # Optional attributes can be removed from type ###################################### - d2 = dict(d) - if "C" in d2.keys(): - del d2["C"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "DistPriceUid" in d2.keys(): - del d2["DistPriceUid"] - Maker.dict_to_tuple(d2) - d2 = dict(d) if "RegPriceUid" in d2.keys(): del d2["RegPriceUid"] diff --git a/tests/types/test_flo_params_simpleresistivehydronic.py b/tests/types/test_flo_params_simpleresistivehydronic.py new file mode 100644 index 0000000..8b63722 --- /dev/null +++ b/tests/types/test_flo_params_simpleresistivehydronic.py @@ -0,0 +1,296 @@ +"""Tests flo.params.simpleresistivehydronic type, version 000""" +import json + +import pytest +from gridworks.errors import SchemaError +from pydantic import ValidationError + +from gwatn.enums import RecognizedCurrencyUnit +from gwatn.types import FloParamsSimpleresistivehydronic_Maker as Maker + + +def test_flo_params_simpleresistivehydronic_generated() -> None: + d = { + "GNodeAlias": "d1.isone.ver.keene.holly", + "FloParamsUid": "97eba574-bd20-45b5-bf82-9ba2f492d8f6", + "HomeCity": "MILLINOCKET_ME", + "TimezoneString": "US/Eastern", + "StartYearUtc": 2020, + "StartMonthUtc": 1, + "StartDayUtc": 1, + "StartHourUtc": 0, + "StartMinuteUtc": 0, + "StoreSizeGallons": 240, + "MaxStoreTempF": 190, + "ElementMaxPowerKw": 9.5, + "RequiredSourceWaterTempF": 120, + "FixedPumpGpm": 4.5, + "ReturnWaterFixedDeltaT": 20, + "SliceDurationMinutes": [60], + "PowerLostFromHouseKwList": [3.42], + "OutsideTempF": [-5.1], + "DistributionPrice": [40.0], + "RealtimeElectricityPrice": [10.35], + "RtElecPriceUid": "bd2ec5c5-40b9-4b61-ad1b-4613370246d6", + "WeatherUid": "3bbcb552-52e3-4b86-84e0-084959f9fc0f", + "DistPriceUid": "b91ef8e7-50d7-4587-bf13-a3af7ecdb83a", + "CurrencyUnitGtEnumSymbol": "e57c5143", + "TypeName": "flo.params.simpleresistivehydronic", + "Version": "000", + } + + with pytest.raises(SchemaError): + Maker.type_to_tuple(d) + + with pytest.raises(SchemaError): + Maker.type_to_tuple('"not a dict"') + + # Test type_to_tuple + gtype = json.dumps(d) + gtuple = Maker.type_to_tuple(gtype) + + # test type_to_tuple and tuple_to_type maps + assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple + + # test Maker init + t = Maker( + g_node_alias=gtuple.GNodeAlias, + flo_params_uid=gtuple.FloParamsUid, + home_city=gtuple.HomeCity, + timezone_string=gtuple.TimezoneString, + start_year_utc=gtuple.StartYearUtc, + start_month_utc=gtuple.StartMonthUtc, + start_day_utc=gtuple.StartDayUtc, + start_hour_utc=gtuple.StartHourUtc, + start_minute_utc=gtuple.StartMinuteUtc, + store_size_gallons=gtuple.StoreSizeGallons, + max_store_temp_f=gtuple.MaxStoreTempF, + element_max_power_kw=gtuple.ElementMaxPowerKw, + required_source_water_temp_f=gtuple.RequiredSourceWaterTempF, + fixed_pump_gpm=gtuple.FixedPumpGpm, + return_water_fixed_delta_t=gtuple.ReturnWaterFixedDeltaT, + slice_duration_minutes=gtuple.SliceDurationMinutes, + power_lost_from_house_kw_list=gtuple.PowerLostFromHouseKwList, + outside_temp_f=gtuple.OutsideTempF, + distribution_price=gtuple.DistributionPrice, + realtime_electricity_price=gtuple.RealtimeElectricityPrice, + rt_elec_price_uid=gtuple.RtElecPriceUid, + weather_uid=gtuple.WeatherUid, + dist_price_uid=gtuple.DistPriceUid, + currency_unit=gtuple.CurrencyUnit, + ).tuple + assert t == gtuple + + ###################################### + # SchemaError raised if missing a required attribute + ###################################### + + d2 = dict(d) + del d2["TypeName"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["GNodeAlias"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["FloParamsUid"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["HomeCity"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["TimezoneString"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["StartYearUtc"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["StartMonthUtc"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["StartDayUtc"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["StartHourUtc"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["StartMinuteUtc"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["StoreSizeGallons"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["MaxStoreTempF"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["ElementMaxPowerKw"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["RequiredSourceWaterTempF"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["FixedPumpGpm"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["ReturnWaterFixedDeltaT"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["SliceDurationMinutes"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["PowerLostFromHouseKwList"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["OutsideTempF"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["DistributionPrice"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["RealtimeElectricityPrice"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["RtElecPriceUid"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["WeatherUid"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["DistPriceUid"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["CurrencyUnitGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + ###################################### + # Behavior on incorrect types + ###################################### + + d2 = dict(d, StartYearUtc="2020.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, StartMonthUtc="1.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, StartDayUtc="1.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, StartHourUtc="0.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, StartMinuteUtc="0.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, StoreSizeGallons="240.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, MaxStoreTempF="190.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, ElementMaxPowerKw="this is not a float") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, RequiredSourceWaterTempF="120.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, FixedPumpGpm="this is not a float") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, ReturnWaterFixedDeltaT="20.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, CurrencyUnitGtEnumSymbol="hi") + Maker.dict_to_tuple(d2).CurrencyUnit = RecognizedCurrencyUnit.default() + + ###################################### + # SchemaError raised if TypeName is incorrect + ###################################### + + d2 = dict(d, TypeName="not the type alias") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if primitive attributes do not have appropriate property_format + ###################################### + + d2 = dict(d, GNodeAlias="a.b-h") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, FloParamsUid="d4be12d5-33ba-4f1f-b9e5") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, RtElecPriceUid="d4be12d5-33ba-4f1f-b9e5") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, WeatherUid="d4be12d5-33ba-4f1f-b9e5") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, DistPriceUid="d4be12d5-33ba-4f1f-b9e5") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + # End of Test