"""
Auto-generated by datamodel-codegen. 

DO NOT EDIT MANUALLY.

timestamp: 2026-03-11T10:29:37+00:00

version: datamodel-codegen 0.55.0

The contents of this file are updated automatically when changes to ./src/openbes/schemas/OpenBES.schema.json 
are pushed to GitHub using the ./github/workflows/generate_models.yml Action. 
To make changes to the models, the first step is to try updating OpenBES.schema.json, 
which is the single source of truth for the simulation specification. 

If that does not suffice for your needs, you can add post-hoc wrapper functionality in 
./src/openbes/schemas/__init__.py to change how the model code is exported.

"""

from __future__ import annotations

from enum import StrEnum
from pydantic import BaseModel, ConfigDict, Field, RootModel


class ENERGYSOURCES(StrEnum):
    Electricity = "Electricity"
    Diesel = "Diesel"
    LPG = "LPG"
    Natural_gas = "Natural gas"
    Biomass = "Biomass"
    Pellets = "Pellets"


class COOLINGSYSTEMTYPES(StrEnum):
    Heat_pump = "Heat pump"


class HEATINGSYSTEMTYPES(StrEnum):
    Electric_heating__radiators_ = "Electric heating (radiators)"
    Electric_boiler = "Electric boiler"
    Conventional_boiler = "Conventional boiler"
    Low_temperature_boiler = "Low temperature boiler"
    Condensing_boiler = "Condensing boiler"
    Biomass_boiler = "Biomass boiler"
    Heat_pump = "Heat pump"


class METEOROLOGICALFILE(StrEnum):
    openbes___USA_Denver_725650TYCST_epw = "openbes://USA_Denver_725650TYCST.epw"
    openbes___UK_Oxford_GBR_ENG_RAF_Benson_036580_TMYx_2007_2021_epw = (
        "openbes://UK_Oxford_GBR_ENG_RAF.Benson.036580_TMYx.2007-2021.epw"
    )
    openbes___SPAIN_Sevilla_083910_SWEC_epw = "openbes://SPAIN_Sevilla.083910_SWEC.epw"
    openbes___SPAIN_Madrid_082210_SWEC_epw = "openbes://SPAIN_Madrid.082210_SWEC.epw"


class TERRAINCLASS(StrEnum):
    Open_terrain = "Open terrain"
    Country = "Country"
    Urban = "Urban"


class SUPPORTEDCOUNTRIES(StrEnum):
    Spain = "Spain"
    USA = "USA"
    France = "France"
    UK = "UK"
    Other = "Other"


class WindowCounts(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    front: list[int] | None = Field(
        default=None,
        description="Array of window counts per floor on the front side of the building.",
    )
    back: list[int] | None = Field(
        default=None,
        description="Array of window counts per floor on the back side of the building.",
    )
    left: list[int] | None = Field(
        default=None,
        description="Array of window counts per floor on the left side of the building.",
    )
    right: list[int] | None = Field(
        default=None,
        description="Array of window counts per floor on the right side of the building.",
    )


class Building(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    area: str | None = Field(
        default=None, description="Street/region the building is in."
    )
    height: float | None = Field(
        default=None, description="Height of the building in m."
    )
    length: float = Field(..., description="Length of the building in m.")
    width: float = Field(..., description="Width of the building in m.")
    name: str | None = Field(default=None, description="Name of the building")
    type: str | None = Field(
        default=None, description="Type of building (e.g. Office, School)."
    )
    floor_count: int | None = Field(
        default=1, description="Number of floors in the building."
    )
    floor_to_ceiling_height: float | None = Field(
        default=2.7, description="Floor to ceiling height in m."
    )
    slab_thickness: float | None = Field(
        default=0.2, description="Slab thickness in m."
    )
    orientation_angle: float | None = Field(
        default=0.0,
        description="Orientation angle of the building in degrees clockwise from North=0.",
    )
    roof_angle: float | None = Field(
        default=0.0, description="Roof angle in degrees from horizontal."
    )
    solar_external_shading_summer: float | None = Field(
        default=0.0,
        description="External solar shading factor for summer (Summer = air temp > 22C).",
    )
    solar_external_shading_winter: float | None = Field(
        default=0.0,
        description="External solar shading factor for winter (Winter = air temp <= 22C).",
    )
    terrain_class: TERRAINCLASS | None = Field(
        default=None, description="Terrain class around the building."
    )
    thermal_bridge_facade_ground: bool | None = Field(
        default=None,
        description="Whether to model the thermal bridge factor for facade to ground junctions.",
    )
    thermal_bridge_facade_intermediate: bool | None = Field(
        default=None,
        description="Whether to model the thermal bridge factor for facade to intermediate floor junctions.",
    )
    thermal_bridge_facade_roof: bool | None = Field(
        default=None,
        description="Whether to model the thermal bridge factor for facade to roof junctions.",
    )
    thermal_bridge_shading: bool | None = Field(
        default=None,
        description="Whether to model the thermal bridge factor for shading devices.",
    )
    thermal_bridge_window: bool | None = Field(
        default=None,
        description="Whether to model the thermal bridge factor for windows.",
    )
    uvalue_facade: float | None = Field(
        default=0.0, description="U-value of the facade in W/m2K."
    )
    uvalue_roof: float | None = Field(
        default=0.0, description="U-value of the roof in W/m2K."
    )
    uvalue_window: float | None = Field(
        default=0.0, description="U-value of the windows in W/m2K."
    )
    uvalue_floor: float | None = Field(
        default=0.0, description="U-value of the floor in W/m2K."
    )
    window_frame_factor: float | None = Field(
        default=0.0, description="Window frame factor (fraction)."
    )
    window_gvalue: float | None = Field(
        default=0.0, description="Window g-value (fraction)."
    )
    window_height: float | None = Field(default=0.0, description="Window height in m.")
    window_length: float | None = Field(default=0.0, description="Window length in m.")
    window_counts: WindowCounts | None = Field(
        default=None,
        description="A map of building side to array of window counts per floor.",
    )


class Duration(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    start: int = Field(..., description="Start hour (1-24).")
    end: int = Field(..., description="End hour (1-24).")


class Zone(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    name: str = Field(..., description="Zone name")
    areas: list[float] = Field(..., description="Zone area for each floor in m2")
    conditioned: bool | None = Field(
        default=True, description="Whether this zone uses HVAC."
    )
    active_hours: Duration | None = Field(
        default=None, description="Times the zone is occupied each day."
    )


class ZoneSimultaneity(RootModel[dict[str, float]]):
    root: dict[str, float]


class OccupationSchedule(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    January: bool | None = False
    February: bool | None = False
    March: bool | None = False
    April: bool | None = False
    May: bool | None = False
    June: bool | None = False
    July: bool | None = False
    August: bool | None = False
    September: bool | None = False
    October: bool | None = False
    November: bool | None = False
    December: bool | None = False
    Monday: bool | None = False
    Tuesday: bool | None = False
    Wednesday: bool | None = False
    Thursday: bool | None = False
    Friday: bool | None = False
    Saturday: bool | None = False
    Sunday: bool | None = False


class Range(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    min: float = Field(..., description="Minimum value of the range.")
    max: float = Field(..., description="Maximum value of the range.")


class Consumption(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    January: float | None = 0.0
    February: float | None = 0.0
    March: float | None = 0.0
    April: float | None = 0.0
    May: float | None = 0.0
    June: float | None = 0.0
    July: float | None = 0.0
    August: float | None = 0.0
    September: float | None = 0.0
    October: float | None = 0.0
    November: float | None = 0.0
    December: float | None = 0.0


class HeatingSystem(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    type: HEATINGSYSTEMTYPES | None = None
    energy_source: ENERGYSOURCES = Field(
        ..., description="Energy source for this heating system."
    )
    efficiency_cop: float = Field(..., description="COP or efficiency (if applicable).")
    min_demand: float | None = Field(
        default=0.0, description="Minimum demand threshold."
    )
    nominal_capacity: float = Field(..., description="Nominal capacity.")
    count: int | None = Field(default=1, description="Number of identical units.")
    active_hours: Duration = Field(..., description="Times the system is on each day.")
    simultaneity: ZoneSimultaneity | None = None


class CoolingSystem(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    type: COOLINGSYSTEMTYPES | None = None
    energy_source: ENERGYSOURCES
    efficiency_ratio: float = Field(
        ..., description="EER/COP or energy efficiency ratio."
    )
    min_demand: float | None = Field(default=None, description="Minimum demand.")
    nominal_capacity: float = Field(..., description="Nominal capacity.")
    sensible_nominal_capacity: float = Field(..., description="Sensible capacity.")
    count: int | None = Field(default=1, description="Number of identical units.")
    active_hours: Duration = Field(..., description="Times the system is on each day.")
    simultaneity: ZoneSimultaneity | None = None


class VentilationSystem(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    type: float | None = Field(default=None, description="Optional numeric type code.")
    energy_source: ENERGYSOURCES
    airflow: float = Field(..., description="Airflow (m3/s).")
    heat_recovery_efficiency: float | None = Field(
        default=None, description="Heat recovery efficiency."
    )
    rated_input_power: float | None = Field(
        default=None, description="Rated input power."
    )
    ventilated_area: float | None = Field(
        default=1.0, description="Proportion of area ventilated."
    )
    active_hours: Duration | None = Field(
        default_factory=lambda: Duration.model_validate({"start": 1, "end": 24}),
        description="Times the system is on each day.",
    )


class Ballast(StrEnum):
    Electronic_ballast = "Electronic ballast"
    Ferromagnetic_ballast = "Ferromagnetic ballast"


class LightingSystemTech(StrEnum):
    Tubular_fluorescent_T8 = "Tubular fluorescent T8"
    Tubular_fluorescent_T5 = "Tubular fluorescent T5"
    Compact_fluorescent = "Compact fluorescent"
    Incandescent = "Incandescent"
    Halogen = "Halogen"
    Mercury_vapor = "Mercury vapor"
    Sodium_vapour = "Sodium vapour"
    Metal_halide = "Metal halide"
    Induction = "Induction"
    LED = "LED"


class HotWaterSystem(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    demand: Consumption = Field(..., description="Hot water demand (litres).")
    reference_temperature: float = Field(..., description="Reference temperature (°C).")
    supply_temperature: float = Field(..., description="Supply temperature (°C).")
    energy_source: ENERGYSOURCES = Field(
        ..., description="Energy source for this hot water system."
    )
    efficiency_cop: float = Field(
        ..., description="Efficiency of the hot water system."
    )
    nominal_capacity: float = Field(
        ..., description="Nominal capacity of the hot water system."
    )
    type: float | None = Field(default=None, description="[UNUSED]")


class Courtyard(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    length: float = Field(..., description="Courtyard length (m).")
    width: float = Field(..., description="Courtyard width (m).")
    count: int | None = Field(
        default=1, description="How many courtyards of this geometry exist."
    )


class OpenCourtyard(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    depth: float = Field(..., description="Courtyard depth (m).")
    count: int | None = Field(
        default=1, description="How many courtyards of this geometry exist."
    )


class OpenCourtyardMap(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    front: OpenCourtyard | None = None
    back: OpenCourtyard | None = None
    left: OpenCourtyard | None = None
    right: OpenCourtyard | None = None


class HeatCapacity(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    Am: float = Field(..., description="Mass factor")
    Cm: float = Field(..., description="Areal heat capacity (J/m2K)")


class OpenBESParameters(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    altitude: float | None = Field(
        default=None,
        description="Altitude (m). [Currently UNUSED: EPW file altitude is used instead]",
    )
    include_appliances: bool | None = Field(
        default=True, description="Model appliance consumption."
    )
    cooling_load_factor: float | None = Field(
        default=1.0, description="Multiplier applied to cooling loads."
    )
    density_of_air: float | None = Field(
        default=1.211, description="Air density (kg/m3)."
    )
    facade_absorption_coefficient: float | None = Field(
        default=0.6, description="Facade solar absorption coefficient."
    )
    facade_correction_factor: float | None = Field(
        default=1.0, description="Facade correction factor."
    )
    facade_emissivity: float | None = Field(
        default=0.9, description="Facade emissivity value."
    )
    floor_correction_factor: float | None = Field(
        default=1.0, description="Floor correction factor."
    )
    heat_capacity_correction_factor: float | None = Field(
        default=1.0, description="Heat capacity correction factor."
    )
    heat_capacity_joule: float | None = Field(
        default=None, description="Heat capacity of air in J/kgK."
    )
    heating_load_factor: float | None = Field(
        default=1.0, description="Multiplier applied to heating loads."
    )
    infiltration_correction_factor: float | None = Field(
        default=1.0, description="Correction factor for infiltration."
    )
    leakage_air_flow_dependent: float | None = Field(
        default=0.0001,
        description="Leakage air flow rate dependent on other factors (m3/s).",
    )
    include_lighting: bool | None = Field(
        default=True, description="Whether lighting is modelled."
    )
    nia_gba_ratio: float | None = Field(
        default=0.95, description="Net internal area / gross building area fraction."
    )
    include_occupancy: bool | None = Field(
        default=True, description="Whether occupancy schedules are modelled."
    )
    output_csv_precision: int | None = Field(
        default=2, description="Number of decimal places in CSV output."
    )
    pressure_of_air: float | None = Field(
        default=None, description="Air pressure. [UNUSED]"
    )
    roof_absorption_coefficient: float | None = Field(
        default=0.8, description="Roof solar absorption coefficient."
    )
    roof_correction_factor: float | None = Field(
        default=1.0, description="Roof correction factor."
    )
    roof_emissivity: float | None = Field(default=0.9, description="Roof emissivity.")
    shading_correction_factor: float | None = Field(
        default=1.0, description="Shading correction factor."
    )
    specific_heat_of_air: float | None = Field(
        default=1.015, description="Specific heat of air."
    )
    view_factor_to_sky_facade: float | None = Field(
        default=0.5, description="View factor to sky from facade."
    )
    view_factor_to_sky_roof: float | None = Field(
        default=1.0, description="View factor to sky from roof."
    )
    window_correction_factor: float | None = Field(
        default=1.0, description="Window correction factor."
    )
    window_angular_correction_factors: list[float] | None = Field(
        default=[1.0, -0.189, 0.644, -0.596, 0.0],
        description="Angular correction factors for windows.",
        max_length=5,
        min_length=5,
    )


class LightingControl(StrEnum):
    Manual = "Manual"
    Automatic = "Automatic"


class SpaceThermalDemandResult(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    demand_total: float | None = Field(
        default=None, description="Annual energy required for heating/cooling in kWh."
    )
    demand_scaled: float | None = Field(
        default=None,
        description="Annual energy required for heating/cooling in kWh/m2.",
    )
    demand_on_all_year: float | None = Field(
        default=None,
        description="Annual energy demanded in kWh/m2 if the system is on all year round.",
    )
    load_csv: str | None = Field(
        default=None, description="Load in kWh/m2 by calendar month."
    )
    load_duration_csv: str | None = Field(
        default=None, description="Load in kW by annual percentile."
    )


class VentilationSystemResult(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    energy_demand: float | None = Field(
        default=None, description="Energy demanded in kWh/m2"
    )
    peak_load: float | None = Field(
        default=None, description="Maximum hourly energy required in kW."
    )
    sfp: float | None = Field(
        default=None, description="Specific fan power in kW/m3/s."
    )
    mechanical_ventilation_rate: float | None = Field(
        default=None,
        description="Mechanical ventilation rate in m3/h per m2. Recommended: 3 m3/h m2.",
    )
    ventilation_rate: float | None = Field(
        default=None,
        description="Ventilation rate in l/s m2. Reference: 0.83l/s m2 or 12.5l/s per person.",
    )
    ach: float | None = Field(default=None, description="Air changes per hour.")


class ModelValidation(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    energy_use_csv: str | None = Field(
        default=None,
        description="Energy used for each calendar month in kWh. Compare to user-supplied values in `OpenBESSpecification.*_consumption`.",
    )
    nmbe: float | None = Field(default=None, description="Normalised mean bias error.")
    cv_rmse: float | None = Field(
        default=None,
        description="Coefficient of variation of the root mean square deviation.",
    )
    r2: float | None = Field(default=None, description="R-squared model fit.")


class Month(StrEnum):
    January = "January"
    February = "February"
    March = "March"
    April = "April"
    May = "May"
    June = "June"
    July = "July"
    August = "August"
    September = "September"
    October = "October"
    November = "November"
    December = "December"


class DayPeak(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    value: float | None = Field(default=None, description="The statistic in question.")
    month: Month | None = None
    day: float | None = Field(default=None, description="Day of the month.")


class HourPeak(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    value: float | None = Field(default=None, description="The statistic in question.")
    month: Month | None = None
    day: float | None = Field(default=None, description="Day of the month.")
    hour: float | None = Field(default=None, description="Hour of the day.")


class LocationSimulationOutput(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    altitude: float | None = Field(
        default=None, description="Altitude as read from the weather file."
    )
    latitude: float | None = Field(
        default=None, description="Latitude as read from the weather file."
    )
    longitude: float | None = Field(
        default=None, description="Longitude as read from the weather file."
    )
    city: str | None = Field(
        default=None, description="City as read from the weather file."
    )
    country: str | None = Field(
        default=None, description="Country as read from the weather file."
    )
    state_province: str | None = Field(
        default=None, description="State/province as read from the weather file."
    )
    solstice_ghr_csv: str | None = Field(
        default=None,
        description="Global Horizontal Radiation (Wh/m2) for the longest and shortest days of the year (21 Dec, 21 Jun).",
    )
    max_outdoor_temperature: HourPeak | None = None
    min_outdoor_temperature: HourPeak | None = None
    mean_outdoor_temperature: float | None = Field(
        default=None, description="Mean outdoor temperature over the year."
    )
    max_outdoor_day_temperature: DayPeak | None = Field(
        default=None,
        description="Hottest day of the year where the temperature is the mean of the hottest hour plus and coldest hour.",
    )
    min_outdoor_day_temperature: DayPeak | None = Field(
        default=None,
        description="Coldest day of the year where the temperature is the mean of the hottest hour plus and coldest hour.",
    )
    mean_outdoor_day_temperature: float | None = Field(
        default=None,
        description="Mean daily temperature given by the mean of the hottest hour plus and coldest hour.",
    )
    climate_quantiles_csv: str | None = Field(
        default=None,
        description="Outdoor air temperature percentiles from climate data.",
    )
    degree_days_csv: str | None = Field(
        default=None, description="Heating/cooling degree days by month."
    )
    annual_incident_solar_radiation_csv: str | None = Field(
        default=None,
        description="Annual incident solar radiation in kWh/m2 for each 1/8th compass point.",
    )


class GeometrySimulationOutput(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    gross_building_area: float | None = Field(
        default=None, description="Total building area in m2."
    )
    conditioned_floor_area: float | None = Field(
        default=None,
        description="Floor area subject to HVAC systems. Used as the divisor in most /m2 statistics.",
    )
    indoor_air_volume: float | None = Field(
        default=None,
        description="Volume of air in the building's conditioned areas in m3.",
    )
    indoor_air_heat_capacity: float | None = Field(
        default=None, description="Indoor air heat capacity in kJ/K"
    )
    building_geometry_csv: str | None = Field(
        default=None,
        description="Building area by floor and category in m2, and window-to-wall ratio (unitless).",
    )
    building_geometry_orientation_csv: str | None = Field(
        default=None,
        description="Building facade, window, and roof area in m2 by category and orientation.",
    )


class ClimateSimulationOutput(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    external_internal_temperature_csv: str | None = Field(
        default=None,
        description="Internal (HVAC) and external temperatures for each hour of the year.",
    )
    max_indoor_temperature: HourPeak | None = None
    min_indoor_temperature: HourPeak | None = None
    mean_indoor_temperature: float | None = Field(
        default=None, description="Mean indoor temperature over the year."
    )
    discomfort_hours_percent: float | None = Field(
        default=None,
        description="The number of hours when the building is occupied and it would be outside 18-26C if HVAC were not operating.",
    )
    discomfort_hours_percent_summer: float | None = Field(
        default=None,
        description="The number of hours when the building is occupied and it would be outside 18-26C if HVAC were not operating during June, July, and August.",
    )
    heat_exchange_breakdown_csv: str | None = Field(
        default=None,
        description="The heat loss/gain in kWh/m2 for each month, broken down by transmission category.",
    )
    space_thermal_demand_csv: str | None = Field(
        default=None,
        description="The heating and cooling energy requirement in kWh/m2 for each month of the year based on heat, mass balance, and occupancy.",
    )
    infiltration_ach: float | None = Field(
        default=None, description="Air changes/hour from air infiltration."
    )
    natural_ach: float | None = Field(
        default=None, description="Air changes/hour from natural ventilation."
    )
    heating_demand: SpaceThermalDemandResult | None = None
    cooling_demand: SpaceThermalDemandResult | None = None
    running_average_outside_temp_csv: str | None = Field(
        default=None,
        description="The 7-day weighted running mean of the outside temperature for each hour of the year. The first 6 days copy the value from day 7.",
    )
    overheating_limits_csv: str | None = Field(
        default=None,
        description="Category I, II, and III overheating limits in °C at different outdoor temperatures.",
    )
    solar_heat_gains_csv: str | None = Field(
        default=None,
        description="Solar heat gains in kWh by orientation and category. For kWh/m2 divide by relevant `building_facade_csv` area.",
    )
    window_transmissivity_coefficient_csv: str | None = Field(
        default=None,
        description="Annual transmissivity coefficient of windows by orientation.",
    )


class VentilationSimulationOutput(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    ventilation_systems: list[VentilationSystemResult] | None = None


class LightingSimulationOutput(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    lighting_demand: float | None = Field(
        default=None, description="Energy demand for lighting in kWh/m2."
    )
    lighting_peak_load: float | None = Field(
        default=None, description="Peak lighting load in kW."
    )
    lighting_load_ratio: float | None = Field(
        default=None, description="Lighting energy required in W/m2."
    )


class HotWaterSimulationOutput(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    hot_water_demand: float | None = Field(
        default=None, description="Energy demand for water heating in kWh/m2."
    )


class BuildingEnergySimulationOutput(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    other_energy_use_electricity: float | None = Field(
        default=None, description="Miscellaneous electricity use in kWh/m2."
    )
    other_energy_use_gas: float | None = Field(
        default=None, description="Miscellaneous natural gas usage in kWh/m2."
    )
    on_site_electricity_generated: float | None = Field(
        default=None, description="Renewable electricity generated on site in kWh/m2."
    )
    on_site_electricity_used: float | None = Field(
        default=None,
        description="Renewable electricity generated and consumed on site in kWh/m2.",
    )
    on_site_electricity_fraction: float | None = Field(
        default=None,
        description="Renewable energy consumption / Final energy consumption",
    )
    all_renewable_fraction: float | None = Field(
        default=None,
        description="Fraction of final energy consumption drawn from renewable energy sources including both on- and off-site renewable energy sources.",
    )
    final_energy_consumption: float | None = Field(
        default=None, description="Final energy consumption in kWh/m2."
    )
    primary_energy_consumption: float | None = Field(
        default=None, description="Primary energy consumption in kWh/m2."
    )
    non_renewable_primary_energy_consumption: float | None = Field(
        default=None, description="Non-renewable primary energy consumption in kWh/m2."
    )
    co2_equivalent_emissions: float | None = Field(
        default=None, description="CO2 equivalent emissions in kg CO2 eq/m2."
    )
    final_energy_consumption_csv: str | None = Field(
        default=None,
        description="Final energy consumption in kWh for each energy use category. Divide by `conditioned_floor_area` for kWh/m2.",
    )
    electricity_validation: ModelValidation | None = None
    gas_validation: ModelValidation | None = None


class OpenBESMetaData(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    version: str = Field(..., description="Version of Open BES used.")
    timestamp: str | None = Field(
        default=None, description="Timestamp of when the simulation was run."
    )
    EPW_file_checksum: str | None = Field(
        default=None, description="MD5 Checksum of the EPW file used in the simulation."
    )


class LightingSystem(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    tech: LightingSystemTech
    ballast: Ballast | None = Field(default=None, description="Ballast type.")
    lamp_number: int = Field(..., description="Number of lamps.")
    lamp_power: float = Field(..., description="Lamp power (W).")
    luminary_number: int = Field(..., description="Number of luminaires.")
    name: str | None = Field(
        default=None, description="Optional human label for the system."
    )
    active_hours: Duration = Field(
        ..., description="Times the lighting is on each day."
    )
    count: int | None = Field(
        default=1, description="Count of similar lighting systems aggregated together."
    )
    simultaneity_factor: float = Field(
        ..., description="Simultaneity factor (fraction)."
    )


class OpenBESSpecification(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    parameters: OpenBESParameters | None = Field(
        default=None,
        description="Collapsed parameters object. Use this to hold heating/cooling/ventilation/lighting/courtyards arrays.",
    )
    building: Building | None = Field(
        default=None, description="Building general information."
    )
    zones: list[Zone] | None = Field(default=None, description="Zones in the building.")
    occupation_schedule: OccupationSchedule | None = Field(
        default=None, description="Occupation schedule for the building."
    )
    heating_systems: list[HeatingSystem] | None = Field(
        default=None, description="Heating systems in the building."
    )
    cooling_systems: list[CoolingSystem] | None = Field(
        default=None, description="Cooling systems in the building."
    )
    ventilation_systems: list[VentilationSystem] | None = Field(
        default=None, description="Ventilation systems in the building."
    )
    lighting_systems: list[LightingSystem] | None = Field(
        default=None, description="Lighting systems in the building."
    )
    hot_water_systems: list[HotWaterSystem] | None = Field(
        default=None, description="Hot water systems in the building."
    )
    courtyards: list[Courtyard] | None = Field(
        default=None, description="Courtyards in the building."
    )
    open_courtyards: OpenCourtyardMap | None = Field(
        default=None, description="Open courtyards on each side of the building."
    )
    appliances_load: float | None = Field(
        default=0.0, description="Load from appliances (Wh/m2)."
    )
    biomass_consumption: Consumption | None = Field(
        default=None, description="Amount of biomass consumed by the building. [UNUSED]"
    )
    biomass_pellets_consumption: Consumption | None = Field(
        default=None,
        description="Amount of biomass pellets consumed by the building. [UNUSED]",
    )
    building_standby_electricity_consumption: Consumption | None = Field(
        default=None, description="Electrical standby load for the building."
    )
    diesel_consumption: Consumption | None = Field(
        default=None, description="Amount of diesel consumed by the building. [UNUSED]"
    )
    electricity_consumption: Consumption | None = Field(
        default=None,
        description="Amount of electricity consumed by the building. [UNUSED]",
    )
    other_electricity_consumption: Consumption | None = Field(
        default=None,
        description="Amount of other electricity consumed by the building. [UNUSED]",
    )
    energy_generated: Consumption | None = Field(
        default=None, description="Amount of energy generated by the building. [UNUSED]"
    )
    energy_used: Consumption | None = Field(
        default=None, description="Amount of energy used by the building. [UNUSED]"
    )
    other_gas_consumption: Consumption | None = Field(
        default=None,
        description="Amount of other gas consumed by the building. [UNUSED]",
    )
    LPG_consumption: Consumption | None = Field(
        default=None, description="Amount of LPG consumed by the building. [UNUSED]"
    )
    natural_gas_consumption: Consumption | None = Field(
        default=None,
        description="Amount of natural gas consumed by the building. [UNUSED]",
    )
    country: SUPPORTEDCOUNTRIES | None = None
    heat_capacity: HeatCapacity | None = Field(
        default=None, description="Heat capacity properties of the building."
    )
    holiday: bool | None = Field(
        default=None, description="Whether the building is open on public holidays."
    )
    leakage_air_flow: float | None = Field(
        default=None, description="Leakage air flow rate (m3/s)."
    )
    leakage_air_flow_independent: float | None = Field(
        default=0.0,
        description="Leakage air flow rate independent of other factors (m3/s).",
    )
    lighting_control: LightingControl | None = Field(
        default="Automatic", description="Type of lighting control system."
    )
    lighting_active_hours: Duration | None = Field(
        default=None, description="Daily active hours for lighting."
    )
    lighting_simultaneity_factor: float | None = Field(
        default=0.2, description="Simultaneity factor for lighting."
    )
    max_building_occupation: int | None = Field(
        default=None,
        description="Maximum occupancy of the building in number of people.",
    )
    meteorological_file_path: METEOROLOGICALFILE | str | None = Field(
        default=None, description="Meteorological filename or identifier."
    )
    natural_ventilation_night: float | None = Field(
        default=None, description="Night-time natural ventilation rate."
    )
    setpoint_temperature_day: Range | None = Field(
        default_factory=lambda: Range.model_validate({"min": 20.0, "max": 20.0}),
        description="Daytime temperature setpoint range.",
    )
    setpoint_temperature_night: Range | None = Field(
        default_factory=lambda: Range.model_validate({"min": 20.0, "max": 20.0}),
        description="Night-time temperature setpoint range.",
    )
    typical_occupation: int | None = Field(
        default=None,
        description="Typical occupancy of the building in number of people.",
    )


class ThermalSystemResult(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    conditioned_area: float | None = Field(
        default=None,
        description="Conditioned equivalent area in m2 (conditioned area adjusted for simultaneity).",
    )
    energy_demand: float | None = Field(
        default=None,
        description="Energy demanded in kWh/m2 when specific zonal occupancy is simulated.",
    )
    system_usage: float | None = Field(
        default=None,
        description="Energy demand divided by conditioned area (no simultaneity adjustment) in kWh/m2",
    )
    peak_load: HourPeak | None = None
    peak_capacity: float | None = Field(
        default=None, description="Maximum hourly energy consumed in kW."
    )
    peak_ratio: float | None = Field(
        default=None,
        description="Sensible peak capacity for cooling systems. For heating systems, peak capacity/load at 99.6th percentile.",
    )


class HeatingSimulationOutput(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    heating_systems: list[ThermalSystemResult] | None = None


class CoolingSimulationOutput(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    cooling_systems: list[ThermalSystemResult] | None = None


class OpenBESOutput(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    location_simulation_output: LocationSimulationOutput | None = None
    geometry_simulation_output: GeometrySimulationOutput | None = None
    climate_simulation_output: ClimateSimulationOutput | None = None
    ventilation_simulation_output: VentilationSimulationOutput | None = None
    heating_simulation_output: HeatingSimulationOutput | None = None
    cooling_simulation_output: CoolingSimulationOutput | None = None
    lighting_simulation_output: LightingSimulationOutput | None = None
    hot_water_simulation_output: HotWaterSimulationOutput | None = None
    building_energy_simulation_output: BuildingEnergySimulationOutput | None = None


class OpenBESCase(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    inputs: OpenBESSpecification
    outputs: OpenBESOutput | None = None
    log: list[str] | None = Field(
        default=None, description="Log output from the simulation run."
    )
    meta: OpenBESMetaData | None = None
