{
  "openapi": "3.1.0",
  "info": {
    "title": "MicrogridModeler Simulation API",
    "version": "2026-09-25",
    "description": "Public reproducible 8,760-hour techno-economic and resilience simulation endpoint for PV, wind, battery, diesel, and optional grid-connected microgrids. Requests are validated against this document: unknown properties, out-of-range values, non-finite numbers and wrong types are rejected with HTTP 422 and RFC 6901 JSON-pointer errors. Resilience and disturbance studies are limited by a published compute budget (x-compute-budget) and requests are rate limited per client (HTTP 429 with Retry-After). See https://www.microgridmodeler.com/methodology for equations, assumptions, and model boundaries.",
    "x-compute-budget": {
      "maxIslandHourSteps": 30000000,
      "outageSteps": "ceil(8760 / startHourStride) × max(outageDurations)",
      "disturbanceSteps": "1.25 × runs(1 if deterministic) × (1 + min(maxShiftSamples, localShiftHours + 1) + min(maxShiftSamples, 8760)) × horizonHours × (1 + expectedFailedUnits / 30)",
      "calibration": "Measured 2026-09-24 on the reference engine: ~4.6 M outage island-hours/s, ~3.7 M disturbance island-hours/s; each failed unit adds ~1/33 of an island-hour per step. 30 M steps ≈ 7 s."
    },
    "x-lifecycle-limit": "Component lifetimes must leave at most 10,000 replacements per component over projectYears (bound: projectYears / life, with a battery cycling at its full charge/discharge power every hour and a genset running every hour). Shorter lifetimes are rejected with 422 at the offending field (e.g. /project/design/converter/lifetimeYears); realistic projects need fewer than 100.",
    "x-api-limit": "Schema keywords listed in a property's x-api-limit array (e.g. [\"maximum\"]) are compute caps of this public endpoint, not physical limits. The API enforces them; the web app's own imports (share links, saved projects, autosaves) do not, because the in-browser engine runs larger studies.",
    "x-rate-limit": {
      "requestsPerMinutePerClient": 20,
      "burst": 20,
      "scope": "per server instance (defense in depth; an edge firewall rule may apply a stricter limit)"
    }
  },
  "servers": [{"url": "https://www.microgridmodeler.com"}],
  "paths": {
    "/api/v1/simulate": {
      "options": {"summary": "CORS preflight", "responses": {"204": {"description": "CORS headers"}}},
      "post": {
        "operationId": "simulateMicrogrid",
        "summary": "Run a reproducible chronological microgrid simulation",
        "description": "Provide exactly one of benchmarkKey or project. Aggregate technical, economic, and requested resilience results are returned by default; set includeHourly=true to include the complete year-one 8,760-hour dispatch trace. Seeded stochastic disturbance runs are exactly repeatable. Invalid requests return 422 VALIDATION_ERROR with JSON-pointer errors; studies above the compute budget return 422 COMPUTE_BUDGET_EXCEEDED with the computed cost; a result containing a non-finite number is never returned as success (500 NON_FINITE_RESULT).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/SimulationRequest"},
              "examples": {"benchmark": {"value": {"benchmarkKey": "clinic-small", "includeHourly": false}}}
            }
          }
        },
        "responses": {
          "200": {
            "description": "Simulation completed",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SimulationResponse"}}}
          },
          "400": {"$ref": "#/components/responses/Error"},
          "404": {"$ref": "#/components/responses/Error"},
          "405": {"$ref": "#/components/responses/Error"},
          "413": {"$ref": "#/components/responses/Error"},
          "415": {"$ref": "#/components/responses/Error"},
          "422": {"$ref": "#/components/responses/Error"},
          "429": {"$ref": "#/components/responses/Error"},
          "500": {"$ref": "#/components/responses/Error"}
        }
      }
    }
  },
  "components": {
    "responses": {
      "Error": {
        "description": "Request, validation, budget, rate-limit or simulation error",
        "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
      }
    },
    "schemas": {
      "SimulationRequest": {
        "type": "object",
        "properties": {
          "benchmarkKey": {
            "type": "string",
            "enum": [
              "arctic-telecom",
              "desert-telecom",
              "clinic-small",
              "clinic-medium",
              "island-guesthouse",
              "island-resort",
              "rural-school",
              "boarding-school",
              "village-tiny",
              "village-small",
              "village-medium",
              "mine-camp-small",
              "mine-camp-medium",
              "cold-storage",
              "water-pumping",
              "fisheries-ice",
              "municipality",
              "construction-camp",
              "military-base",
              "island-feeder"
            ],
            "description": "Stable key from /benchmarks.json"
          },
          "project": {"$ref": "#/components/schemas/Project"},
          "includeHourly": {"type": "boolean", "default": false}
        },
        "oneOf": [
          {"required": ["benchmarkKey"], "not": {"required": ["project"]}},
          {"required": ["project"], "not": {"required": ["benchmarkKey"]}}
        ],
        "additionalProperties": false
      },
      "Project": {
        "type": "object",
        "required": ["id", "name", "site", "loadArchetype", "loadKw", "economics", "constraints", "design"],
        "properties": {
          "id": {"type": "string", "maxLength": 200},
          "name": {"type": "string", "maxLength": 200},
          "site": {"$ref": "#/components/schemas/Site"},
          "loadArchetype": {"type": "string", "maxLength": 100},
          "loadKw": {
            "type": "array",
            "minItems": 8760,
            "maxItems": 8760,
            "items": {"type": "number", "minimum": 0, "maximum": 10000000},
            "description": "Chronological hourly load in kW, January 1 00:00 through December 31 23:00; non-leap year."
          },
          "economics": {"$ref": "#/components/schemas/Economics"},
          "constraints": {"$ref": "#/components/schemas/Constraints"},
          "design": {"$ref": "#/components/schemas/SystemDesign"},
          "grid": {"$ref": "#/components/schemas/GridContext"},
          "resilience": {"$ref": "#/components/schemas/Resilience"},
          "loadProfile": {"$ref": "#/components/schemas/LoadProfile"}
        },
        "additionalProperties": false
      },
      "Site": {
        "type": "object",
        "required": ["name", "latitude", "longitude", "climate", "albedo", "utcOffset"],
        "properties": {
          "timeZone": {"type": "string", "maxLength": 64, "description": "IANA time zone (e.g. America/Denver)"},
          "elevationM": {"type": "number", "minimum": -500, "maximum": 9000, "description": "Site elevation, m"},
          "climatology": {"$ref": "#/components/schemas/SiteClimatology"},
          "climateUserSet": {"type": "boolean", "description": "True once the user picks the climate, so a relocation keeps it"},
          "name": {"type": "string", "maxLength": 200},
          "latitude": {"type": "number", "minimum": -90, "maximum": 90},
          "longitude": {"type": "number", "minimum": -180, "maximum": 180},
          "climate": {"type": "string", "enum": ["desert", "tropical", "temperate", "continental", "arctic", "maritime"]},
          "albedo": {"type": "number", "minimum": 0, "maximum": 1},
          "utcOffset": {"type": "number", "minimum": -14, "maximum": 14}
        },
        "additionalProperties": false
      },
      "SystemDesign": {
        "type": "object",
        "required": ["pv", "battery", "genset", "converter", "strategy"],
        "properties": {
          "pv": {"$ref": "#/components/schemas/Pv"},
          "wind": {"$ref": "#/components/schemas/Wind"},
          "battery": {"$ref": "#/components/schemas/Battery"},
          "genset": {"$ref": "#/components/schemas/Genset"},
          "converter": {"$ref": "#/components/schemas/Converter"},
          "strategy": {
            "type": "string",
            "description": "Selectable energy-management policy: forecast-aware battery-first, NPS favor-diesel, NPS legacy diesel-first, or NPS maintain-maximum-SOC.",
            "enum": ["load-following", "cycle-charging", "diesel-first", "soc-maintaining"]
          }
        },
        "additionalProperties": false
      },
      "Pv": {
        "type": "object",
        "required": ["ratedKw", "tiltDeg", "azimuthDeg", "derate", "tempCoeff", "noct", "degradation"],
        "properties": {
          "inverterEfficiency": {"type": "number", "minimum": 0.5, "maximum": 1, "description": "PVWatts inverter efficiency (default 0.96)"},
          "dcAcRatio": {"type": "number", "minimum": 0.5, "maximum": 3, "description": "DC nameplate / inverter AC rating (default 1.2)"},
          "mounting": {"type": "string", "enum": ["open-rack", "roof"]},
          "dcLossesPct": {"type": "object", "description": "Itemized DC losses, percent (PVWatts categories)", "properties": {"soiling": {"type": "number", "minimum": 0, "maximum": 100}, "shading": {"type": "number", "minimum": 0, "maximum": 100}, "snow": {"type": "number", "minimum": 0, "maximum": 100}, "mismatch": {"type": "number", "minimum": 0, "maximum": 100}, "wiring": {"type": "number", "minimum": 0, "maximum": 100}, "connections": {"type": "number", "minimum": 0, "maximum": 100}, "lightInducedDegradation": {"type": "number", "minimum": 0, "maximum": 100}, "nameplate": {"type": "number", "minimum": 0, "maximum": 100}, "age": {"type": "number", "minimum": 0, "maximum": 100}, "availability": {"type": "number", "minimum": 0, "maximum": 100}}, "additionalProperties": false},
          "orientationUserSet": {"type": "boolean"},
          "ratedKw": {"type": "number", "minimum": 0, "maximum": 10000000},
          "unitCount": {"type": "integer", "minimum": 1, "maximum": 10000, "default": 1, "x-api-limit": ["maximum"]},
          "tracking": {"type": "string", "enum": ["fixed", "dual-axis"], "default": "fixed"},
          "tiltDeg": {"type": "number", "minimum": 0, "maximum": 90},
          "azimuthDeg": {"type": "number", "minimum": 0, "maximum": 360},
          "derate": {"type": "number", "minimum": 0, "maximum": 1},
          "tempCoeff": {
            "type": "number",
            "minimum": -0.02,
            "maximum": 0.01,
            "description": "Fraction per °C (negative), e.g. -0.004"
          },
          "noct": {"type": "number", "minimum": 0, "maximum": 100, "description": "Nominal operating cell temperature, °C"},
          "degradation": {"type": "number", "minimum": 0, "maximum": 1}
        },
        "additionalProperties": false
      },
      "Wind": {
        "type": "object",
        "required": [
          "ratedKw",
          "sizeClass",
          "hubHeightM",
          "meanWindSpeedMs",
          "referenceHeightM",
          "shearExponent",
          "weibullK",
          "cutInMs",
          "ratedMs",
          "cutOutMs",
          "arrayLossFraction",
          "degradation"
        ],
        "properties": {
          "availabilityLossFraction": {"type": "number", "minimum": 0, "maximum": 1},
          "icingLossFraction": {"type": "number", "minimum": 0, "maximum": 1},
          "powerCurve": {"type": "object", "required": ["speedsMs", "powerPerKw", "source"], "properties": {"speedsMs": {"type": "array", "items": {"type": "number", "minimum": 0, "maximum": 100}, "minItems": 2, "maxItems": 200}, "powerPerKw": {"type": "array", "items": {"type": "number", "minimum": 0, "maximum": 1.5}, "minItems": 2, "maxItems": 200}, "source": {"type": "string", "maxLength": 500}, "label": {"type": "string", "maxLength": 200}}, "additionalProperties": false},
          "rotorDiameterM": {"type": "number", "minimum": 0, "maximum": 400},
          "terrain": {"type": "string", "enum": ["open-water", "open-grass", "crops-bushes", "trees-buildings-hills"]},
          "meanWindSource": {"type": "string", "enum": ["climate-default", "climatology-grid", "nasa-power-hourly", "wind-toolkit", "user"]},
          "ratedKw": {"type": "number", "minimum": 0, "maximum": 10000000},
          "unitCount": {"type": "integer", "minimum": 1, "maximum": 10000, "default": 1, "x-api-limit": ["maximum"]},
          "sizeClass": {"type": "string", "enum": ["residential", "commercial", "medium", "large"]},
          "hubHeightM": {"type": "number", "exclusiveMinimum": 0, "maximum": 300},
          "meanWindSpeedMs": {"type": "number", "minimum": 0, "maximum": 30},
          "referenceHeightM": {"type": "number", "exclusiveMinimum": 0, "maximum": 300},
          "shearExponent": {"type": "number", "minimum": 0, "maximum": 1},
          "weibullK": {"type": "number", "exclusiveMinimum": 0, "maximum": 10},
          "cutInMs": {"type": "number", "minimum": 0, "maximum": 50},
          "ratedMs": {"type": "number", "minimum": 0, "maximum": 50},
          "cutOutMs": {"type": "number", "minimum": 0, "maximum": 80},
          "arrayLossFraction": {"type": "number", "minimum": 0, "maximum": 1},
          "degradation": {"type": "number", "minimum": 0, "maximum": 1}
        },
        "additionalProperties": false
      },
      "Battery": {
        "type": "object",
        "required": ["capacityKwh", "maxChargeKw", "maxDischargeKw", "roundTripEff", "minSoc", "initialSoc", "cycleLife"],
        "properties": {
          "degradation": {"$ref": "#/components/schemas/BatteryDegradation"},
          "capacityKwh": {"type": "number", "minimum": 0, "maximum": 10000000},
          "unitCount": {"type": "integer", "minimum": 1, "maximum": 10000, "default": 1, "x-api-limit": ["maximum"]},
          "maxChargeKw": {"type": "number", "minimum": 0, "maximum": 10000000},
          "maxDischargeKw": {"type": "number", "minimum": 0, "maximum": 10000000},
          "roundTripEff": {"type": "number", "exclusiveMinimum": 0, "maximum": 1},
          "chargeEfficiency": {"type": "number", "exclusiveMinimum": 0, "maximum": 1},
          "dischargeEfficiency": {"type": "number", "exclusiveMinimum": 0, "maximum": 1},
          "minSoc": {"type": "number", "minimum": 0, "maximum": 1},
          "maxSoc": {"type": "number", "minimum": 0, "maximum": 1, "default": 1},
          "initialSoc": {"type": "number", "minimum": 0, "maximum": 1},
          "cycleLife": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 1000000,
            "description": "Full equivalent cycles before replacement (must be > 0)"
          }
        },
        "additionalProperties": false
      },
      "Genset": {
        "type": "object",
        "required": ["ratedKw", "minLoadRatio", "fuelInterceptLPerHrPerKw", "fuelSlopeLPerKwh", "lifetimeHours"],
        "properties": {
          "fuelCurveBasis": {"type": "string", "enum": ["size-class-default", "datasheet-fit", "user", "legacy-generic", "reopt-parity"]},
          "startCostPerStart": {"type": "number", "minimum": 0, "maximum": 1000000.0, "description": "Cost per unit start, $"},
          "minUpHours": {"type": "number", "minimum": 0, "maximum": 8760},
          "minDownHours": {"type": "number", "minimum": 0, "maximum": 8760},
          "maxStartsPerDay": {"type": "number", "minimum": 0, "maximum": 1440},
          "ratedKw": {"type": "number", "minimum": 0, "maximum": 10000000},
          "unitCount": {"type": "integer", "minimum": 1, "maximum": 10000, "default": 1, "x-api-limit": ["maximum"]},
          "minLoadRatio": {"type": "number", "minimum": 0, "maximum": 1},
          "fuelInterceptLPerHrPerKw": {"type": "number", "minimum": 0, "maximum": 1},
          "fuelSlopeLPerKwh": {"type": "number", "minimum": 0, "maximum": 2},
          "startupDelayHours": {"type": "number", "minimum": 0, "maximum": 168, "default": 0},
          "lifetimeHours": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 10000000,
            "description": "Operating hours per unit to major overhaul (must be > 0); replacement follows economics.gensetMaintenance"
          }
        },
        "additionalProperties": false
      },
      "Converter": {
        "type": "object",
        "required": ["ratedKw", "efficiency", "lifetimeYears"],
        "properties": {
          "coupling": {"type": "string", "enum": ["dc-shared", "ac-battery"]},
          "pvInverterAcKw": {"type": "number", "minimum": 0, "maximum": 10000000.0},
          "ratedKw": {"type": "number", "minimum": 0, "maximum": 10000000},
          "efficiency": {"type": "number", "exclusiveMinimum": 0, "maximum": 1},
          "lifetimeYears": {"type": "number", "exclusiveMinimum": 0, "maximum": 100, "description": "Must be > 0"}
        },
        "additionalProperties": false
      },
      "GridContext": {
        "type": "object",
        "required": ["tariff", "spec"],
        "properties": {
          "tariff": {"$ref": "#/components/schemas/Tariff"},
          "spec": {
            "type": "object",
            "required": ["canGridCharge", "maxImportKw", "maxExportKw", "netMetering"],
            "properties": {
              "backupReserveFraction": {"type": "number", "minimum": 0, "maximum": 1, "description": "Battery fraction held for outages in normal grid operation"},
              "demandForecast": {"type": "string", "enum": ["perfect-day-ahead", "persistence"]},
              "canGridCharge": {"type": "boolean"},
              "maxImportKw": {"type": "number", "minimum": 0, "maximum": 1000000000.0},
              "maxExportKw": {"type": "number", "minimum": 0, "maximum": 1000000000.0},
              "netMetering": {"type": "boolean"}
            },
            "additionalProperties": false
          }
        },
        "additionalProperties": false
      },
      "Resilience": {
        "type": "object",
        "required": ["criticalLoadFraction", "outageDurations", "startHourStride"],
        "properties": {
          "startSocConvention": {"type": "string", "enum": ["end-of-start-hour", "start-of-hour"]},
          "outagesPerYear": {"type": "number", "minimum": 0, "maximum": 365},
          "durationProbabilities": {"type": "array", "items": {"type": "number", "minimum": 0, "maximum": 1}, "maxItems": 100},
          "criticalLoadFraction": {"type": "number", "minimum": 0, "maximum": 1},
          "criticalLoadKw": {
            "type": "array",
            "minItems": 8760,
            "maxItems": 8760,
            "items": {"type": "number", "minimum": 0, "maximum": 10000000}
          },
          "outageDurations": {
            "type": "array",
            "minItems": 1,
            "items": {"type": "integer", "minimum": 1, "maximum": 8760},
            "maxItems": 16,
            "x-api-limit": ["maxItems"]
          },
          "startHourStride": {"type": "integer", "minimum": 1, "maximum": 8760},
          "fuelAvailableL": {"type": "number", "minimum": 0, "maximum": 1000000000.0},
          "vollPerKwh": {"type": "number", "minimum": 0, "maximum": 10000},
          "disturbance": {"$ref": "#/components/schemas/Disturbance"}
        },
        "additionalProperties": false
      },
      "Disturbance": {
        "type": "object",
        "required": ["mode", "seed", "runs", "startHour", "horizonHours", "localShiftHours", "maxShiftSamples", "assets"],
        "properties": {
          "mode": {"type": "string", "enum": ["deterministic", "stochastic"]},
          "seed": {"type": "integer", "minimum": -2147483648, "maximum": 4294967295, "x-api-limit": ["minimum", "maximum"]},
          "runs": {"type": "integer", "minimum": 1, "maximum": 1000, "x-api-limit": ["maximum"]},
          "startHour": {"type": "integer", "minimum": 0, "maximum": 8759},
          "horizonHours": {"type": "integer", "minimum": 1, "maximum": 8760, "x-api-limit": ["maximum"]},
          "localShiftHours": {"type": "integer", "minimum": 0, "maximum": 8760, "x-api-limit": ["maximum"]},
          "maxShiftSamples": {"type": "integer", "minimum": 1, "maximum": 8760, "x-api-limit": ["maximum"]},
          "assets": {"type": "array", "maxItems": 8, "items": {"$ref": "#/components/schemas/AssetDisturbance"}}
        },
        "additionalProperties": false
      },
      "AssetDisturbance": {
        "type": "object",
        "required": ["asset", "failureProbability", "meanRepairHours"],
        "properties": {
          "asset": {"type": "string", "enum": ["pv", "wind", "battery", "genset"]},
          "affectedUnits": {"type": "integer", "minimum": 0, "maximum": 10000, "x-api-limit": ["maximum"]},
          "failureProbability": {"type": "number", "minimum": 0, "maximum": 1},
          "meanRepairHours": {"type": "number", "minimum": 0, "maximum": 8760, "x-api-limit": ["maximum"]}
        },
        "additionalProperties": false
      },
      "Constraints": {
        "type": "object",
        "required": ["maxUnmetFraction", "minRenewableFraction", "maxExcessFraction"],
        "properties": {
          "renewableBasis": {"type": "string", "enum": ["every-year", "lifetime"]},
          "maxUnmetFraction": {"type": "number", "minimum": 0, "maximum": 1},
          "minRenewableFraction": {"type": "number", "minimum": 0, "maximum": 1},
          "maxExcessFraction": {"type": "number", "minimum": 0, "maximum": 1}
        },
        "additionalProperties": false
      },
      "Economics": {
        "type": "object",
        "required": [
          "discountRate",
          "inflationRate",
          "projectYears",
          "annualLoadGrowth",
          "fuelPricePerL",
          "fuelPriceEscalation",
          "elecCostEscalation",
          "voll",
          "itcFraction",
          "pvCapexPerKw",
          "windCapexPerKw",
          "batteryCapexPerKwh",
          "gensetCapexPerKw",
          "converterCapexPerKw",
          "bosFixed",
          "pvOmPerKwYr",
          "windOmPerKwYr",
          "batteryOmPerKwhYr",
          "gensetOmPerKwh",
          "converterOmPerKwYr",
          "co2PerL",
          "noxPerL",
          "so2PerL",
          "pm25PerL",
          "batteryCalendarLifeYears",
          "pvLifeYears",
          "windLifeYears"
        ],
        "properties": {
          "escalationBasis": {"type": "string", "enum": ["nominal", "real"], "description": "Basis of discountRate and escalation rates; absent = legacy real escalations"},
          "incentives": {"$ref": "#/components/schemas/IncentiveSelection"},
          "batteryCapexPerKw": {"type": "number", "minimum": 0, "maximum": 100000.0},
          "batteryCapexConstant": {"type": "number", "minimum": 0, "maximum": 1000000000.0},
          "batteryConstantReferenceKwh": {"type": "number", "exclusiveMinimum": 0, "maximum": 100000000.0},
          "gensetMaintenance": {"$ref": "#/components/schemas/GensetMaintenance"},
          "costBasis": {"$ref": "#/components/schemas/CostBasis"},
          "baselineGensetUnits": {"type": "integer", "minimum": 1, "maximum": 100},
          "baselineGensetRedundancy": {"type": "string", "enum": ["none", "n+1"]},
          "gensetLowLoadCostPerHour": {"type": "number", "minimum": 0, "maximum": 100000.0},
          "discountRate": {
            "type": "number",
            "minimum": -0.05,
            "maximum": 0.5,
            "description": "Annual fraction (0.08 = 8%). Values above 0.5 are rejected so a percent typed as a whole number (8) cannot pass."
          },
          "inflationRate": {"type": "number", "minimum": -0.05, "maximum": 0.5, "description": "Annual fraction"},
          "projectYears": {"type": "integer", "minimum": 1, "maximum": 50},
          "annualLoadGrowth": {"type": "number", "minimum": -0.2, "maximum": 0.5, "description": "Annual fraction, compounded"},
          "fuelPricePerL": {"type": "number", "minimum": 0, "maximum": 100},
          "fuelPriceEscalation": {"type": "number", "minimum": -0.2, "maximum": 0.5, "description": "Real annual fraction"},
          "elecCostEscalation": {"type": "number", "minimum": -0.2, "maximum": 0.5, "description": "Real annual fraction (grid mode)"},
          "voll": {"type": "number", "minimum": 0, "maximum": 10000},
          "itcFraction": {"type": "number", "minimum": 0, "maximum": 1},
          "pvCapexPerKw": {"type": "number", "minimum": 0, "maximum": 100000.0},
          "windCapexPerKw": {"type": "number", "minimum": 0, "maximum": 100000.0},
          "batteryCapexPerKwh": {"type": "number", "minimum": 0, "maximum": 100000.0},
          "gensetCapexPerKw": {"type": "number", "minimum": 0, "maximum": 100000.0},
          "converterCapexPerKw": {"type": "number", "minimum": 0, "maximum": 100000.0},
          "bosFixed": {"type": "number", "minimum": 0, "maximum": 10000000000.0},
          "pvOmPerKwYr": {"type": "number", "minimum": 0, "maximum": 10000.0},
          "windOmPerKwYr": {"type": "number", "minimum": 0, "maximum": 10000.0},
          "batteryOmPerKwhYr": {"type": "number", "minimum": 0, "maximum": 10000.0},
          "gensetOmPerKwh": {"type": "number", "minimum": 0, "maximum": 100},
          "converterOmPerKwYr": {"type": "number", "minimum": 0, "maximum": 10000.0},
          "co2PerL": {"type": "number", "minimum": 0, "maximum": 10},
          "noxPerL": {"type": "number", "minimum": 0, "maximum": 1},
          "so2PerL": {"type": "number", "minimum": 0, "maximum": 1},
          "pm25PerL": {"type": "number", "minimum": 0, "maximum": 1},
          "batteryCalendarLifeYears": {"type": "number", "exclusiveMinimum": 0, "maximum": 100, "description": "Must be > 0"},
          "pvLifeYears": {"type": "number", "exclusiveMinimum": 0, "maximum": 100, "description": "Must be > 0"},
          "windLifeYears": {"type": "number", "exclusiveMinimum": 0, "maximum": 100, "description": "Must be > 0"},
          "costModel": {"$ref": "#/components/schemas/CostModel"}
        },
        "additionalProperties": false
      },
      "CostModel": {
        "type": "object",
        "description": "Optional advanced real-dollar owner/lifecycle cash-flow assumptions. Omitted fields default to zero for backward compatibility.",
        "properties": {
          "capexContingencyFraction": {"type": "number", "minimum": 0, "maximum": 1},
          "developmentCostFixed": {"type": "number", "minimum": 0, "maximum": 10000000000.0},
          "annualInsuranceFraction": {"type": "number", "minimum": 0, "maximum": 0.2},
          "omEscalation": {"type": "number", "minimum": -0.2, "maximum": 0.5},
          "replacementCostDeclineRate": {"type": "number", "exclusiveMinimum": -1, "exclusiveMaximum": 1},
          "carbonPricePerTonne": {"type": "number", "minimum": 0, "maximum": 10000},
          "carbonPriceEscalation": {"type": "number", "minimum": -0.2, "maximum": 0.5},
          "decommissioningCostFraction": {"type": "number", "minimum": 0, "maximum": 1}
        },
        "additionalProperties": false
      },
      "SimulationResponse": {
        "type": "object",
        "required": ["schemaVersion", "engine", "fingerprint", "project", "provenance", "resource", "result"],
        "properties": {
          "schemaVersion": {"type": "string"},
          "engine": {
            "type": "string",
            "description": "Engine name and version (src/engine/version.ts), e.g. 'MicrogridModeler 3.0.0 …'"
          },
          "fingerprint": {
            "type": "object",
            "properties": {
              "value": {"type": "string"},
              "method": {"type": "string"},
              "cryptographic": {"type": "boolean", "const": false}
            }
          },
          "project": {"type": "object"},
          "provenance": {"type": "object"},
          "resource": {"type": "object"},
          "result": {
            "type": "object",
            "description": "Design, technical results, economics, feasibility, violations, deterministic outage survival, and component disturbance results when requested."
          },
          "hourly": {
            "type": "object",
            "description": "Complete year-one energy-flow series, present only when includeHourly=true."
          },
          "engineVersion": {"type": "string"}
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "METHOD_NOT_ALLOWED",
              "UNSUPPORTED_MEDIA_TYPE",
              "PAYLOAD_TOO_LARGE",
              "INVALID_JSON",
              "INVALID_REQUEST",
              "UNKNOWN_BENCHMARK",
              "VALIDATION_ERROR",
              "COMPUTE_BUDGET_EXCEEDED",
              "RATE_LIMITED",
              "NON_FINITE_RESULT",
              "SIMULATION_ERROR",
              "INTERNAL_ERROR"
            ]
          },
          "message": {"type": "string"},
          "details": {
            "type": "array",
            "items": {"type": "string"},
            "description": "Human-readable 'pointer: message' lines (legacy)"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["pointer", "keyword", "message"],
              "properties": {
                "pointer": {"type": "string", "description": "RFC 6901 JSON Pointer into the request body"},
                "keyword": {"type": "string"},
                "message": {"type": "string"}
              }
            }
          },
          "budget": {
            "type": "object",
            "properties": {
              "steps": {"type": "number"},
              "limit": {"type": "number"},
              "outageSteps": {"type": "number"},
              "disturbanceSteps": {"type": "number"}
            }
          },
          "retryAfterSeconds": {"type": "integer", "minimum": 0}
        }
      },
      "LoadProfile": {
        "type": "object",
        "description": "Provenance of the normalized planning load (informational; not used by the engine).",
        "required": ["source"],
        "properties": {
          "source": {"type": "string", "enum": ["archetype", "uploaded", "generated"]},
          "fileName": {"type": "string", "maxLength": 300},
          "sourceColumn": {"type": "string", "maxLength": 300},
          "originalCount": {"type": "integer", "minimum": 0, "maximum": 1000000},
          "timestamped": {"type": "boolean"},
          "sourceStartIso": {"type": "string", "maxLength": 40},
          "sourceEndIso": {"type": "string", "maxLength": 40},
          "studyYear": {"type": "integer", "minimum": 1900, "maximum": 2200},
          "sourceIntervalMinutes": {"type": "number", "exclusiveMinimum": 0, "maximum": 1440},
          "resampled": {"type": "boolean"},
          "warnings": {"type": "array", "maxItems": 50, "items": {"type": "string", "maxLength": 500}}
        },
        "additionalProperties": false
      },
      "Tariff": {
        "type": "object",
        "description": "Normalized hourly/period electricity tariff (src/engine/tariff.ts NormalizedTariff). Hourly arrays are 8,760 values aligned to the load; monthly arrays have 12 rows of tiers. JSON has no Infinity: send null for an unbounded last tier.",
        "required": [
          "year",
          "energyRate",
          "energyRatesByTier",
          "energyTierLimitsKwh",
          "exportRate",
          "monthlyDemandRates",
          "monthlyDemandTierLimitsKw",
          "touDemand",
          "fixedMonthlyCharge",
          "minMonthlyCharge",
          "annualMinCharge",
          "netMetering"
        ],
        "properties": {
          "exportMode": {"type": "string", "enum": ["net-metering", "net-billing", "none"]},
          "nemCreditCap": {"type": "string", "enum": ["energy", "bill"]},
          "netSurplusRatePerKwh": {"type": "number", "minimum": 0, "maximum": 10},
          "year": {"type": "integer", "minimum": 1900, "maximum": 2200},
          "energyRate": {
            "type": "array",
            "minItems": 8760,
            "maxItems": 8760,
            "items": {"type": "number", "minimum": -10, "maximum": 100}
          },
          "energyRatesByTier": {
            "type": "array",
            "minItems": 1,
            "maxItems": 20,
            "items": {
              "type": "array",
              "minItems": 8760,
              "maxItems": 8760,
              "items": {"type": "number", "minimum": -10, "maximum": 100}
            }
          },
          "energyTierLimitsKwh": {
            "type": "array",
            "minItems": 12,
            "maxItems": 12,
            "items": {
              "type": "array",
              "minItems": 1,
              "maxItems": 20,
              "items": {
                "type": ["number", "null"],
                "minimum": 0,
                "description": "Upper tier limit; null (or omitted last tier) = unbounded"
              }
            }
          },
          "exportRate": {
            "type": "array",
            "minItems": 8760,
            "maxItems": 8760,
            "items": {"type": "number", "minimum": -10, "maximum": 100}
          },
          "monthlyDemandRates": {
            "type": "array",
            "minItems": 12,
            "maxItems": 12,
            "items": {
              "type": "array",
              "minItems": 1,
              "maxItems": 20,
              "items": {"type": "number", "minimum": -10, "maximum": 1000}
            }
          },
          "monthlyDemandTierLimitsKw": {
            "type": "array",
            "minItems": 12,
            "maxItems": 12,
            "items": {
              "type": "array",
              "minItems": 1,
              "maxItems": 20,
              "items": {
                "type": ["number", "null"],
                "minimum": 0,
                "description": "Upper tier limit; null (or omitted last tier) = unbounded"
              }
            }
          },
          "touDemand": {
            "type": "array",
            "maxItems": 288,
            "items": {
              "type": "object",
              "required": ["hours", "rate"],
              "properties": {
                "hours": {"type": "array", "maxItems": 8760, "items": {"type": "integer", "minimum": 0, "maximum": 8759}},
                "rate": {"type": "number", "minimum": 0, "maximum": 1000}
              },
              "additionalProperties": false
            }
          },
          "fixedMonthlyCharge": {"type": "number", "minimum": 0, "maximum": 10000000.0},
          "minMonthlyCharge": {"type": "number", "minimum": 0, "maximum": 10000000.0},
          "annualMinCharge": {"type": "number", "minimum": 0, "maximum": 100000000.0},
          "netMetering": {"type": "boolean"}
        },
        "additionalProperties": false
      },
      "SiteClimatology": {"type": "object", "description": "Monthly site climatology (NASA POWER) that calibrates the synthetic weather year.", "required": ["ghiKwhM2Day", "t2mC", "source"], "properties": {"ghiKwhM2Day": {"type": "array", "items": {"type": "number", "minimum": 0, "maximum": 15}, "minItems": 12, "maxItems": 12}, "clearSkyGhiKwhM2Day": {"type": "array", "items": {"type": "number", "minimum": 0, "maximum": 15}, "minItems": 12, "maxItems": 12}, "diffuseKwhM2Day": {"type": "array", "items": {"type": "number", "minimum": 0, "maximum": 15}, "minItems": 12, "maxItems": 12}, "t2mC": {"type": "array", "items": {"type": "number", "minimum": -90, "maximum": 60}, "minItems": 12, "maxItems": 12}, "t2mRangeC": {"type": "array", "items": {"type": "number", "minimum": 0, "maximum": 60}, "minItems": 12, "maxItems": 12}, "albedo": {"type": "array", "items": {"type": "number", "minimum": 0, "maximum": 1}, "minItems": 12, "maxItems": 12}, "ws10Ms": {"type": "array", "items": {"type": "number", "minimum": 0, "maximum": 50}, "minItems": 12, "maxItems": 12}, "ws50Ms": {"type": "array", "items": {"type": "number", "minimum": 0, "maximum": 60}, "minItems": 12, "maxItems": 12}, "cellElevationM": {"type": "number", "minimum": -500, "maximum": 9000}, "interannualGhiCov": {"type": "number", "minimum": 0, "maximum": 1}, "source": {"type": "object", "description": "Provenance of the climatology (dataset, point, fetch date)."}}, "additionalProperties": false},
      "BatteryDegradation": {"type": "object", "description": "State-of-health fade model used by the multi-year rollup.", "required": ["model", "endOfLifeSoh", "maintenance"], "properties": {"model": {"type": "string", "enum": ["reopt-lfp", "rated-life", "custom", "none"]}, "calendarFadeCoefficient": {"type": "number", "minimum": 0, "maximum": 1}, "cycleFadeCoefficient": {"type": "number", "minimum": 0, "maximum": 1}, "timeExponent": {"type": "number", "minimum": 0.05, "maximum": 1}, "endOfLifeSoh": {"type": "number", "minimum": 0.3, "maximum": 0.99}, "maintenance": {"type": "string", "enum": ["replacement", "augmentation"]}}, "additionalProperties": false},
      "IncentiveSelection": {"type": "object", "description": "Investment-credit inputs; resolved per technology by the dated rules table unless mode is manual.", "properties": {"mode": {"type": "string", "enum": ["resolve", "manual"]}, "manual": {"type": "object", "properties": {"pv": {"type": "number", "minimum": 0, "maximum": 1}, "wind": {"type": "number", "minimum": 0, "maximum": 1}, "storage": {"type": "number", "minimum": 0, "maximum": 1}}, "additionalProperties": false}, "jurisdiction": {"type": "string", "enum": ["US", "US-territory", "non-US", "unknown"]}, "owner": {"type": "string", "enum": ["homeowner", "taxable-business", "tax-exempt", "third-party"]}, "eligibilityConfirmed": {"type": "boolean"}, "constructionStartDate": {"type": "string", "maxLength": 32}, "placedInServiceDate": {"type": "string", "maxLength": 32}, "pwaCompliant": {"type": "boolean"}, "pfeCompliant": {"type": "boolean"}, "domesticContent": {"type": "boolean"}, "energyCommunity": {"type": "boolean"}, "territoryDomesticOwner": {"type": "boolean"}, "sizesKwAc": {"type": "object", "properties": {"pv": {"type": "number", "minimum": 0, "maximum": 10000000.0}, "wind": {"type": "number", "minimum": 0, "maximum": 10000000.0}, "storage": {"type": "number", "minimum": 0, "maximum": 10000000.0}}, "additionalProperties": false}, "evaluationDate": {"type": "string", "maxLength": 32}}, "additionalProperties": false},
      "GensetMaintenance": {"type": "object", "description": "Hours-driven genset overhaul/replacement economics (all optional; documented defaults apply).", "properties": {"overhaulIntervalHours": {"type": "number", "exclusiveMinimum": 0, "maximum": 10000000.0}, "overhaulCostFraction": {"type": "number", "minimum": 0, "maximum": 1}, "overhaulsBeforeReplacement": {"type": "integer", "minimum": 0, "maximum": 10}, "replacementCostFraction": {"type": "number", "minimum": 0, "maximum": 2}, "calendarLifeYears": {"type": "number", "minimum": 0, "maximum": 100}}, "additionalProperties": false},
      "CostBasis": {"type": "object", "description": "Provenance of the cost defaults (informational).", "properties": {"assumptionIds": {"type": "array", "items": {"type": "string", "maxLength": 100}, "maxItems": 200}, "pvSizeClass": {"type": "string", "maxLength": 100}, "pvMount": {"type": "string", "enum": ["roof", "ground"]}, "logisticsMultiplier": {"type": "number", "minimum": 0, "maximum": 10}, "summary": {"type": "string", "maxLength": 2000}}, "additionalProperties": false}
    }
  }
}
