{"openapi":"3.1.0","info":{"title":"Battery Storage Service","description":"Battery dispatch, ageing and investment analysis for Swiss buildings. Self-consumption and self-sufficiency per Luthander et al. (2015); cycle counting per ASTM E1049-85; economic evaluation by the annuity method of SIA 480:2016; storage cost per Swissolar (2025) and Schmidt et al. (2019). GET /api/v1/battery/references returns the full bibliography.","version":"0.1.0"},"paths":{"/api/v1/battery/analysis":{"post":{"summary":"Analyse battery storage for one or more Swiss buildings","description":"Dispatch a battery against each building's PV and load, then evaluate it.\n\nReturns, per building: the energy balance with self-consumption and\nself-sufficiency (Luthander et al. 2015), an ageing assessment from rainflow\ncycle counting of the state-of-charge trajectory (ASTM E1049-85, fade model\nstructured as in NREL BLAST-Lite), and an economic evaluation by the annuity\nmethod of SIA 480:2016 with both the simple Swiss storage cost (Swissolar 2025)\nand the discounted levelised cost of storage (Schmidt et al. 2019). Optionally\na sizing sweep and the break-even thresholds.\n\nSingle and multiple buildings use the same shape: a single building is a\none-element ``buildings`` list. For more than one building a ``portfolio``\nblock is added, either the sum over independent per-building batteries or one\nshared battery on the aggregated net load - see ``portfolio_mode``.\n\nAll model parameters have sourced defaults and may be overridden per request;\n``GET /api/v1/battery/references`` returns the full bibliography.","operationId":"battery_analysis_api_v1_battery_analysis_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatteryAnalysisRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatteryAnalysisResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"SEP bearer token":[]},{"SEP token (query)":[]}]}},"/api/v1/battery/references":{"get":{"summary":"The published sources behind every calculation in this service","description":"Return the full bibliography.\n\nDeliberately unauthenticated and always available: the methods this service\nuses are public standards and published papers, and a reviewer should be able\nto check them without a token.","operationId":"battery_references_api_v1_battery_references_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferencesResponse"}}}}}}},"/health":{"get":{"summary":"Health Check","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/ready":{"get":{"summary":"Readiness Check","description":"Readiness for a service with no outbound dependencies.\n\nThis service holds no clients, no cache and no warm state: PV and load series\narrive in the request body. Once the process is up it can serve, so readiness\nis a genuine statement rather than a placeholder.","operationId":"readiness_check_ready_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"BatteryAnalysisRequest":{"properties":{"buildings":{"items":{"$ref":"#/components/schemas/BuildingInput"},"type":"array","minItems":1,"title":"Buildings","description":"One or more buildings. A single building is a one-element list; the response shape is identical either way."},"battery":{"$ref":"#/components/schemas/BatteryInput"},"economics":{"$ref":"#/components/schemas/EconomicsInput"},"timestep_minutes":{"type":"integer","enum":[15,30,60],"title":"Timestep Minutes","description":"Resolution of the supplied series.","default":15},"portfolio_mode":{"type":"string","enum":["per_building","shared"],"title":"Portfolio Mode","description":"per_building: each building gets its own battery, portfolio totals are the sum. shared: one battery on the aggregated net load (ZEV / local electricity community). The two answer different questions and are not comparable.","default":"per_building"},"sizing_sweep_kwh":{"anyOf":[{"items":{"type":"number"},"type":"array"},{"type":"null"}],"title":"Sizing Sweep Kwh","description":"Candidate capacities to evaluate per building. Returns one economic result per candidate plus the capacity with the highest SIA 480 annuity."},"include_break_even":{"type":"boolean","title":"Include Break Even","description":"Include the thresholds at which the investment reaches break-even (battery price, retail tariff, subsidy). Cheap - the dispatch is reused.","default":true},"include_dispatch_series":{"type":"boolean","title":"Include Dispatch Series","description":"Include the full per-step charge/discharge/SoC series. Off by default: a year at 15-minute resolution adds roughly 5 MB of JSON per building.","default":false},"require_full_year":{"type":"boolean","title":"Require Full Year","description":"Reject windows shorter than a year for the economic evaluation rather than extrapolating them. Set false to get energy KPIs for a short window; the economics are then reported with extrapolated=true.","default":true}},"type":"object","required":["buildings"],"title":"BatteryAnalysisRequest","example":{"battery":{"capacity_kwh":10.0},"buildings":[{"address_id":"30106230","egid":"350492","load_kw":[0.4,0.4],"pv_kw":[0.0,0.0]}],"economics":{"buy_price_chf_per_kwh":0.2544,"feed_in_chf_per_kwh":0.0783},"portfolio_mode":"per_building","require_full_year":false,"sizing_sweep_kwh":[5,7.5,10,12.5,15],"timestep_minutes":15}},"BatteryAnalysisResponse":{"properties":{"success":{"type":"boolean","title":"Success","description":"True when at least one building succeeded. Check each entry's own flag."},"buildings":{"items":{"$ref":"#/components/schemas/BuildingBatteryResult"},"type":"array","title":"Buildings"},"portfolio":{"anyOf":[{"$ref":"#/components/schemas/PortfolioResponse"},{"type":"null"}],"description":"Present only for multi-building requests."},"metadata":{"$ref":"#/components/schemas/BatteryMetadata"}},"additionalProperties":true,"type":"object","required":["success","buildings","metadata"],"title":"BatteryAnalysisResponse"},"BatteryInput":{"properties":{"capacity_kwh":{"type":"number","maximum":1000.0,"exclusiveMinimum":0.0,"title":"Capacity Kwh","description":"Nominal (rated) capacity.","default":10.0},"power_kw":{"anyOf":[{"type":"number","exclusiveMinimum":0.0},{"type":"null"}],"title":"Power Kw","description":"Inverter power. Defaults to capacity_kwh * power_to_energy_ratio."},"power_to_energy_ratio":{"type":"number","maximum":4.0,"exclusiveMinimum":0.0,"title":"Power To Energy Ratio","description":"C-rate used when power_kw is not given. Default 0.5 (10 kWh -> 5 kW).","default":0.5},"round_trip_efficiency":{"type":"number","maximum":1.0,"exclusiveMinimum":0.0,"title":"Round Trip Efficiency","description":"AC-to-AC system round-trip efficiency as defined by the BVES/BSW Effizienzleitfaden - the whole system, not the cell.","default":0.9},"usable_depth_of_discharge":{"type":"number","maximum":1.0,"exclusiveMinimum":0.0,"title":"Usable Depth Of Discharge","description":"Usable fraction of rated capacity (IEC 62933-2-1 usable energy).","default":0.9},"standby_power_w":{"type":"number","maximum":1000.0,"minimum":0.0,"title":"Standby Power W","description":"Standby consumption of the storage system, added to the load.","default":5.0}},"type":"object","title":"BatteryInput","description":"Battery specification. All defaults sourced - see app/core/constants.py."},"BatteryMetadata":{"properties":{"request_time":{"type":"string","format":"date-time","title":"Request Time"},"processing_time_seconds":{"type":"number","title":"Processing Time Seconds"},"total_buildings":{"type":"integer","title":"Total Buildings"},"successful_buildings":{"type":"integer","title":"Successful Buildings"},"failed_buildings":{"type":"integer","title":"Failed Buildings"},"timestep_minutes":{"type":"integer","title":"Timestep Minutes"},"window_hours":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Window Hours"},"extrapolated":{"type":"boolean","title":"Extrapolated","description":"True when economic results were scaled from a window shorter than a year. Seasonal bias makes such figures indicative only.","default":false},"references":{"items":{"$ref":"#/components/schemas/ReferenceResponse"},"type":"array","title":"References","description":"Published sources for every method used to produce this response."}},"type":"object","required":["request_time","processing_time_seconds","total_buildings","successful_buildings","failed_buildings","timestep_minutes"],"title":"BatteryMetadata"},"BreakEvenResponse":{"properties":{"battery_cost_chf_per_kwh":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Battery Cost Chf Per Kwh"},"buy_price_chf_per_kwh":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Buy Price Chf Per Kwh"},"feed_in_chf_per_kwh":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Feed In Chf Per Kwh"},"battery_subsidy_chf":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Battery Subsidy Chf"},"electricity_price_change_factor":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Electricity Price Change Factor"}},"type":"object","title":"BreakEvenResponse","description":"Value of each lever at which the total annuity reaches zero.\n\nnull means that lever cannot reach break-even within a plausible range."},"BuildingBatteryResult":{"properties":{"address_id":{"type":"string","title":"Address Id"},"egid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Egid"},"success":{"type":"boolean","title":"Success"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"nominal_capacity_kwh":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Nominal Capacity Kwh"},"power_kw":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Power Kw"},"energy":{"anyOf":[{"$ref":"#/components/schemas/EnergyKPIResponse"},{"type":"null"}]},"degradation":{"anyOf":[{"$ref":"#/components/schemas/DegradationResponse"},{"type":"null"}]},"economics":{"anyOf":[{"$ref":"#/components/schemas/EconomicsResponse"},{"type":"null"}]},"break_even":{"anyOf":[{"$ref":"#/components/schemas/BreakEvenResponse"},{"type":"null"}]},"sizing":{"anyOf":[{"$ref":"#/components/schemas/SizingResponse"},{"type":"null"}]},"dispatch":{"anyOf":[{"$ref":"#/components/schemas/DispatchSeriesResponse"},{"type":"null"}]},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings","description":"Consistency conflicts between the ageing model and the economic assumptions."}},"type":"object","required":["address_id","success"],"title":"BuildingBatteryResult"},"BuildingInput":{"properties":{"address_id":{"type":"string","title":"Address Id","description":"SEP address ID; echoed on every result, including failures."},"egid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Egid","description":"Federal building identifier. Used to de-duplicate addresses that share one physical building when aggregating a shared-battery portfolio."},"pv_kw":{"items":{"type":"number"},"type":"array","minItems":1,"title":"Pv Kw","description":"PV generation in kW per step, 15-minute resolution by default."},"load_kw":{"items":{"type":"number"},"type":"array","minItems":1,"title":"Load Kw","description":"Electricity demand in kW per step, same length and alignment as pv_kw."},"mean_temperature_c":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Mean Temperature C","description":"Mean ambient temperature over the window, in degrees Celsius. Drives the calendar-ageing temperature correction. In the SEP Multiload pipeline this is available from the weather service's temp_air without an extra fetch."}},"type":"object","required":["address_id","pv_kw","load_kw"],"title":"BuildingInput","description":"One building's time series. ``pv_kw`` and ``load_kw`` must be equal length."},"DegradationResponse":{"properties":{"equivalent_full_cycles_per_year":{"type":"number","title":"Equivalent Full Cycles Per Year"},"counted_cycles":{"type":"integer","title":"Counted Cycles"},"mean_cycle_depth":{"type":"number","title":"Mean Cycle Depth"},"calendar_fade_per_year":{"type":"number","title":"Calendar Fade Per Year"},"cyclic_fade_per_year":{"type":"number","title":"Cyclic Fade Per Year"},"total_fade_per_year":{"type":"number","title":"Total Fade Per Year"},"years_to_end_of_life":{"type":"number","title":"Years To End Of Life"},"end_of_life_capacity_fraction":{"type":"number","title":"End Of Life Capacity Fraction"},"cycle_depth_histogram":{"items":{"additionalProperties":{"type":"number"},"type":"object"},"type":"array","title":"Cycle Depth Histogram"}},"type":"object","required":["equivalent_full_cycles_per_year","counted_cycles","mean_cycle_depth","calendar_fade_per_year","cyclic_fade_per_year","total_fade_per_year","years_to_end_of_life","end_of_life_capacity_fraction"],"title":"DegradationResponse","description":"Ageing assessment (ASTM_E1049 cycle counting, NREL_BLAST_LITE fade structure)."},"DispatchSeriesResponse":{"properties":{"charge_kw":{"items":{"type":"number"},"type":"array","title":"Charge Kw"},"discharge_kw":{"items":{"type":"number"},"type":"array","title":"Discharge Kw"},"soc_kwh":{"items":{"type":"number"},"type":"array","title":"Soc Kwh"},"grid_import_kw":{"items":{"type":"number"},"type":"array","title":"Grid Import Kw"},"grid_export_kw":{"items":{"type":"number"},"type":"array","title":"Grid Export Kw"}},"type":"object","required":["charge_kw","discharge_kw","soc_kwh","grid_import_kw","grid_export_kw"],"title":"DispatchSeriesResponse"},"EconomicsInput":{"properties":{"battery_cost_chf_per_kwh":{"type":"number","minimum":0.0,"title":"Battery Cost Chf Per Kwh","description":"Capacity-proportional installed cost. See constants.py on source quality.","default":640.0},"battery_fixed_cost_chf":{"type":"number","minimum":0.0,"title":"Battery Fixed Cost Chf","description":"Size-independent cost: inverter, BMS, electrical work, commissioning.","default":1300.0},"battery_subsidy_chf":{"type":"number","minimum":0.0,"title":"Battery Subsidy Chf","description":"One-off subsidy. Default 0: federal EIV covers PV, not storage.","default":0.0},"buy_price_chf_per_kwh":{"type":"number","minimum":0.0,"title":"Buy Price Chf Per Kwh","description":"Retail electricity price. Supply the building's ElCom value where known.","default":0.29},"feed_in_chf_per_kwh":{"type":"number","minimum":0.0,"title":"Feed In Chf Per Kwh","description":"Feed-in remuneration. The spread against buy_price is what a battery monetises.","default":0.08},"calculation_period_years":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Calculation Period Years","description":"SIA 480 calculation period (Betrachtungszeitraum).","default":15},"discount_rate":{"type":"number","maximum":0.5,"minimum":0.0,"title":"Discount Rate","description":"SIA 480 Kalkulationszinssatz (discount rate), real.","default":0.025},"electricity_price_change_factor":{"type":"number","maximum":2.0,"minimum":0.5,"title":"Electricity Price Change Factor","description":"SIA 480 / VDI 2067 price change factor r for electricity. 1.01 = +1%/year.","default":1.01},"feed_in_price_change_factor":{"type":"number","maximum":2.0,"minimum":0.5,"title":"Feed In Price Change Factor","description":"SIA 480 / VDI 2067 price change factor r for feed-in remuneration.","default":1.0},"operation_cost_fraction_of_capex":{"type":"number","maximum":0.2,"minimum":0.0,"title":"Operation Cost Fraction Of Capex","description":"Operation-bound costs per year as a fraction of investment.","default":0.005},"residual_value_chf":{"type":"number","minimum":0.0,"title":"Residual Value Chf","description":"Residual value at the end of the period. Default 0 - no established resale market.","default":0.0}},"type":"object","title":"EconomicsInput","description":"Economic assumptions for the SIA 480:2016 annuity evaluation."},"EconomicsResponse":{"properties":{"capex_chf":{"type":"number","title":"Capex Chf"},"annual_saving_year1_chf":{"type":"number","title":"Annual Saving Year1 Chf"},"annuity_capital_chf_per_year":{"type":"number","title":"Annuity Capital Chf Per Year","description":"A_N,K - capital-bound annuity."},"annuity_operation_chf_per_year":{"type":"number","title":"Annuity Operation Chf Per Year","description":"A_N,B - operation-bound annuity."},"annuity_proceeds_chf_per_year":{"type":"number","title":"Annuity Proceeds Chf Per Year","description":"A_N,E - proceeds annuity."},"total_annuity_chf_per_year":{"type":"number","title":"Total Annuity Chf Per Year","description":"A_N. Positive means economically advantageous - SIA 480's criterion."},"net_present_value_chf":{"type":"number","title":"Net Present Value Chf"},"simple_payback_years":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Simple Payback Years"},"discounted_payback_years":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Discounted Payback Years","description":"null when the investment does not pay back within the period."},"internal_rate_of_return_percent":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Internal Rate Of Return Percent"},"lcos_chf_per_kwh":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lcos Chf Per Kwh","description":"Discounted levelised cost of storage per Schmidt et al. (2019), including the opportunity cost of charging. Compare against tariff_spread_chf_per_kwh. Use this for an investment decision."},"simple_storage_cost_chf_per_kwh":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Simple Storage Cost Chf Per Kwh","description":"Undiscounted CAPEX over lifetime throughput, the figure Swissolar publishes for the Swiss market. Quote this for comparability with Swiss industry material; it excludes discounting, charging cost and capacity fade."},"tariff_spread_chf_per_kwh":{"type":"number","title":"Tariff Spread Chf Per Kwh","description":"buy - feed-in. The entire economic mechanism under a flat tariff."},"economically_viable":{"type":"boolean","title":"Economically Viable"},"method":{"type":"string","title":"Method"}},"type":"object","required":["capex_chf","annual_saving_year1_chf","annuity_capital_chf_per_year","annuity_operation_chf_per_year","annuity_proceeds_chf_per_year","total_annuity_chf_per_year","net_present_value_chf","simple_payback_years","internal_rate_of_return_percent","tariff_spread_chf_per_kwh","economically_viable","method"],"title":"EconomicsResponse","description":"Economic evaluation (SIA_480 annuity method; LCOS per SCHMIDT_2019)."},"EnergyKPIResponse":{"properties":{"pv_generation_kwh":{"type":"number","title":"Pv Generation Kwh"},"load_kwh":{"type":"number","title":"Load Kwh"},"direct_self_consumption_kwh":{"type":"number","title":"Direct Self Consumption Kwh","description":"min(PV, load) summed - the instantaneous match, without the battery."},"battery_discharge_to_load_kwh":{"type":"number","title":"Battery Discharge To Load Kwh"},"self_consumption_kwh":{"type":"number","title":"Self Consumption Kwh","description":"Direct match plus battery discharge."},"grid_import_kwh":{"type":"number","title":"Grid Import Kwh"},"grid_export_kwh":{"type":"number","title":"Grid Export Kwh"},"battery_charge_kwh":{"type":"number","title":"Battery Charge Kwh"},"battery_losses_kwh":{"type":"number","title":"Battery Losses Kwh"},"self_consumption_rate_percent":{"type":"number","title":"Self Consumption Rate Percent","description":"Eigenverbrauchsquote (SCR)."},"self_sufficiency_rate_percent":{"type":"number","title":"Self Sufficiency Rate Percent","description":"Autarkiegrad (SSR)."},"window_hours":{"type":"number","title":"Window Hours"}},"type":"object","required":["pv_generation_kwh","load_kwh","direct_self_consumption_kwh","battery_discharge_to_load_kwh","self_consumption_kwh","grid_import_kwh","grid_export_kwh","battery_charge_kwh","battery_losses_kwh","self_consumption_rate_percent","self_sufficiency_rate_percent","window_hours"],"title":"EnergyKPIResponse","description":"Energy balance over the evaluated window (LUTHANDER_2015 definitions)."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"PortfolioResponse":{"properties":{"mode":{"type":"string","enum":["per_building","shared"],"title":"Mode"},"building_count":{"type":"integer","title":"Building Count"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"duplicate_addresses_removed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duplicate Addresses Removed"},"building_egids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Building Egids"}},"additionalProperties":true,"type":"object","required":["mode","building_count"],"title":"PortfolioResponse"},"ReferenceResponse":{"properties":{"key":{"type":"string","title":"Key"},"citation":{"type":"string","title":"Citation"},"url":{"type":"string","title":"Url"},"used_for":{"type":"string","title":"Used For"}},"type":"object","required":["key","citation","url","used_for"],"title":"ReferenceResponse"},"ReferencesResponse":{"properties":{"references":{"items":{"$ref":"#/components/schemas/ReferenceResponse"},"type":"array","title":"References"},"note":{"type":"string","title":"Note","default":"Every formula and default in this service is taken from one of these sources. A value without a reference does not belong in the service."}},"type":"object","required":["references"],"title":"ReferencesResponse","description":"The full bibliography behind this service."},"SizingCandidate":{"properties":{"nominal_capacity_kwh":{"type":"number","title":"Nominal Capacity Kwh"},"power_kw":{"type":"number","title":"Power Kw"},"energy":{"$ref":"#/components/schemas/EnergyKPIResponse"},"degradation":{"$ref":"#/components/schemas/DegradationResponse"},"economics":{"$ref":"#/components/schemas/EconomicsResponse"},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings"}},"type":"object","required":["nominal_capacity_kwh","power_kw","energy","degradation","economics"],"title":"SizingCandidate"},"SizingResponse":{"properties":{"candidates":{"items":{"$ref":"#/components/schemas/SizingCandidate"},"type":"array","title":"Candidates"},"optimal_capacity_kwh":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Optimal Capacity Kwh"},"criterion":{"type":"string","title":"Criterion"},"any_candidate_viable":{"type":"boolean","title":"Any Candidate Viable","description":"False means no swept size pays back - the optimum is only the least bad."}},"type":"object","required":["candidates","optimal_capacity_kwh","criterion","any_candidate_viable"],"title":"SizingResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"SEP bearer token":{"type":"http","description":"SEP-issued JWT as `Authorization: Bearer <token>`.","scheme":"bearer"},"SEP token (query)":{"type":"apiKey","description":"SEP-issued JWT as `?jwt=<token>` (used by the frontend on first load).","in":"query","name":"jwt"}}}}