mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-04-19 00:45:22 +00:00
Documentation: Support nested config
* Add examples to pydantic models.
This commit is contained in:
parent
be26457563
commit
d74a56b75a
File diff suppressed because it is too large
Load Diff
3
docs/_static/eos.css
vendored
Normal file
3
docs/_static/eos.css
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.wy-nav-content {
|
||||
max-width: 90% !important;
|
||||
}
|
@ -99,6 +99,7 @@ html_theme_options = {
|
||||
"logo_only": False,
|
||||
"titles_only": True,
|
||||
}
|
||||
html_css_files = ["eos.css"]
|
||||
|
||||
# -- Options for autodoc -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html
|
||||
|
15696
openapi.json
15696
openapi.json
File diff suppressed because it is too large
Load Diff
@ -2,74 +2,237 @@
|
||||
"""Utility functions for Configuration specification generation."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import textwrap
|
||||
from typing import Any, Union
|
||||
|
||||
from pydantic.fields import ComputedFieldInfo, FieldInfo
|
||||
from pydantic_core import PydanticUndefined
|
||||
|
||||
from akkudoktoreos.config.config import get_config
|
||||
from akkudoktoreos.core.logging import get_logger
|
||||
from akkudoktoreos.core.pydantic import PydanticBaseModel
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
config_eos = get_config()
|
||||
|
||||
# Fixed set of prefixes to filter configuration values and their respective titles
|
||||
CONFIG_PREFIXES = {
|
||||
"battery": "Battery Device Simulation Configuration",
|
||||
"bev": "Battery Electric Vehicle Device Simulation Configuration",
|
||||
"dishwasher": "Dishwasher Device Simulation Configuration",
|
||||
"inverter": "Inverter Device Simulation Configuration",
|
||||
"measurement": "Measurement Configuration",
|
||||
"optimization": "General Optimization Configuration",
|
||||
"server": "Server Configuration",
|
||||
"elecprice": "Electricity Price Prediction Configuration",
|
||||
"load": "Load Prediction Configuration",
|
||||
"logging": "Logging Configuration",
|
||||
"prediction": "General Prediction Configuration",
|
||||
"pvforecast": "PV Forecast Configuration",
|
||||
"weather": "Weather Forecast Configuration",
|
||||
}
|
||||
|
||||
# Static set of configuration names to include in a separate table
|
||||
GENERAL_CONFIGS = [
|
||||
"config_default_file_path",
|
||||
"config_file_path",
|
||||
"config_folder_path",
|
||||
"config_keys",
|
||||
"config_keys_read_only",
|
||||
"data_cache_path",
|
||||
"data_cache_subpath",
|
||||
"data_folder_path",
|
||||
"data_output_path",
|
||||
"data_output_subpath",
|
||||
"latitude",
|
||||
"longitude",
|
||||
"package_root_path",
|
||||
"timezone",
|
||||
]
|
||||
documented_types: set[PydanticBaseModel] = set()
|
||||
undocumented_types: dict[PydanticBaseModel, tuple[str, list[str]]] = dict()
|
||||
|
||||
|
||||
def generate_config_table_md(configs, title):
|
||||
def get_title(config: PydanticBaseModel) -> str:
|
||||
if config.__doc__ is None:
|
||||
raise NameError(f"Missing docstring: {config}")
|
||||
return config.__doc__.strip().splitlines()[0].strip(".")
|
||||
|
||||
|
||||
def get_body(config: PydanticBaseModel) -> str:
|
||||
if config.__doc__ is None:
|
||||
raise NameError(f"Missing docstring: {config}")
|
||||
return textwrap.dedent("\n".join(config.__doc__.strip().splitlines()[1:])).strip()
|
||||
|
||||
|
||||
def resolve_nested_types(field_type: Any, parent_types: list[str]) -> list[tuple[Any, list[str]]]:
|
||||
resolved_types: list[tuple[type, list[str]]] = []
|
||||
|
||||
origin = getattr(field_type, "__origin__", field_type)
|
||||
if origin is Union:
|
||||
for arg in getattr(field_type, "__args__", []):
|
||||
resolved_types.extend(resolve_nested_types(arg, parent_types))
|
||||
elif origin is list:
|
||||
for arg in getattr(field_type, "__args__", []):
|
||||
resolved_types.extend(resolve_nested_types(arg, parent_types + ["list"]))
|
||||
else:
|
||||
resolved_types.append((field_type, parent_types))
|
||||
|
||||
return resolved_types
|
||||
|
||||
|
||||
def get_example_or_default(field_name: str, field_info: FieldInfo) -> dict[str, Any]:
|
||||
"""Generate a default value for a field, considering constraints."""
|
||||
if field_info.examples is not None:
|
||||
return field_info.examples[0]
|
||||
|
||||
if field_info.default is not None:
|
||||
return field_info.default
|
||||
|
||||
raise NotImplementedError(f"No default or example provided '{field_name}': {field_info}")
|
||||
|
||||
|
||||
def create_model_from_examples(model_class: PydanticBaseModel) -> PydanticBaseModel:
|
||||
"""Create a model instance with default or example values, respecting constraints."""
|
||||
example_data = {}
|
||||
for field_name, field_info in model_class.model_fields.items():
|
||||
example_data[field_name] = get_example_or_default(field_name, field_info)
|
||||
return model_class(**example_data)
|
||||
|
||||
|
||||
def build_nested_structure(keys: list[str], value: Any) -> Any:
|
||||
if not keys:
|
||||
return value
|
||||
|
||||
current_key = keys[0]
|
||||
if current_key == "list":
|
||||
return [build_nested_structure(keys[1:], value)]
|
||||
else:
|
||||
return {current_key: build_nested_structure(keys[1:], value)}
|
||||
|
||||
|
||||
def get_default_value(field_info: Union[FieldInfo, ComputedFieldInfo], regular_field: bool) -> Any:
|
||||
default_value = ""
|
||||
if regular_field:
|
||||
if (val := field_info.default) is not PydanticUndefined:
|
||||
default_value = val
|
||||
else:
|
||||
default_value = "required"
|
||||
else:
|
||||
default_value = "N/A"
|
||||
return default_value
|
||||
|
||||
|
||||
def get_type_name(field_type: type) -> str:
|
||||
type_name = str(field_type).replace("typing.", "")
|
||||
if type_name.startswith("<class"):
|
||||
type_name = field_type.__name__
|
||||
return type_name
|
||||
|
||||
|
||||
def generate_config_table_md(
|
||||
config: PydanticBaseModel,
|
||||
toplevel_keys: list[str],
|
||||
prefix: str,
|
||||
toplevel: bool = False,
|
||||
extra_config: bool = False,
|
||||
) -> str:
|
||||
"""Generate a markdown table for given configurations.
|
||||
|
||||
Args:
|
||||
configs (dict): Configuration values with keys and their descriptions.
|
||||
title (str): Title for the table.
|
||||
config (PydanticBaseModel): PydanticBaseModel configuration definition.
|
||||
prefix (str): Prefix for table entries.
|
||||
|
||||
Returns:
|
||||
str: The markdown table as a string.
|
||||
"""
|
||||
if not configs:
|
||||
return ""
|
||||
table = ""
|
||||
if toplevel:
|
||||
title = get_title(config)
|
||||
|
||||
heading_level = "###" if extra_config else "##"
|
||||
env_header = ""
|
||||
env_header_underline = ""
|
||||
env_width = ""
|
||||
if not extra_config:
|
||||
env_header = "| Environment Variable "
|
||||
env_header_underline = "| -------------------- "
|
||||
env_width = "20 "
|
||||
|
||||
table += f"{heading_level} {title}\n\n"
|
||||
|
||||
body = get_body(config)
|
||||
if body:
|
||||
table += body
|
||||
table += "\n\n"
|
||||
|
||||
table += (
|
||||
":::{table} "
|
||||
+ f"{'::'.join(toplevel_keys)}\n:widths: 10 {env_width}10 5 5 30\n:align: left\n\n"
|
||||
)
|
||||
table += f"| Name {env_header}| Type | Read-Only | Default | Description |\n"
|
||||
table += f"| ---- {env_header_underline}| ---- | --------- | ------- | ----------- |\n"
|
||||
|
||||
for field_name, field_info in list(config.model_fields.items()) + list(
|
||||
config.model_computed_fields.items()
|
||||
):
|
||||
regular_field = isinstance(field_info, FieldInfo)
|
||||
|
||||
config_name = field_name if extra_config else field_name.upper()
|
||||
field_type = field_info.annotation if regular_field else field_info.return_type
|
||||
default_value = get_default_value(field_info, regular_field)
|
||||
description = field_info.description if field_info.description else "-"
|
||||
read_only = "rw" if regular_field else "ro"
|
||||
type_name = get_type_name(field_type)
|
||||
|
||||
env_entry = ""
|
||||
if not extra_config:
|
||||
if regular_field:
|
||||
env_entry = f"| `{prefix}{config_name}` "
|
||||
else:
|
||||
env_entry = "| "
|
||||
table += f"| {field_name} {env_entry}| `{type_name}` | `{read_only}` | `{default_value}` | {description} |\n"
|
||||
|
||||
inner_types: dict[PydanticBaseModel, tuple[str, list[str]]] = dict()
|
||||
|
||||
def extract_nested_models(subtype: Any, subprefix: str, parent_types: list[str]):
|
||||
if subtype in inner_types.keys():
|
||||
return
|
||||
nested_types = resolve_nested_types(subtype, [])
|
||||
for nested_type, nested_parent_types in nested_types:
|
||||
if issubclass(nested_type, PydanticBaseModel):
|
||||
new_parent_types = parent_types + nested_parent_types
|
||||
if "list" in parent_types:
|
||||
new_prefix = ""
|
||||
else:
|
||||
new_prefix = f"{subprefix}"
|
||||
inner_types.setdefault(nested_type, (new_prefix, new_parent_types))
|
||||
for nested_field_name, nested_field_info in list(
|
||||
nested_type.model_fields.items()
|
||||
) + list(nested_type.model_computed_fields.items()):
|
||||
nested_field_type = nested_field_info.annotation
|
||||
if new_prefix:
|
||||
new_prefix += f"{nested_field_name.upper()}__"
|
||||
extract_nested_models(
|
||||
nested_field_type,
|
||||
new_prefix,
|
||||
new_parent_types + [nested_field_name],
|
||||
)
|
||||
|
||||
extract_nested_models(field_type, f"{prefix}{config_name}__", toplevel_keys + [field_name])
|
||||
|
||||
for new_type, info in inner_types.items():
|
||||
if new_type not in documented_types:
|
||||
undocumented_types.setdefault(new_type, (info[0], info[1]))
|
||||
|
||||
if toplevel:
|
||||
table += ":::\n\n" # Add an empty line after the table
|
||||
|
||||
ins = create_model_from_examples(config)
|
||||
if ins is not None:
|
||||
# Transform to JSON (and manually to dict) to use custom serializers and then merge with parent keys
|
||||
ins_json = ins.model_dump_json(include_computed_fields=False)
|
||||
ins_dict = json.loads(ins_json)
|
||||
|
||||
ins_out_json = ins.model_dump_json(include_computed_fields=True)
|
||||
ins_out_dict = json.loads(ins_out_json)
|
||||
same_output = ins_out_dict == ins_dict
|
||||
same_output_str = "/Output" if same_output else ""
|
||||
|
||||
table += f"#{heading_level} Example Input{same_output_str}\n\n"
|
||||
table += "```{eval-rst}\n"
|
||||
table += ".. code-block:: json\n\n"
|
||||
input_dict = build_nested_structure(toplevel_keys, ins_dict)
|
||||
table += textwrap.indent(json.dumps(input_dict, indent=4), " ")
|
||||
table += "\n"
|
||||
table += "```\n\n"
|
||||
|
||||
if not same_output:
|
||||
table += f"#{heading_level} Example Output\n\n"
|
||||
table += "```{eval-rst}\n"
|
||||
table += ".. code-block:: json\n\n"
|
||||
output_dict = build_nested_structure(toplevel_keys, ins_out_dict)
|
||||
table += textwrap.indent(json.dumps(output_dict, indent=4), " ")
|
||||
table += "\n"
|
||||
table += "```\n\n"
|
||||
|
||||
while undocumented_types:
|
||||
extra_config_type, extra_info = undocumented_types.popitem()
|
||||
documented_types.add(extra_config_type)
|
||||
table += generate_config_table_md(
|
||||
extra_config_type, extra_info[1], extra_info[0], True, True
|
||||
)
|
||||
|
||||
table = f"## {title}\n\n"
|
||||
table += ":::{table} " + f"{title}\n:widths: 10 10 5 5 30\n:align: left\n\n"
|
||||
table += "| Name | Type | Read-Only | Default | Description |\n"
|
||||
table += "| ---- | ---- | --------- | ------- | ----------- |\n"
|
||||
for name, config in sorted(configs.items()):
|
||||
type_name = config["type"]
|
||||
if type_name.startswith("typing."):
|
||||
type_name = type_name[len("typing.") :]
|
||||
table += f"| `{config['name']}` | `{type_name}` | `{config['read-only']}` | `{config['default']}` | {config['description']} |\n"
|
||||
table += ":::\n\n" # Add an empty line after the table
|
||||
return table
|
||||
|
||||
|
||||
@ -79,57 +242,16 @@ def generate_config_md() -> str:
|
||||
Returns:
|
||||
str: The Markdown representation of the configuration spec.
|
||||
"""
|
||||
# FIXME: Support for nested
|
||||
configs = {}
|
||||
config_keys = config_eos.model_fields_set
|
||||
# config_keys_read_only = config_eos.config_keys_read_only
|
||||
config_keys_read_only: list[str] = []
|
||||
for config_key in config_keys:
|
||||
config = {}
|
||||
config["name"] = config_key
|
||||
config["value"] = getattr(config_eos, config_key)
|
||||
|
||||
if config_key in config_keys_read_only:
|
||||
config["read-only"] = "ro"
|
||||
computed_field_info = config_eos.__pydantic_decorators__.computed_fields[
|
||||
config_key
|
||||
].info
|
||||
config["default"] = "N/A"
|
||||
config["description"] = computed_field_info.description
|
||||
config["type"] = str(computed_field_info.return_type)
|
||||
else:
|
||||
config["read-only"] = "rw"
|
||||
field_info = config_eos.model_fields[config_key]
|
||||
config["default"] = field_info.default
|
||||
config["description"] = field_info.description
|
||||
config["type"] = str(field_info.annotation)
|
||||
|
||||
configs[config_key] = config
|
||||
|
||||
# Generate markdown for the main table
|
||||
markdown = "# Configuration Table\n\n"
|
||||
|
||||
# Generate table for general configuration names
|
||||
general_configs = {k: v for k, v in configs.items() if k in GENERAL_CONFIGS}
|
||||
for k in general_configs.keys():
|
||||
del configs[k] # Remove general configs from the main configs dictionary
|
||||
markdown += generate_config_table_md(general_configs, "General Configuration Values")
|
||||
# Generate tables for each top level config
|
||||
for field_name, field_info in config_eos.model_fields.items():
|
||||
field_type = field_info.annotation
|
||||
markdown += generate_config_table_md(
|
||||
field_type, [field_name], f"EOS_{field_name.upper()}__", True
|
||||
)
|
||||
|
||||
non_prefixed_configs = {k: v for k, v in configs.items()}
|
||||
|
||||
# Generate tables for each prefix (sorted by value) and remove prefixed configs from the main dictionary
|
||||
sorted_prefixes = sorted(CONFIG_PREFIXES.items(), key=lambda item: item[1])
|
||||
for prefix, title in sorted_prefixes:
|
||||
prefixed_configs = {k: v for k, v in configs.items() if k.startswith(prefix)}
|
||||
for k in prefixed_configs.keys():
|
||||
del non_prefixed_configs[k]
|
||||
markdown += generate_config_table_md(prefixed_configs, title)
|
||||
|
||||
# Generate markdown for the remaining non-prefixed configs if any
|
||||
if non_prefixed_configs:
|
||||
markdown += generate_config_table_md(non_prefixed_configs, "Other Configuration Values")
|
||||
|
||||
# Assure the is no double \n at end of file
|
||||
# Assure there is no double \n at end of file
|
||||
markdown = markdown.rstrip("\n")
|
||||
markdown += "\n"
|
||||
|
||||
|
@ -63,10 +63,13 @@ def get_absolute_path(
|
||||
|
||||
|
||||
class ConfigCommonSettings(SettingsBaseModel):
|
||||
"""Settings for common configuration."""
|
||||
"""Settings for common configuration.
|
||||
|
||||
General configuration to set directories of cache and output files.
|
||||
"""
|
||||
|
||||
data_folder_path: Optional[Path] = Field(
|
||||
default=None, description="Path to EOS data directory."
|
||||
default=None, description="Path to EOS data directory.", examples=[None, "/home/eos/data"]
|
||||
)
|
||||
|
||||
data_output_subpath: Optional[Path] = Field(
|
||||
|
@ -14,10 +14,12 @@ from akkudoktoreos.core.logabc import logging_str_to_level
|
||||
|
||||
|
||||
class LoggingCommonSettings(SettingsBaseModel):
|
||||
"""Common settings for logging."""
|
||||
"""Logging Configuration."""
|
||||
|
||||
logging_level_default: Optional[str] = Field(
|
||||
default=None, description="EOS default logging level."
|
||||
default=None,
|
||||
description="EOS default logging level.",
|
||||
examples=["INFO", "DEBUG", "WARNING", "ERROR", "CRITICAL"],
|
||||
)
|
||||
|
||||
# Validators
|
||||
|
@ -128,9 +128,16 @@ class PydanticBaseModel(BaseModel):
|
||||
return value
|
||||
|
||||
# Override Pydantic’s serialization for all DateTime fields
|
||||
def model_dump(self, *args: Any, **kwargs: Any) -> dict:
|
||||
def model_dump(
|
||||
self, *args: Any, include_computed_fields: bool = True, **kwargs: Any
|
||||
) -> dict[str, Any]:
|
||||
"""Custom dump method to handle serialization for DateTime fields."""
|
||||
result = super().model_dump(*args, **kwargs)
|
||||
|
||||
if not include_computed_fields:
|
||||
for computed_field_name in self.model_computed_fields:
|
||||
result.pop(computed_field_name, None)
|
||||
|
||||
for key, value in result.items():
|
||||
if isinstance(value, pendulum.DateTime):
|
||||
result[key] = PydanticTypeAdapterDateTime.serialize(value)
|
||||
@ -185,6 +192,10 @@ class PydanticBaseModel(BaseModel):
|
||||
"""
|
||||
return cls.model_validate(data)
|
||||
|
||||
def model_dump_json(self, *args: Any, indent: Optional[int] = None, **kwargs: Any) -> str:
|
||||
data = self.model_dump(*args, **kwargs)
|
||||
return json.dumps(data, indent=indent, default=str)
|
||||
|
||||
def to_json(self) -> str:
|
||||
"""Convert the PydanticBaseModel instance to a JSON string.
|
||||
|
||||
|
@ -25,15 +25,26 @@ def max_charging_power_field(description: Optional[str] = None) -> float:
|
||||
|
||||
|
||||
def initial_soc_percentage_field(description: str) -> int:
|
||||
return Field(default=0, ge=0, le=100, description=description)
|
||||
return Field(default=0, ge=0, le=100, description=description, examples=[42])
|
||||
|
||||
|
||||
def discharging_efficiency_field(default_value: float) -> float:
|
||||
return Field(
|
||||
default=default_value,
|
||||
gt=0,
|
||||
le=1,
|
||||
description="A float representing the discharge efficiency of the battery.",
|
||||
)
|
||||
|
||||
|
||||
class BaseBatteryParameters(DeviceParameters):
|
||||
"""Base class for battery parameters with fields for capacity, efficiency, and state of charge."""
|
||||
"""Battery Device Simulation Configuration."""
|
||||
|
||||
device_id: str = Field(description="ID of battery")
|
||||
device_id: str = Field(description="ID of battery", examples=["battery1"])
|
||||
capacity_wh: int = Field(
|
||||
gt=0, description="An integer representing the capacity of the battery in watt-hours."
|
||||
gt=0,
|
||||
description="An integer representing the capacity of the battery in watt-hours.",
|
||||
examples=[8000],
|
||||
)
|
||||
charging_efficiency: float = Field(
|
||||
default=0.88,
|
||||
@ -41,12 +52,7 @@ class BaseBatteryParameters(DeviceParameters):
|
||||
le=1,
|
||||
description="A float representing the charging efficiency of the battery.",
|
||||
)
|
||||
discharging_efficiency: float = Field(
|
||||
default=0.88,
|
||||
gt=0,
|
||||
le=1,
|
||||
description="A float representing the discharge efficiency of the battery.",
|
||||
)
|
||||
discharging_efficiency: float = discharging_efficiency_field(0.88)
|
||||
max_charge_power_w: Optional[float] = max_charging_power_field()
|
||||
initial_soc_percentage: int = initial_soc_percentage_field(
|
||||
"An integer representing the state of charge of the battery at the **start** of the current hour (not the current state)."
|
||||
@ -56,6 +62,7 @@ class BaseBatteryParameters(DeviceParameters):
|
||||
ge=0,
|
||||
le=100,
|
||||
description="An integer representing the minimum state of charge (SOC) of the battery in percentage.",
|
||||
examples=[10],
|
||||
)
|
||||
max_soc_percentage: int = Field(
|
||||
default=100,
|
||||
@ -70,10 +77,10 @@ class SolarPanelBatteryParameters(BaseBatteryParameters):
|
||||
|
||||
|
||||
class ElectricVehicleParameters(BaseBatteryParameters):
|
||||
"""Parameters specific to an electric vehicle (EV)."""
|
||||
"""Battery Electric Vehicle Device Simulation Configuration."""
|
||||
|
||||
device_id: str = Field(description="ID of electric vehicle")
|
||||
discharging_efficiency: float = 1.0
|
||||
device_id: str = Field(description="ID of electric vehicle", examples=["ev1"])
|
||||
discharging_efficiency: float = discharging_efficiency_field(1.0)
|
||||
initial_soc_percentage: int = initial_soc_percentage_field(
|
||||
"An integer representing the current state of charge (SOC) of the battery in percentage."
|
||||
)
|
||||
@ -82,7 +89,7 @@ class ElectricVehicleParameters(BaseBatteryParameters):
|
||||
class ElectricVehicleResult(DeviceOptimizeResult):
|
||||
"""Result class containing information related to the electric vehicle's charging and discharging behavior."""
|
||||
|
||||
device_id: str = Field(description="ID of electric vehicle")
|
||||
device_id: str = Field(description="ID of electric vehicle", examples=["ev1"])
|
||||
charge_array: list[float] = Field(
|
||||
description="Hourly charging status (0 for no charging, 1 for charging)."
|
||||
)
|
||||
|
@ -19,20 +19,19 @@ from akkudoktoreos.utils.datetimeutil import to_duration
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
# class DeviceParameters(PydanticBaseModel):
|
||||
class DeviceParameters(ParametersBaseModel):
|
||||
device_id: str = Field(description="ID of device")
|
||||
device_id: str = Field(description="ID of device", examples="device1")
|
||||
hours: Optional[int] = Field(
|
||||
default=None,
|
||||
gt=0,
|
||||
description="Number of prediction hours. Defaults to global config prediction hours.",
|
||||
examples=[None],
|
||||
)
|
||||
|
||||
|
||||
# class DeviceOptimizeResult(PydanticBaseModel):
|
||||
class DeviceOptimizeResult(ParametersBaseModel):
|
||||
device_id: str = Field(description="ID of device")
|
||||
hours: int = Field(gt=0, description="Number of hours in the simulation.")
|
||||
device_id: str = Field(description="ID of device", examples=["device1"])
|
||||
hours: int = Field(gt=0, description="Number of hours in the simulation.", examples=[24])
|
||||
|
||||
|
||||
class DeviceState(Enum):
|
||||
|
@ -10,14 +10,18 @@ logger = get_logger(__name__)
|
||||
|
||||
|
||||
class HomeApplianceParameters(DeviceParameters):
|
||||
device_id: str = Field(description="ID of home appliance")
|
||||
"""Home Appliance Device Simulation Configuration."""
|
||||
|
||||
device_id: str = Field(description="ID of home appliance", examples=["dishwasher"])
|
||||
consumption_wh: int = Field(
|
||||
gt=0,
|
||||
description="An integer representing the energy consumption of a household device in watt-hours.",
|
||||
examples=[2000],
|
||||
)
|
||||
duration_h: int = Field(
|
||||
gt=0,
|
||||
description="An integer representing the usage duration of a household device in hours.",
|
||||
examples=[3],
|
||||
)
|
||||
|
||||
|
||||
|
@ -10,9 +10,13 @@ logger = get_logger(__name__)
|
||||
|
||||
|
||||
class InverterParameters(DeviceParameters):
|
||||
device_id: str = Field(description="ID of inverter")
|
||||
max_power_wh: float = Field(gt=0)
|
||||
battery: Optional[str] = Field(default=None, description="ID of battery")
|
||||
"""Inverter Device Simulation Configuration."""
|
||||
|
||||
device_id: str = Field(description="ID of inverter", examples=["inverter1"])
|
||||
max_power_wh: float = Field(gt=0, examples=[10000])
|
||||
battery: Optional[str] = Field(
|
||||
default=None, description="ID of battery", examples=[None, "battery1"]
|
||||
)
|
||||
|
||||
|
||||
class Inverter(DeviceBase):
|
||||
|
@ -15,11 +15,13 @@ class DevicesCommonSettings(SettingsBaseModel):
|
||||
"""Base configuration for devices simulation settings."""
|
||||
|
||||
batteries: Optional[list[BaseBatteryParameters]] = Field(
|
||||
default=None, description="List of battery/ev devices"
|
||||
default=None,
|
||||
description="List of battery/ev devices",
|
||||
examples=[[{"device_id": "battery1", "capacity_wh": 8000}]],
|
||||
)
|
||||
inverters: Optional[list[InverterParameters]] = Field(
|
||||
default=None, description="List of inverters"
|
||||
default=None, description="List of inverters", examples=[[]]
|
||||
)
|
||||
home_appliances: Optional[list[HomeApplianceParameters]] = Field(
|
||||
default=None, description="List of home appliances"
|
||||
default=None, description="List of home appliances", examples=[[]]
|
||||
)
|
||||
|
@ -23,20 +23,22 @@ logger = get_logger(__name__)
|
||||
|
||||
|
||||
class MeasurementCommonSettings(SettingsBaseModel):
|
||||
"""Measurement Configuration."""
|
||||
|
||||
measurement_load0_name: Optional[str] = Field(
|
||||
default=None, description="Name of the load0 source (e.g. 'Household', 'Heat Pump')"
|
||||
default=None, description="Name of the load0 source", examples=["Household", "Heat Pump"]
|
||||
)
|
||||
measurement_load1_name: Optional[str] = Field(
|
||||
default=None, description="Name of the load1 source (e.g. 'Household', 'Heat Pump')"
|
||||
default=None, description="Name of the load1 source", examples=[None]
|
||||
)
|
||||
measurement_load2_name: Optional[str] = Field(
|
||||
default=None, description="Name of the load2 source (e.g. 'Household', 'Heat Pump')"
|
||||
default=None, description="Name of the load2 source", examples=[None]
|
||||
)
|
||||
measurement_load3_name: Optional[str] = Field(
|
||||
default=None, description="Name of the load3 source (e.g. 'Household', 'Heat Pump')"
|
||||
default=None, description="Name of the load3 source", examples=[None]
|
||||
)
|
||||
measurement_load4_name: Optional[str] = Field(
|
||||
default=None, description="Name of the load4 source (e.g. 'Household', 'Heat Pump')"
|
||||
default=None, description="Name of the load4 source", examples=[None]
|
||||
)
|
||||
|
||||
|
||||
@ -49,29 +51,29 @@ class MeasurementDataRecord(DataRecord):
|
||||
|
||||
# Single loads, to be aggregated to total load
|
||||
measurement_load0_mr: Optional[float] = Field(
|
||||
default=None, ge=0, description="Load0 meter reading [kWh]"
|
||||
default=None, ge=0, description="Load0 meter reading [kWh]", examples=[40421]
|
||||
)
|
||||
measurement_load1_mr: Optional[float] = Field(
|
||||
default=None, ge=0, description="Load1 meter reading [kWh]"
|
||||
default=None, ge=0, description="Load1 meter reading [kWh]", examples=[None]
|
||||
)
|
||||
measurement_load2_mr: Optional[float] = Field(
|
||||
default=None, ge=0, description="Load2 meter reading [kWh]"
|
||||
default=None, ge=0, description="Load2 meter reading [kWh]", examples=[None]
|
||||
)
|
||||
measurement_load3_mr: Optional[float] = Field(
|
||||
default=None, ge=0, description="Load3 meter reading [kWh]"
|
||||
default=None, ge=0, description="Load3 meter reading [kWh]", examples=[None]
|
||||
)
|
||||
measurement_load4_mr: Optional[float] = Field(
|
||||
default=None, ge=0, description="Load4 meter reading [kWh]"
|
||||
default=None, ge=0, description="Load4 meter reading [kWh]", examples=[None]
|
||||
)
|
||||
|
||||
measurement_max_loads: ClassVar[int] = 5 # Maximum number of loads that can be set
|
||||
|
||||
measurement_grid_export_mr: Optional[float] = Field(
|
||||
default=None, ge=0, description="Export to grid meter reading [kWh]"
|
||||
default=None, ge=0, description="Export to grid meter reading [kWh]", examples=[1000]
|
||||
)
|
||||
|
||||
measurement_grid_import_mr: Optional[float] = Field(
|
||||
default=None, ge=0, description="Import from grid meter reading [kWh]"
|
||||
default=None, ge=0, description="Import from grid meter reading [kWh]", examples=[1000]
|
||||
)
|
||||
|
||||
# Computed fields
|
||||
|
@ -9,7 +9,7 @@ logger = get_logger(__name__)
|
||||
|
||||
|
||||
class OptimizationCommonSettings(SettingsBaseModel):
|
||||
"""Base configuration for optimization settings.
|
||||
"""General Optimization Configuration.
|
||||
|
||||
Attributes:
|
||||
optimization_hours (int): Number of hours for optimizations.
|
||||
|
@ -7,11 +7,17 @@ from akkudoktoreos.prediction.elecpriceimport import ElecPriceImportCommonSettin
|
||||
|
||||
|
||||
class ElecPriceCommonSettings(SettingsBaseModel):
|
||||
"""Electricity Price Prediction Configuration."""
|
||||
|
||||
elecprice_provider: Optional[str] = Field(
|
||||
default=None, description="Electricity price provider id of provider to be used."
|
||||
default=None,
|
||||
description="Electricity price provider id of provider to be used.",
|
||||
examples=["ElecPriceAkkudoktor"],
|
||||
)
|
||||
elecprice_charges_kwh: Optional[float] = Field(
|
||||
default=None, ge=0, description="Electricity price charges (€/kWh)."
|
||||
default=None, ge=0, description="Electricity price charges (€/kWh).", examples=[0.21]
|
||||
)
|
||||
|
||||
provider_settings: Optional[ElecPriceImportCommonSettings] = None
|
||||
provider_settings: Optional[ElecPriceImportCommonSettings] = Field(
|
||||
default=None, description="Provider settings", examples=[None]
|
||||
)
|
||||
|
@ -23,12 +23,15 @@ class ElecPriceImportCommonSettings(SettingsBaseModel):
|
||||
"""Common settings for elecprice data import from file or JSON String."""
|
||||
|
||||
elecpriceimport_file_path: Optional[Union[str, Path]] = Field(
|
||||
default=None, description="Path to the file to import elecprice data from."
|
||||
default=None,
|
||||
description="Path to the file to import elecprice data from.",
|
||||
examples=[None, "/path/to/prices.json"],
|
||||
)
|
||||
|
||||
elecpriceimport_json: Optional[str] = Field(
|
||||
default=None,
|
||||
description="JSON string, dictionary of electricity price forecast value lists.",
|
||||
examples=['{"elecprice_marketprice_wh": [0.0003384, 0.0003318, 0.0003284]}'],
|
||||
)
|
||||
|
||||
# Validators
|
||||
|
@ -13,12 +13,14 @@ logger = get_logger(__name__)
|
||||
|
||||
|
||||
class LoadCommonSettings(SettingsBaseModel):
|
||||
"""Common settings for loaod forecast providers."""
|
||||
"""Load Prediction Configuration."""
|
||||
|
||||
load_provider: Optional[str] = Field(
|
||||
default=None, description="Load provider id of provider to be used."
|
||||
default=None,
|
||||
description="Load provider id of provider to be used.",
|
||||
examples=["LoadAkkudoktor"],
|
||||
)
|
||||
|
||||
provider_settings: Optional[Union[LoadAkkudoktorCommonSettings, LoadImportCommonSettings]] = (
|
||||
None
|
||||
Field(default=None, description="Provider settings", examples=[None])
|
||||
)
|
||||
|
@ -17,7 +17,7 @@ class LoadAkkudoktorCommonSettings(SettingsBaseModel):
|
||||
"""Common settings for load data import from file."""
|
||||
|
||||
loadakkudoktor_year_energy: Optional[float] = Field(
|
||||
default=None, description="Yearly energy consumption (kWh)."
|
||||
default=None, description="Yearly energy consumption (kWh).", examples=[40421]
|
||||
)
|
||||
|
||||
|
||||
|
@ -23,10 +23,14 @@ class LoadImportCommonSettings(SettingsBaseModel):
|
||||
"""Common settings for load data import from file or JSON string."""
|
||||
|
||||
load_import_file_path: Optional[Union[str, Path]] = Field(
|
||||
default=None, description="Path to the file to import load data from."
|
||||
default=None,
|
||||
description="Path to the file to import load data from.",
|
||||
examples=[None, "/path/to/yearly_load.json"],
|
||||
)
|
||||
load_import_json: Optional[str] = Field(
|
||||
default=None, description="JSON string, dictionary of load forecast value lists."
|
||||
default=None,
|
||||
description="JSON string, dictionary of load forecast value lists.",
|
||||
examples=['{"load0_mean": [676.71, 876.19, 527.13]}'],
|
||||
)
|
||||
|
||||
# Validators
|
||||
|
@ -45,7 +45,7 @@ from akkudoktoreos.utils.datetimeutil import to_timezone
|
||||
|
||||
|
||||
class PredictionCommonSettings(SettingsBaseModel):
|
||||
"""Base configuration for prediction settings, including forecast duration, geographic location, and time zone.
|
||||
"""General Prediction Configuration.
|
||||
|
||||
This class provides configuration for prediction settings, allowing users to specify
|
||||
parameters such as the forecast duration (in hours) and location (latitude and longitude).
|
||||
|
@ -12,29 +12,37 @@ logger = get_logger(__name__)
|
||||
|
||||
|
||||
class PVForecastCommonSettings(SettingsBaseModel):
|
||||
"""PV Forecast Configuration."""
|
||||
|
||||
# General plane parameters
|
||||
# https://pvlib-python.readthedocs.io/en/stable/_modules/pvlib/iotools/pvgis.html
|
||||
# Inverter Parameters
|
||||
# https://pvlib-python.readthedocs.io/en/stable/_modules/pvlib/inverter.html
|
||||
|
||||
pvforecast_provider: Optional[str] = Field(
|
||||
default=None, description="PVForecast provider id of provider to be used."
|
||||
default=None,
|
||||
description="PVForecast provider id of provider to be used.",
|
||||
examples=["PVForecastAkkudoktor"],
|
||||
)
|
||||
# pvforecast0_latitude: Optional[float] = Field(default=None, description="Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)")
|
||||
# Plane 0
|
||||
pvforecast0_surface_tilt: Optional[float] = Field(
|
||||
default=None, description="Tilt angle from horizontal plane. Ignored for two-axis tracking."
|
||||
default=None,
|
||||
description="Tilt angle from horizontal plane. Ignored for two-axis tracking.",
|
||||
examples=[10.0],
|
||||
)
|
||||
pvforecast0_surface_azimuth: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).",
|
||||
examples=[10.0],
|
||||
)
|
||||
pvforecast0_userhorizon: Optional[List[float]] = Field(
|
||||
default=None,
|
||||
description="Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.",
|
||||
examples=[[10.0, 20.0, 30.0]],
|
||||
)
|
||||
pvforecast0_peakpower: Optional[float] = Field(
|
||||
default=None, description="Nominal power of PV system in kW."
|
||||
default=None, description="Nominal power of PV system in kW.", examples=[5.0]
|
||||
)
|
||||
pvforecast0_pvtechchoice: Optional[str] = Field(
|
||||
default="crystSi", description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'."
|
||||
@ -49,48 +57,60 @@ class PVForecastCommonSettings(SettingsBaseModel):
|
||||
pvforecast0_trackingtype: Optional[int] = Field(
|
||||
default=None,
|
||||
description="Type of suntracking. 0=fixed, 1=single horizontal axis aligned north-south, 2=two-axis tracking, 3=vertical axis tracking, 4=single horizontal axis aligned east-west, 5=single inclined axis aligned north-south.",
|
||||
examples=[0, 1, 2, 3, 4, 5],
|
||||
)
|
||||
pvforecast0_optimal_surface_tilt: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="Calculate the optimum tilt angle. Ignored for two-axis tracking.",
|
||||
examples=[False],
|
||||
)
|
||||
pvforecast0_optimalangles: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.",
|
||||
examples=[False],
|
||||
)
|
||||
pvforecast0_albedo: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Proportion of the light hitting the ground that it reflects back.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast0_module_model: Optional[str] = Field(
|
||||
default=None, description="Model of the PV modules of this plane."
|
||||
default=None, description="Model of the PV modules of this plane.", examples=[None]
|
||||
)
|
||||
pvforecast0_inverter_model: Optional[str] = Field(
|
||||
default=None, description="Model of the inverter of this plane."
|
||||
default=None, description="Model of the inverter of this plane.", examples=[None]
|
||||
)
|
||||
pvforecast0_inverter_paco: Optional[int] = Field(
|
||||
default=None, description="AC power rating of the inverter. [W]"
|
||||
default=None, description="AC power rating of the inverter. [W]", examples=[6000]
|
||||
)
|
||||
pvforecast0_modules_per_string: Optional[int] = Field(
|
||||
default=None, description="Number of the PV modules of the strings of this plane."
|
||||
default=None,
|
||||
description="Number of the PV modules of the strings of this plane.",
|
||||
examples=[20],
|
||||
)
|
||||
pvforecast0_strings_per_inverter: Optional[int] = Field(
|
||||
default=None, description="Number of the strings of the inverter of this plane."
|
||||
default=None,
|
||||
description="Number of the strings of the inverter of this plane.",
|
||||
examples=[2],
|
||||
)
|
||||
# Plane 1
|
||||
pvforecast1_surface_tilt: Optional[float] = Field(
|
||||
default=None, description="Tilt angle from horizontal plane. Ignored for two-axis tracking."
|
||||
default=None,
|
||||
description="Tilt angle from horizontal plane. Ignored for two-axis tracking.",
|
||||
examples=[20.0],
|
||||
)
|
||||
pvforecast1_surface_azimuth: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).",
|
||||
examples=[20.0],
|
||||
)
|
||||
pvforecast1_userhorizon: Optional[List[float]] = Field(
|
||||
default=None,
|
||||
description="Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.",
|
||||
examples=[[5.0, 15.0, 25.0]],
|
||||
)
|
||||
pvforecast1_peakpower: Optional[float] = Field(
|
||||
default=None, description="Nominal power of PV system in kW."
|
||||
default=None, description="Nominal power of PV system in kW.", examples=[3.5]
|
||||
)
|
||||
pvforecast1_pvtechchoice: Optional[str] = Field(
|
||||
default="crystSi", description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'."
|
||||
@ -105,262 +125,332 @@ class PVForecastCommonSettings(SettingsBaseModel):
|
||||
pvforecast1_trackingtype: Optional[int] = Field(
|
||||
default=None,
|
||||
description="Type of suntracking. 0=fixed, 1=single horizontal axis aligned north-south, 2=two-axis tracking, 3=vertical axis tracking, 4=single horizontal axis aligned east-west, 5=single inclined axis aligned north-south.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast1_optimal_surface_tilt: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="Calculate the optimum tilt angle. Ignored for two-axis tracking.",
|
||||
examples=[False],
|
||||
)
|
||||
pvforecast1_optimalangles: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.",
|
||||
examples=[False],
|
||||
)
|
||||
pvforecast1_albedo: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Proportion of the light hitting the ground that it reflects back.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast1_module_model: Optional[str] = Field(
|
||||
default=None, description="Model of the PV modules of this plane."
|
||||
default=None, description="Model of the PV modules of this plane.", examples=[None]
|
||||
)
|
||||
pvforecast1_inverter_model: Optional[str] = Field(
|
||||
default=None, description="Model of the inverter of this plane."
|
||||
default=None, description="Model of the inverter of this plane.", examples=[None]
|
||||
)
|
||||
pvforecast1_inverter_paco: Optional[int] = Field(
|
||||
default=None, description="AC power rating of the inverter. [W]"
|
||||
default=None, description="AC power rating of the inverter. [W]", examples=[4000]
|
||||
)
|
||||
pvforecast1_modules_per_string: Optional[int] = Field(
|
||||
default=None, description="Number of the PV modules of the strings of this plane."
|
||||
default=None,
|
||||
description="Number of the PV modules of the strings of this plane.",
|
||||
examples=[20],
|
||||
)
|
||||
pvforecast1_strings_per_inverter: Optional[int] = Field(
|
||||
default=None, description="Number of the strings of the inverter of this plane."
|
||||
default=None,
|
||||
description="Number of the strings of the inverter of this plane.",
|
||||
examples=[2],
|
||||
)
|
||||
# Plane 2
|
||||
pvforecast2_surface_tilt: Optional[float] = Field(
|
||||
default=None, description="Tilt angle from horizontal plane. Ignored for two-axis tracking."
|
||||
default=None,
|
||||
description="Tilt angle from horizontal plane. Ignored for two-axis tracking.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast2_surface_azimuth: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast2_userhorizon: Optional[List[float]] = Field(
|
||||
default=None,
|
||||
description="Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast2_peakpower: Optional[float] = Field(
|
||||
default=None, description="Nominal power of PV system in kW."
|
||||
default=None, description="Nominal power of PV system in kW.", examples=[None]
|
||||
)
|
||||
pvforecast2_pvtechchoice: Optional[str] = Field(
|
||||
default="crystSi", description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'."
|
||||
default="crystSi",
|
||||
description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast2_mountingplace: Optional[str] = Field(
|
||||
default="free",
|
||||
description="Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast2_loss: Optional[float] = Field(
|
||||
default=14.0, description="Sum of PV system losses in percent"
|
||||
default=14.0, description="Sum of PV system losses in percent", examples=[None]
|
||||
)
|
||||
pvforecast2_trackingtype: Optional[int] = Field(
|
||||
default=None,
|
||||
description="Type of suntracking. 0=fixed, 1=single horizontal axis aligned north-south, 2=two-axis tracking, 3=vertical axis tracking, 4=single horizontal axis aligned east-west, 5=single inclined axis aligned north-south.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast2_optimal_surface_tilt: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="Calculate the optimum tilt angle. Ignored for two-axis tracking.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast2_optimalangles: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast2_albedo: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Proportion of the light hitting the ground that it reflects back.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast2_module_model: Optional[str] = Field(
|
||||
default=None, description="Model of the PV modules of this plane."
|
||||
default=None, description="Model of the PV modules of this plane.", examples=[None]
|
||||
)
|
||||
pvforecast2_inverter_model: Optional[str] = Field(
|
||||
default=None, description="Model of the inverter of this plane."
|
||||
default=None, description="Model of the inverter of this plane.", examples=[None]
|
||||
)
|
||||
pvforecast2_inverter_paco: Optional[int] = Field(
|
||||
default=None, description="AC power rating of the inverter. [W]"
|
||||
default=None, description="AC power rating of the inverter. [W]", examples=[None]
|
||||
)
|
||||
pvforecast2_modules_per_string: Optional[int] = Field(
|
||||
default=None, description="Number of the PV modules of the strings of this plane."
|
||||
default=None,
|
||||
description="Number of the PV modules of the strings of this plane.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast2_strings_per_inverter: Optional[int] = Field(
|
||||
default=None, description="Number of the strings of the inverter of this plane."
|
||||
default=None,
|
||||
description="Number of the strings of the inverter of this plane.",
|
||||
examples=[None],
|
||||
)
|
||||
# Plane 3
|
||||
pvforecast3_surface_tilt: Optional[float] = Field(
|
||||
default=None, description="Tilt angle from horizontal plane. Ignored for two-axis tracking."
|
||||
default=None,
|
||||
description="Tilt angle from horizontal plane. Ignored for two-axis tracking.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast3_surface_azimuth: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast3_userhorizon: Optional[List[float]] = Field(
|
||||
default=None,
|
||||
description="Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast3_peakpower: Optional[float] = Field(
|
||||
default=None, description="Nominal power of PV system in kW."
|
||||
default=None, description="Nominal power of PV system in kW.", examples=[None]
|
||||
)
|
||||
pvforecast3_pvtechchoice: Optional[str] = Field(
|
||||
default="crystSi", description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'."
|
||||
default="crystSi",
|
||||
description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast3_mountingplace: Optional[str] = Field(
|
||||
default="free",
|
||||
description="Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast3_loss: Optional[float] = Field(
|
||||
default=14.0, description="Sum of PV system losses in percent"
|
||||
default=14.0, description="Sum of PV system losses in percent", examples=[None]
|
||||
)
|
||||
pvforecast3_trackingtype: Optional[int] = Field(
|
||||
default=None,
|
||||
description="Type of suntracking. 0=fixed, 1=single horizontal axis aligned north-south, 2=two-axis tracking, 3=vertical axis tracking, 4=single horizontal axis aligned east-west, 5=single inclined axis aligned north-south.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast3_optimal_surface_tilt: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="Calculate the optimum tilt angle. Ignored for two-axis tracking.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast3_optimalangles: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast3_albedo: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Proportion of the light hitting the ground that it reflects back.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast3_module_model: Optional[str] = Field(
|
||||
default=None, description="Model of the PV modules of this plane."
|
||||
default=None, description="Model of the PV modules of this plane.", examples=[None]
|
||||
)
|
||||
pvforecast3_inverter_model: Optional[str] = Field(
|
||||
default=None, description="Model of the inverter of this plane."
|
||||
default=None, description="Model of the inverter of this plane.", examples=[None]
|
||||
)
|
||||
pvforecast3_inverter_paco: Optional[int] = Field(
|
||||
default=None, description="AC power rating of the inverter. [W]"
|
||||
default=None, description="AC power rating of the inverter. [W]", examples=[None]
|
||||
)
|
||||
pvforecast3_modules_per_string: Optional[int] = Field(
|
||||
default=None, description="Number of the PV modules of the strings of this plane."
|
||||
default=None,
|
||||
description="Number of the PV modules of the strings of this plane.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast3_strings_per_inverter: Optional[int] = Field(
|
||||
default=None, description="Number of the strings of the inverter of this plane."
|
||||
default=None,
|
||||
description="Number of the strings of the inverter of this plane.",
|
||||
examples=[None],
|
||||
)
|
||||
# Plane 4
|
||||
pvforecast4_surface_tilt: Optional[float] = Field(
|
||||
default=None, description="Tilt angle from horizontal plane. Ignored for two-axis tracking."
|
||||
default=None,
|
||||
description="Tilt angle from horizontal plane. Ignored for two-axis tracking.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast4_surface_azimuth: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast4_userhorizon: Optional[List[float]] = Field(
|
||||
default=None,
|
||||
description="Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast4_peakpower: Optional[float] = Field(
|
||||
default=None, description="Nominal power of PV system in kW."
|
||||
default=None, description="Nominal power of PV system in kW.", examples=[None]
|
||||
)
|
||||
pvforecast4_pvtechchoice: Optional[str] = Field(
|
||||
default="crystSi", description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'."
|
||||
default="crystSi",
|
||||
description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast4_mountingplace: Optional[str] = Field(
|
||||
default="free",
|
||||
description="Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast4_loss: Optional[float] = Field(
|
||||
default=14.0, description="Sum of PV system losses in percent"
|
||||
default=14.0, description="Sum of PV system losses in percent", examples=[None]
|
||||
)
|
||||
pvforecast4_trackingtype: Optional[int] = Field(
|
||||
default=None,
|
||||
description="Type of suntracking. 0=fixed, 1=single horizontal axis aligned north-south, 2=two-axis tracking, 3=vertical axis tracking, 4=single horizontal axis aligned east-west, 5=single inclined axis aligned north-south.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast4_optimal_surface_tilt: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="Calculate the optimum tilt angle. Ignored for two-axis tracking.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast4_optimalangles: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast4_albedo: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Proportion of the light hitting the ground that it reflects back.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast4_module_model: Optional[str] = Field(
|
||||
default=None, description="Model of the PV modules of this plane."
|
||||
default=None, description="Model of the PV modules of this plane.", examples=[None]
|
||||
)
|
||||
pvforecast4_inverter_model: Optional[str] = Field(
|
||||
default=None, description="Model of the inverter of this plane."
|
||||
default=None, description="Model of the inverter of this plane.", examples=[None]
|
||||
)
|
||||
pvforecast4_inverter_paco: Optional[int] = Field(
|
||||
default=None, description="AC power rating of the inverter. [W]"
|
||||
default=None, description="AC power rating of the inverter. [W]", examples=[None]
|
||||
)
|
||||
pvforecast4_modules_per_string: Optional[int] = Field(
|
||||
default=None, description="Number of the PV modules of the strings of this plane."
|
||||
default=None,
|
||||
description="Number of the PV modules of the strings of this plane.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast4_strings_per_inverter: Optional[int] = Field(
|
||||
default=None, description="Number of the strings of the inverter of this plane."
|
||||
default=None,
|
||||
description="Number of the strings of the inverter of this plane.",
|
||||
examples=[None],
|
||||
)
|
||||
# Plane 5
|
||||
pvforecast5_surface_tilt: Optional[float] = Field(
|
||||
default=None, description="Tilt angle from horizontal plane. Ignored for two-axis tracking."
|
||||
default=None,
|
||||
description="Tilt angle from horizontal plane. Ignored for two-axis tracking.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast5_surface_azimuth: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast5_userhorizon: Optional[List[float]] = Field(
|
||||
default=None,
|
||||
description="Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast5_peakpower: Optional[float] = Field(
|
||||
default=None, description="Nominal power of PV system in kW."
|
||||
default=None, description="Nominal power of PV system in kW.", examples=[None]
|
||||
)
|
||||
pvforecast5_pvtechchoice: Optional[str] = Field(
|
||||
default="crystSi", description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'."
|
||||
default="crystSi",
|
||||
description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast5_mountingplace: Optional[str] = Field(
|
||||
default="free",
|
||||
description="Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast5_loss: Optional[float] = Field(
|
||||
default=14.0, description="Sum of PV system losses in percent"
|
||||
default=14.0, description="Sum of PV system losses in percent", examples=[None]
|
||||
)
|
||||
pvforecast5_trackingtype: Optional[int] = Field(
|
||||
default=None,
|
||||
description="Type of suntracking. 0=fixed, 1=single horizontal axis aligned north-south, 2=two-axis tracking, 3=vertical axis tracking, 4=single horizontal axis aligned east-west, 5=single inclined axis aligned north-south.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast5_optimal_surface_tilt: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="Calculate the optimum tilt angle. Ignored for two-axis tracking.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast5_optimalangles: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast5_albedo: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Proportion of the light hitting the ground that it reflects back.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast5_module_model: Optional[str] = Field(
|
||||
default=None, description="Model of the PV modules of this plane."
|
||||
default=None, description="Model of the PV modules of this plane.", examples=[None]
|
||||
)
|
||||
pvforecast5_inverter_model: Optional[str] = Field(
|
||||
default=None, description="Model of the inverter of this plane."
|
||||
default=None, description="Model of the inverter of this plane.", examples=[None]
|
||||
)
|
||||
pvforecast5_inverter_paco: Optional[int] = Field(
|
||||
default=None, description="AC power rating of the inverter. [W]"
|
||||
default=None, description="AC power rating of the inverter. [W]", examples=[None]
|
||||
)
|
||||
pvforecast5_modules_per_string: Optional[int] = Field(
|
||||
default=None, description="Number of the PV modules of the strings of this plane."
|
||||
default=None,
|
||||
description="Number of the PV modules of the strings of this plane.",
|
||||
examples=[None],
|
||||
)
|
||||
pvforecast5_strings_per_inverter: Optional[int] = Field(
|
||||
default=None, description="Number of the strings of the inverter of this plane."
|
||||
default=None,
|
||||
description="Number of the strings of the inverter of this plane.",
|
||||
examples=[None],
|
||||
)
|
||||
|
||||
pvforecast_max_planes: ClassVar[int] = 6 # Maximum number of planes that can be set
|
||||
|
||||
provider_settings: Optional[PVForecastImportCommonSettings] = None
|
||||
provider_settings: Optional[PVForecastImportCommonSettings] = Field(
|
||||
default=None, description="Provider settings", examples=[None]
|
||||
)
|
||||
|
||||
# Computed fields
|
||||
@computed_field # type: ignore[prop-decorator]
|
||||
|
@ -23,12 +23,15 @@ class PVForecastImportCommonSettings(SettingsBaseModel):
|
||||
"""Common settings for pvforecast data import from file or JSON string."""
|
||||
|
||||
pvforecastimport_file_path: Optional[Union[str, Path]] = Field(
|
||||
default=None, description="Path to the file to import PV forecast data from."
|
||||
default=None,
|
||||
description="Path to the file to import PV forecast data from.",
|
||||
examples=[None, "/path/to/pvforecast.json"],
|
||||
)
|
||||
|
||||
pvforecastimport_json: Optional[str] = Field(
|
||||
default=None,
|
||||
description="JSON string, dictionary of PV forecast value lists.",
|
||||
examples=['{"pvforecast_ac_power": [0, 8.05, 352.91]}'],
|
||||
)
|
||||
|
||||
# Validators
|
||||
|
@ -9,8 +9,14 @@ from akkudoktoreos.prediction.weatherimport import WeatherImportCommonSettings
|
||||
|
||||
|
||||
class WeatherCommonSettings(SettingsBaseModel):
|
||||
"""Weather Forecast Configuration."""
|
||||
|
||||
weather_provider: Optional[str] = Field(
|
||||
default=None, description="Weather provider id of provider to be used."
|
||||
default=None,
|
||||
description="Weather provider id of provider to be used.",
|
||||
examples=["WeatherImport"],
|
||||
)
|
||||
|
||||
provider_settings: Optional[WeatherImportCommonSettings] = None
|
||||
provider_settings: Optional[WeatherImportCommonSettings] = Field(
|
||||
default=None, description="Provider settings", examples=[None]
|
||||
)
|
||||
|
@ -23,11 +23,15 @@ class WeatherImportCommonSettings(SettingsBaseModel):
|
||||
"""Common settings for weather data import from file or JSON string."""
|
||||
|
||||
weatherimport_file_path: Optional[Union[str, Path]] = Field(
|
||||
default=None, description="Path to the file to import weather data from."
|
||||
default=None,
|
||||
description="Path to the file to import weather data from.",
|
||||
examples=[None, "/path/to/weather_data.json"],
|
||||
)
|
||||
|
||||
weatherimport_json: Optional[str] = Field(
|
||||
default=None, description="JSON string, dictionary of weather forecast value lists."
|
||||
default=None,
|
||||
description="JSON string, dictionary of weather forecast value lists.",
|
||||
examples=['{"weather_temp_air": [18.3, 17.8, 16.9]}'],
|
||||
)
|
||||
|
||||
# Validators
|
||||
|
@ -11,7 +11,7 @@ logger = get_logger(__name__)
|
||||
|
||||
|
||||
class ServerCommonSettings(SettingsBaseModel):
|
||||
"""Common server settings.
|
||||
"""Server Configuration.
|
||||
|
||||
Attributes:
|
||||
To be added
|
||||
|
@ -18,6 +18,8 @@ class classproperty(property):
|
||||
|
||||
|
||||
class UtilsCommonSettings(SettingsBaseModel):
|
||||
"""Utils Configuration."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
@ -419,7 +419,9 @@ def prepare_visualize(
|
||||
start_hour: Optional[int] = 0,
|
||||
) -> None:
|
||||
report = VisualizationReport(filename)
|
||||
next_full_hour_date = pendulum.now(report.config.prediction.timezone).start_of("hour").add(hours=1)
|
||||
next_full_hour_date = (
|
||||
pendulum.now(report.config.prediction.timezone).start_of("hour").add(hours=1)
|
||||
)
|
||||
# Group 1:
|
||||
report.create_line_chart_date(
|
||||
next_full_hour_date, # start_date
|
||||
|
@ -535,7 +535,7 @@ class TestDataSequence:
|
||||
json_str = sequence.to_json()
|
||||
assert isinstance(json_str, str)
|
||||
assert "2023-11-06" in json_str
|
||||
assert ":0.8" in json_str
|
||||
assert ": 0.8" in json_str
|
||||
|
||||
def test_from_json(self, sequence, sequence2):
|
||||
json_str = sequence2.to_json()
|
||||
|
Loading…
x
Reference in New Issue
Block a user