Source code for citylearn.internal.baseline

from __future__ import annotations

from copy import deepcopy
from dataclasses import dataclass
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple

import pandas as pd

if TYPE_CHECKING:
    from citylearn.citylearn import CityLearnEnv


[docs] @dataclass class BusinessAsUsualBaselineResult: """Cached result for an operational business-as-usual sidecar run.""" env: "CityLearnEnv" kpis_v2: pd.DataFrame episode: int time_step: int
[docs] class CityLearnBusinessAsUsualBaselineService: """Runs and caches the native business-as-usual baseline for an env.""" def __init__(self, env: "CityLearnEnv"): self.env = env
[docs] def run(self, force: bool = False) -> BusinessAsUsualBaselineResult: from citylearn.agents.baseline import BusinessAsUsualAgent env = self.env target_time_step = int(getattr(env, 'time_step', 0)) episode = int(getattr(env.episode_tracker, 'episode', 0)) cache_key = (episode, target_time_step) cache: Dict[Tuple[int, int], BusinessAsUsualBaselineResult] = getattr( env, '_business_as_usual_baseline_cache', {}, ) if not force and cache_key in cache: return cache[cache_key] baseline_env = self._new_sidecar_env() agent = BusinessAsUsualAgent(baseline_env) baseline_env.reset() while int(baseline_env.time_step) < target_time_step and not (baseline_env.terminated or baseline_env.truncated): actions = agent.predict([], deterministic=True) baseline_env._runtime_service.step_without_feedback(actions) kpis_v2 = baseline_env.evaluate_v2(include_business_as_usual=False) result = BusinessAsUsualBaselineResult( env=baseline_env, kpis_v2=kpis_v2, episode=episode, time_step=target_time_step, ) cache[cache_key] = result env._business_as_usual_baseline_cache = cache return result
[docs] def clear(self): self.env._business_as_usual_baseline_cache = {}
def _new_sidecar_env(self) -> "CityLearnEnv": env = self.env schema = deepcopy(env.schema) if isinstance(schema, dict): schema['render'] = False schema['render_mode'] = 'none' schema['export_kpis_on_episode_end'] = False schema['debug_timing'] = False schema['metrics_log_interval'] = 0 source_buildings = list(getattr(env, '_all_buildings', None) or env.buildings) source_evs = list(getattr(env, '_all_electric_vehicles', None) or env.electric_vehicles) building_names: Optional[List[str]] = [building.name for building in source_buildings] if source_buildings else None ev_names: Optional[List[str]] = [ev.name for ev in source_evs] if source_evs else None start = int(getattr(env.episode_tracker, 'episode_start_time_step', 0) or 0) end = int(getattr(env.episode_tracker, 'episode_end_time_step', max(start, getattr(env, 'time_steps', 1) - 1)) or start) episode_steps = int(getattr(env.episode_tracker, 'episode_time_steps', max(end - start + 1, 1)) or max(end - start + 1, 1)) requires_entity_interface = ( getattr(env, 'topology_mode', None) == 'dynamic' or bool(getattr(getattr(env, '_demand_response_service', None), 'enabled', False)) ) baseline_env = env.__class__( schema, root_directory=getattr(env, 'root_directory', None), buildings=building_names, electric_vehicles=ev_names, simulation_start_time_step=start, simulation_end_time_step=end, episode_time_steps=episode_steps, rolling_episode_split=False, random_episode_split=False, seconds_per_time_step=float(getattr(env, 'seconds_per_time_step', 3600.0)), central_agent=True, random_seed=getattr(env, 'random_seed', None), offline=getattr(env, 'offline', False), time_step_ratio=getattr(env, 'time_step_ratio', None), interface='entity' if requires_entity_interface else 'flat', topology_mode=getattr(env, 'topology_mode', None), render_mode='none', export_kpis_on_episode_end=False, check_observation_limits=False, physics_invariant_checks=bool(getattr(env, 'physics_invariant_checks', False)), ) baseline_env._business_as_usual_sidecar = True return baseline_env