Step 6 - Generate a Storage-in-Switch Savings Analysis
In this step, we’ll pass a new storageProviderProfileId
property input to the savings analysis endpoint to reference the profiles that were created in Step 5 and apply them to properly simulate the effects of storage on savings.
The value of this property should be set to the providerProfileId
sent in the storage profile creation step. When passed to the SA, as in the example below, Switch will run a series of validations to ensure the optimized profiles exist and include all necessary inputs and then assign them to the SA scenarios defined by the user.
The user can, however, override the storage profile values by passing an explicit key to the Savings Analysis. For example, the Savings Analysis uses the following default hierarchy to determine what tariff to use in the calculations:
- explicit ‘after’ mtid passed in the SA
- storage profile mtid
- Post solar mtid set on the account
- solar id associated with the zip on the account (if mitd is not set on the account or is a non-solar tariff)
Input notes:
Our savings projection method for lifetime storage depends strongly on how the battery’s capacity degrades each year. Lifetime calculations with storage support two conventions for capacity fade:
- Percent-based (compounding) degradation: battery capacity will reduce by the specified percentage, year over year.
"batteryDegradation": { "percentPerYear": 3 }
to indicate a 3%-per-year - Linear degradation: battery capacity will degrade by the absolute value specified, each year.
"batteryDegradation": { "kwhPerYear": 0.1 }
to indicate a 0.1 kWh-per-year
By default, we assume that the capacity starts at the value of batteryNameplateCapacity
you passed during storage profile creation and decays by 2% per year.
Example call with 3% battery degradation YoY:
POST /rest/v1/accounts/analysis
{
"providerAccountId": "sins-example-jun23",
"fromDateTime": "2023-05-01T00:00:00-07:00", // important: pass timezone!
"toDateTime": "2024-05-01T00:00:00-07:00",
"propertyInputs": [
{
"scenarios": "before",
"keyName": "masterTariffId",
"dataValue": "522" // no need to pass after tariff; it's already saved
},
{
"scenarios": "before", // important: don't pass "after" here
"keyName": "providerProfileId", //the original usage profile
"dataValue": "sins-example-jun23-bills"
},
{
"scenarios": "after,solar",
"keyName": "storageProviderProfileId",
"dataValue": "sins-example-jun23-poststorage"
},
{
"scenarios":"solar,after,before",
"keyName": "projectDuration",
"dataValue": 10
}
],
"batteryDegradation": { "percentPerYear": 3 }
}
Note the differences between this savings analysis request and the corresponding request to show savings from solar alone:
{
"providerAccountId": "sins-example-jun23",
"fromDateTime": "2023-05-01T00:00:00-07:00",
"toDateTime": "2024-05-01T00:00:00-07:00",
"propertyInputs": [
{
"scenarios": "before",
"keyName": "masterTariffId",
"dataValue": "522"
},
{
"scenarios": "after",
"keyName": "masterTariffId",
"dataValue": "3424821" // must pass after tariff in normal solar case
},
{
"scenarios": "before,after", // initial usage both before and after
"keyName": "providerProfileId",
"dataValue": "sins-example-jun23-bills"
},
{
"scenarios": "after,solar",
"keyName": "providerProfileId",
// here we specify the base solar profile,
// not the adjusted poststorage-solar profile:
"dataValue": "sins-example-jun23-solar"
},
{
"scenarios":"solar,after,before",
"keyName": "projectDuration",
"dataValue": 10
}
]
}
Response notes:
Savings Analyses with more than one-year duration will contain two new series and summary parameters representing the avoided costs attributable directly to solar and to storage, respectively. These parameters can help you quickly understand how much value storage is adding on top of solar.
At the summary
level, these fields are reported under the following two fields:
lifetimeAvoidedCostSolarAndRateChange
lifetimeAvoidedCostStorageOnly
Note, the sum of the two should equal lifetimeAvoidedCost
.
{
"status": "success",
"count": 1,
"type": "AccountAnalysis",
"results": [
{
"designId": null,
"dataStatus": 2,
"currency": "USD",
"summary": {
"lifeTimeUtilityAfterCost": 3710.174779,
"lifeTimeUtilityAvoidedRate": 0.295616,
"lifetimeAvoidedCost": 22265.625221,
"lifetimeAvoidedCostSolarAndRateChange": 13463.303221,
"lifetimeAvoidedCostStorageOnly": 8802.322,
...
},
...]
}
At the series
level, these fields are given in the final two series, which might look something like this:
...
{
"seriesId": 13,
"fromDateTime": "2023-01-01T00:00:00-08:00",
"toDateTime": "2033-01-01T00:00:00-08:00",
"scenario": "storageOnlySavings",
"displayLabel": "Storage Contribution to Total Savings (Annual/Lifetime)",
"seriesPeriod": "YEAR",
"seriesDuration": 10,
"designId": null,
"key": null,
"cost": 8802.322
},
{
"seriesId": 14,
"fromDateTime": "2023-01-01T00:00:00-08:00",
"toDateTime": "2033-01-01T00:00:00-08:00",
"scenario": "solarOnlySavings",
"displayLabel": "Solar Contribution to Total Savings (Annual/Lifetime)",
"seriesPeriod": "YEAR",
"seriesDuration": 10,
"designId": null,
"key": null,
"cost": 13463.303221
},
...
How are Approximations for qty and rate in AFTER series Calculated?
Most of the series
summaries and seriesData
yearly details in a lifetime response contain the three fields:
cost
: the projected average cost of the electricityqty
: the projected total number of units of electricity purchased over the intervalrate
: the projected average price of electricity purchased over the interval
For solar-only analyses, cost is computed from quantity and rate (cost
= qty
* rate
)for each year of the calculation.
However, our savings projection for solar + storage projects the cost savings directly. Because we expect both the total quantity and average rate to change over time with the introduction of storage (due to arbitrage and peak-shifting), we don’t use qty
and rate
in the computation. Instead, we approximate these values going backward from our estimated cost
, first computing the yearly storage AFTER rate
by interpolating between the year-1 storage value and the yearly solar value (starting at the year-1 storage value but decaying to the yearly solar value as the battery capacity degrades), then finally inferring qty
as cost/rate
.
Finally, we use these inferred qty
values to estimate lifeTimeUtilityAvoidedRate
in the top-level summary
.
SA Example: Handling consumption-based solar costs
Savings analyses can model costs for purchasing power agreement (PPA) solar installations using “CONSUMPTION_BASED” charge types (see documentation example). For these installations, customers can be charged per kWh of solar-generated by the panel (rather than per kWh exported to the grid).
In such cases, omitting the original solar profile and just passing "after,solar"
for the storage, scenarios will lead to potential undercounting of costs; because some of the generated solar will be used to power the home or get stored in the battery, the post-storage solar profile will have smaller kWh values than the original solar profile.
To fix this, you can remove solar
from the list of storage scenarios, and separately pass the original solar profile with just solar
as its scenario:
"propertyInputs": [
{
"scenarios": "before",
"keyName": "masterTariffId",
"dataValue": "522"
},
{
"scenarios": "before",
"keyName": "providerProfileId",
"dataValue": "sins-example-jun23-bills"
},
{
"scenarios": "solar", // passing this separately from after
"keyName": "providerProfileId",
"dataValue": "sins-example-jun23-solar"
},
{
"scenarios": "after", // after just includes storage here
"keyName": "storageProviderProfileId",
"dataValue": "sins-example-jun23-poststorage"
},
{
"scenarios":"solar,after,before",
"keyName": "projectDuration",
"dataValue": 1
}
]
}
Appendix
More verbose, non-declarative storage workflow
As mentioned before, under the hood, the storage profile creation endpoint actually creates three profiles:
- Usage: an updated consumption profile representing remaining site consumption not covered by solar generation or storage discharge. This profile’s kWh values should be less than or equal to the original consumption kWh values at all timesteps.
- Solar: an updated solar profile representing solar exports to the grid. This profile’s kWh values should also be less than or equal to the original solar kWh values at all timesteps, since some of the solar goes to charge the battery or meet site consumption.
- Battery: a storage-specific profile representing grid imports and exports directly attributable to the battery. This profile can have both positive and negative values, with positive values representing kWh purchased from the grid to charge the battery and negative values representing kWh sold back to the grid from the battery. (Note that if
allowGridToBattery
andallowBatteryToGrid
are both set tofalse
, this profile should have all 0 for all kWh values.)
If the providerProfileId
passed during profile creation was "sins-example-jun23-poststorage",
then these profiles will have provider profile IDs of "sins-example-jun23-poststorage-usage"
, "sins-example-jun23-poststorage-solar"
, and "sins-example-jun23-poststorage-storage".
As such, instead of passing the "sins-example-jun23-poststorage"
family name to storageProviderProfileId
during savings analyses, it is possible (though more verbose) to construct a savings analysis request by referencing each of these profiles individually using their providerProfileId, along with the after tariff:
"propertyInputs": [
{
"scenarios": "before",
"keyName": "masterTariffId",
"dataValue": "522"
},
{
"scenarios": "after",
"keyName": "masterTariffId",
"dataValue": "3424821"
},
{
"scenarios": "before",
"keyName": "providerProfileId",
"dataValue": "sins-example-jun23-bills"
},
{
"scenarios": "after",
"keyName": "providerProfileId",
"dataValue": "sins-example-jun23-poststorage-usage"
},
{
"scenarios": "after,solar",
"keyName": "providerProfileId",
"dataValue": "sins-example-jun23-poststorage-solar"
},
{
"scenarios": "after",
"keyName": "providerProfileId",
"dataValue": "sins-example-jun23-poststorage-storage"
},
{
"scenarios":"solar,after,before",
"keyName": "projectDuration",
"dataValue": 1
}
]
}
This workflow is no longer recommended because it is unnecessarily verbose and more error-prone for users, but we include it here to provide a clearer sense of what is happening under the hood when running savings analyses or account calculations over storage profiles.
Account Cost Calculations on Storage Profiles
The account cost calculator can be used to determine granular costs for storage calculations. For example, it can be used to determine the cost of electricity at specific times, such as 12 PM on June 13th.
Example Request:
{
"fromDateTime": "2024-06-01T00:00:00",
"toDateTime": "2025-06-01T00:00:00",
"includeDefaultProfile": "false",
"minimums": "true",
"detailLevel": "CHARGE_TYPE",
"groupBy": "HOUR",
"fields": "EXT",
"autoBaseline": true,
"useIntelligentBaselining": true,
"propertyInputs": [
{
"keyName": "storageProviderProfileId",
"dataType": "STRING",
"dataValue": "Storage-profile-7-9" // only pass the id for the storage profile here
}
]
}
Notes:
- The account cost calculator will use the tariff set on the storage profile, regardless of the MTID you pass into the account cost calculator.
- When running an account cost calculation for storage, you must only include the storage provider profile ID in your request.
Any of the following detail levels are supported for account cost calculations with storage profiles. However,CHARGE_TYPE_AND_TOU
is not compatible with NEM3 tariffs.
Detail Level Value | Description |
---|---|
TOTAL | Return only the overall total, without any item breakdown. |
CHARGE_TYPE | Group the rates by charge type, such as FIXED, CONSUMPTION, QUANTITY. |
CHARGE_TYPE_AND_TOU | Group the rates by charge type, quantity type, season, tariff version, time of use, and tiers. NOT COMPATIBLE WITH NEM3 TARIFFS |
RATE | Group the items by rates. |
ALL | Group the calculation results by distinct calculation interval (full details). |
Updated 4 months ago