diff --git a/docs/api/simulator/occupancy.md b/docs/api/simulator/occupancy.md
index 2ecdf0bd..c0eebc1e 100644
--- a/docs/api/simulator/occupancy.md
+++ b/docs/api/simulator/occupancy.md
@@ -1,7 +1,32 @@
-# Occupancy
+# Occupancy Models
-::: smart_control.simulator.randomized_arrival_departure_occupancy
+This page documents all occupancy models available in the `smart_control.simulator.occupancy` module.
-::: smart_control.simulator.step_function_occupancy
+## Model Comparison
-::: smart_control.simulator.stochastic_occupancy
+The following chart compares the behavior of all occupancy models over a 24-hour period with 5-minute intervals:
+
+
+
+For an interactive version of this chart, see the [interactive occupancy comparison plot](../../assets/plots/occupancy_comparison.html).
+
+The comparison script can be run with:
+```bash
+python -m smart_control.simulator.occupancy.compare
+```
+
+## Enhanced Occupancy
+
+::: smart_control.simulator.occupancy.enhanced_occupancy
+
+## Stochastic Occupancy (LIGHTSWITCH)
+
+::: smart_control.simulator.occupancy.stochastic_occupancy
+
+## Randomized Arrival/Departure Occupancy
+
+::: smart_control.simulator.occupancy.randomized_arrival_departure_occupancy
+
+## Step Function Occupancy
+
+::: smart_control.simulator.occupancy.step_function_occupancy
diff --git a/docs/assets/images/occupancy_comparison.png b/docs/assets/images/occupancy_comparison.png
new file mode 100644
index 00000000..7a0ea118
Binary files /dev/null and b/docs/assets/images/occupancy_comparison.png differ
diff --git a/docs/assets/plots/occupancy_comparison.html b/docs/assets/plots/occupancy_comparison.html
new file mode 100644
index 00000000..de19929a
--- /dev/null
+++ b/docs/assets/plots/occupancy_comparison.html
@@ -0,0 +1,3888 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/smart_control/simulator/BUILD b/smart_control/simulator/BUILD
index afae6a13..540318a6 100644
--- a/smart_control/simulator/BUILD
+++ b/smart_control/simulator/BUILD
@@ -106,19 +106,6 @@ py_strict_test(
],
)
-py_strict_test(
- name = "randomized_arrival_departure_occupancy_test",
- srcs = ["randomized_arrival_departure_occupancy_test.py"],
- deps = [
- ":randomized_arrival_departure_occupancy",
- "//third_party/py/absl/testing:absltest",
- "//third_party/py/absl/testing:parameterized",
- "//third_party/py/google/protobuf:use_fast_cpp_protos", # Automatically added go/proto_python_upb_flip
- "//third_party/py/numpy",
- "//third_party/py/pandas",
- ],
-)
-
py_strict_test(
name = "rejection_simulator_building_test",
srcs = ["rejection_simulator_building_test.py"],
@@ -167,7 +154,7 @@ py_strict_test(
":hvac_floorplan_based",
":setpoint_schedule",
":simulator_flexible_floor_plan",
- ":step_function_occupancy",
+ "//third_party/py/smart_buildings/smart_control/simulator/occupancy:step_function_occupancy",
":weather_controller",
"//third_party/py/absl/testing:absltest",
"//third_party/py/absl/testing:parameterized",
@@ -189,7 +176,7 @@ py_strict_test(
":hvac",
":setpoint_schedule",
":simulator",
- ":step_function_occupancy",
+ "//third_party/py/smart_buildings/smart_control/simulator/occupancy:step_function_occupancy",
":weather_controller",
"//third_party/py/absl/testing:absltest",
"//third_party/py/absl/testing:parameterized",
@@ -212,18 +199,6 @@ py_strict_test(
],
)
-py_strict_test(
- name = "step_function_occupancy_test",
- srcs = ["step_function_occupancy_test.py"],
- deps = [
- ":step_function_occupancy",
- "//third_party/py/absl/testing:absltest",
- "//third_party/py/absl/testing:parameterized",
- "//third_party/py/google/protobuf:use_fast_cpp_protos", # Automatically added go/proto_python_upb_flip
- "//third_party/py/pandas",
- ],
-)
-
py_strict_test(
name = "thermal_diffuser_utils_test",
srcs = ["thermal_diffuser_utils_test.py"],
@@ -336,18 +311,6 @@ pytype_strict_library(
],
)
-pytype_strict_library(
- name = "randomized_arrival_departure_occupancy",
- srcs = ["randomized_arrival_departure_occupancy.py"],
- deps = [
- "//third_party/py/gin",
- "//third_party/py/numpy",
- "//third_party/py/pandas",
- "//third_party/py/smart_buildings/smart_control/models:base_occupancy",
- "//third_party/py/smart_buildings/smart_control/utils:conversion_utils",
- ],
-)
-
pytype_strict_library(
name = "rejection_simulator_building",
srcs = ["rejection_simulator_building.py"],
@@ -452,7 +415,7 @@ pytype_strict_library(
":setpoint_schedule",
":simulator",
":simulator_building",
- ":step_function_occupancy",
+ "//third_party/py/smart_buildings/smart_control/simulator/occupancy:step_function_occupancy",
":weather_controller",
"//third_party/py/absl/testing:parameterized",
"//third_party/py/pandas",
@@ -490,17 +453,6 @@ pytype_strict_library(
],
)
-pytype_strict_library(
- name = "step_function_occupancy",
- srcs = ["step_function_occupancy.py"],
- deps = [
- "//third_party/py/gin",
- "//third_party/py/pandas",
- "//third_party/py/smart_buildings/smart_control/models:base_occupancy",
- "//third_party/py/smart_buildings/smart_control/utils:conversion_utils",
- ],
-)
-
pytype_strict_library(
name = "thermal_diffuser_utils",
srcs = ["thermal_diffuser_utils.py"],
diff --git a/smart_control/simulator/occupancy/BUILD b/smart_control/simulator/occupancy/BUILD
new file mode 100644
index 00000000..4edcc9b5
--- /dev/null
+++ b/smart_control/simulator/occupancy/BUILD
@@ -0,0 +1,124 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# BUILD file for simulator/occupancy package.
+load("//devtools/python/blaze:pytype.bzl", "pytype_strict_library")
+load("//devtools/python/blaze:strict.bzl", "py_strict_test")
+
+package(
+ default_applicable_licenses = ["//third_party/py/smart_buildings:license"],
+ default_visibility = ["//visibility:public"],
+)
+
+# Occupancy Model Libraries
+
+pytype_strict_library(
+ name = "enhanced_occupancy",
+ srcs = ["enhanced_occupancy.py"],
+ deps = [
+ "//third_party/py/gin",
+ "//third_party/py/numpy",
+ "//third_party/py/pandas",
+ "//third_party/py/smart_buildings/smart_control/models:base_occupancy",
+ "//third_party/py/smart_buildings/smart_control/utils:conversion_utils",
+ ],
+)
+
+pytype_strict_library(
+ name = "stochastic_occupancy",
+ srcs = ["stochastic_occupancy.py"],
+ deps = [
+ "//third_party/py/gin",
+ "//third_party/py/numpy",
+ "//third_party/py/pandas",
+ "//third_party/py/smart_buildings/smart_control/models:base_occupancy",
+ "//third_party/py/smart_buildings/smart_control/utils:conversion_utils",
+ ],
+)
+
+pytype_strict_library(
+ name = "randomized_arrival_departure_occupancy",
+ srcs = ["randomized_arrival_departure_occupancy.py"],
+ deps = [
+ "//third_party/py/gin",
+ "//third_party/py/numpy",
+ "//third_party/py/pandas",
+ "//third_party/py/smart_buildings/smart_control/models:base_occupancy",
+ "//third_party/py/smart_buildings/smart_control/utils:conversion_utils",
+ ],
+)
+
+pytype_strict_library(
+ name = "step_function_occupancy",
+ srcs = ["step_function_occupancy.py"],
+ deps = [
+ "//third_party/py/gin",
+ "//third_party/py/pandas",
+ "//third_party/py/smart_buildings/smart_control/models:base_occupancy",
+ "//third_party/py/smart_buildings/smart_control/utils:conversion_utils",
+ ],
+)
+
+# Occupancy Model Tests
+
+py_strict_test(
+ name = "enhanced_occupancy_test",
+ srcs = ["enhanced_occupancy_test.py"],
+ deps = [
+ ":enhanced_occupancy",
+ "//third_party/py/absl/testing:absltest",
+ "//third_party/py/absl/testing:parameterized",
+ "//third_party/py/google/protobuf:use_fast_cpp_protos",
+ "//third_party/py/numpy",
+ "//third_party/py/pandas",
+ ],
+)
+
+py_strict_test(
+ name = "stochastic_occupancy_test",
+ srcs = ["stochastic_occupancy_test.py"],
+ deps = [
+ ":stochastic_occupancy",
+ "//third_party/py/absl/testing:absltest",
+ "//third_party/py/absl/testing:parameterized",
+ "//third_party/py/google/protobuf:use_fast_cpp_protos",
+ "//third_party/py/numpy",
+ "//third_party/py/pandas",
+ ],
+)
+
+py_strict_test(
+ name = "randomized_arrival_departure_occupancy_test",
+ srcs = ["randomized_arrival_departure_occupancy_test.py"],
+ deps = [
+ ":randomized_arrival_departure_occupancy",
+ "//third_party/py/absl/testing:absltest",
+ "//third_party/py/absl/testing:parameterized",
+ "//third_party/py/google/protobuf:use_fast_cpp_protos",
+ "//third_party/py/numpy",
+ "//third_party/py/pandas",
+ ],
+)
+
+py_strict_test(
+ name = "step_function_occupancy_test",
+ srcs = ["step_function_occupancy_test.py"],
+ deps = [
+ ":step_function_occupancy",
+ "//third_party/py/absl/testing:absltest",
+ "//third_party/py/absl/testing:parameterized",
+ "//third_party/py/google/protobuf:use_fast_cpp_protos",
+ "//third_party/py/pandas",
+ ],
+)
diff --git a/smart_control/simulator/occupancy/__init__.py b/smart_control/simulator/occupancy/__init__.py
new file mode 100644
index 00000000..6d037b18
--- /dev/null
+++ b/smart_control/simulator/occupancy/__init__.py
@@ -0,0 +1,37 @@
+"""Occupancy models for building simulation.
+
+This module contains various occupancy models that simulate the presence and
+behavior of people in building zones. These models are used to calculate
+average occupancy for different time intervals, which is an important input
+for building simulation and control.
+
+Available Models:
+ - EnhancedOccupancy: Enhanced stochastic model with minute-level control
+ and different worker types (weekday-only, weekend-regular, etc.)
+ - LIGHTSWITCHOccupancy: Stochastic model based on the LIGHTSWITCH algorithm
+ with arrival, departure, and lunch break patterns
+ - RandomizedArrivalDepartureOccupancy: Model with randomized arrival and
+ departure times within specified windows
+ - StepFunctionOccupancy: Simple model with constant occupancy levels for
+ work and non-work periods
+"""
+
+from smart_control.simulator.occupancy.enhanced_occupancy import EnhancedOccupancy
+from smart_control.simulator.occupancy.enhanced_occupancy import MinuteLevelZoneOccupant
+from smart_control.simulator.occupancy.enhanced_occupancy import WorkerType
+from smart_control.simulator.occupancy.randomized_arrival_departure_occupancy import RandomizedArrivalDepartureOccupancy
+from smart_control.simulator.occupancy.step_function_occupancy import StepFunctionOccupancy
+from smart_control.simulator.occupancy.stochastic_occupancy import LIGHTSWITCHOccupancy
+from smart_control.simulator.occupancy.stochastic_occupancy import OccupancyStateEnum
+from smart_control.simulator.occupancy.stochastic_occupancy import ZoneOccupant
+
+__all__ = [
+ "EnhancedOccupancy",
+ "MinuteLevelZoneOccupant",
+ "WorkerType",
+ "LIGHTSWITCHOccupancy",
+ "ZoneOccupant",
+ "RandomizedArrivalDepartureOccupancy",
+ "StepFunctionOccupancy",
+ "OccupancyStateEnum",
+]
diff --git a/smart_control/simulator/occupancy/compare.py b/smart_control/simulator/occupancy/compare.py
new file mode 100644
index 00000000..5237bb64
--- /dev/null
+++ b/smart_control/simulator/occupancy/compare.py
@@ -0,0 +1,247 @@
+"""Comparison script for occupancy models.
+
+This script generates comparison charts showing the behavior of all occupancy
+models over a 24-hour period. The charts are saved as both PNG (for documentation)
+and HTML (for interactive viewing).
+"""
+
+import os
+from typing import Dict, List
+
+import matplotlib.pyplot as plt
+import pandas as pd
+import plotly.graph_objects as go
+
+from smart_control.simulator.occupancy import EnhancedOccupancy
+from smart_control.simulator.occupancy import LIGHTSWITCHOccupancy
+from smart_control.simulator.occupancy import RandomizedArrivalDepartureOccupancy
+from smart_control.simulator.occupancy import StepFunctionOccupancy
+
+
+def create_occupancy_models() -> Dict[str, object]:
+ """Creates instances of all occupancy models with reasonable defaults.
+
+ Returns:
+ Dictionary mapping model names to model instances.
+ """
+ models = {}
+
+ # Step Function Occupancy - simple constant levels
+ models['Step Function'] = StepFunctionOccupancy(
+ work_start_time=pd.Timedelta(9, unit='h'),
+ work_end_time=pd.Timedelta(17, unit='h'),
+ work_occupancy=10.0,
+ nonwork_occupancy=0.5,
+ )
+
+ # Randomized Arrival/Departure Occupancy
+ models['Randomized Arrival/Departure'] = (
+ RandomizedArrivalDepartureOccupancy(
+ zone_assignment=10,
+ earliest_expected_arrival_hour=8,
+ latest_expected_arrival_hour=10,
+ earliest_expected_departure_hour=16,
+ latest_expected_departure_hour=18,
+ time_step_sec=300, # 5 minutes
+ seed=42,
+ time_zone='US/Pacific',
+ )
+ )
+
+ # LIGHTSWITCH Occupancy (Stochastic)
+ models['LIGHTSWITCH (Stochastic)'] = LIGHTSWITCHOccupancy(
+ zone_assignment=10,
+ earliest_expected_arrival_hour=8,
+ latest_expected_arrival_hour=10,
+ earliest_expected_departure_hour=16,
+ latest_expected_departure_hour=18,
+ lunch_start_hour=12,
+ lunch_end_hour=13,
+ time_step_sec=300, # 5 minutes
+ seed=42,
+ time_zone='US/Pacific',
+ )
+
+ # Enhanced Occupancy - with minute-level control
+ models['Enhanced (Minute-Level)'] = EnhancedOccupancy(
+ zone_assignment=10,
+ earliest_expected_arrival_hour=8,
+ latest_expected_arrival_hour=10,
+ earliest_expected_departure_hour=16,
+ latest_expected_departure_hour=18,
+ lunch_start_hour=12,
+ lunch_end_hour=13,
+ time_step=pd.Timedelta(5, unit='min'),
+ time_zone='US/Pacific',
+ )
+
+ return models
+
+
+def generate_occupancy_data(
+ models: Dict[str, object], zone_id: str = 'zone_0'
+) -> pd.DataFrame:
+ """Generates occupancy data for all models over a 24-hour period.
+
+ Args:
+ models: Dictionary of occupancy model instances.
+ zone_id: Zone identifier to query.
+
+ Returns:
+ DataFrame with timestamp index and columns for each model.
+ """
+ # Create timestamps for a single day in 5-minute intervals
+ # Use a weekday (Monday) to ensure work patterns are active
+ start_time = pd.Timestamp('2024-01-08 00:00:00', tz='US/Pacific')
+ end_time = start_time + pd.Timedelta(24, unit='h')
+ time_step = pd.Timedelta(5, unit='min')
+
+ timestamps = pd.date_range(start=start_time, end=end_time, freq=time_step)
+
+ # Calculate occupancy for each model at each timestamp
+ data = {'Timestamp': timestamps[:-1]} # Exclude the last timestamp
+
+ for model_name, model in models.items():
+ occupancies = []
+ for i in range(len(timestamps) - 1):
+ interval_start = timestamps[i]
+ interval_end = timestamps[i + 1]
+ try:
+ occupancy = model.average_zone_occupancy(
+ zone_id, interval_start, interval_end
+ )
+ occupancies.append(occupancy)
+ except Exception as e:
+ print(
+ f'Error calculating occupancy for {model_name} at'
+ f' {interval_start}: {e}'
+ )
+ occupancies.append(0.0)
+
+ data[model_name] = occupancies
+
+ df = pd.DataFrame(data)
+ df.set_index('Timestamp', inplace=True)
+ return df
+
+
+def create_matplotlib_plot(df: pd.DataFrame, output_path: str):
+ """Creates a matplotlib plot and saves as PNG.
+
+ Args:
+ df: DataFrame with occupancy data.
+ output_path: Path to save the PNG file.
+ """
+ plt.figure(figsize=(14, 7))
+
+ for column in df.columns:
+ # Convert timestamps to local time strings for x-axis
+ times = df.index.strftime('%H:%M')
+ plt.plot(range(len(df)), df[column], label=column, linewidth=2)
+
+ plt.xlabel('Time of Day (Pacific Time)', fontsize=12)
+ plt.ylabel('Average Occupancy (people)', fontsize=12)
+ plt.title(
+ 'Occupancy Model Comparison - 24 Hour Period (5-Minute Intervals)',
+ fontsize=14,
+ fontweight='bold',
+ )
+ plt.legend(loc='best', fontsize=10)
+ plt.grid(True, alpha=0.3)
+
+ # Set x-axis ticks to show every 2 hours
+ tick_indices = range(0, len(df), 24) # Every 2 hours (24 * 5 min = 2 hours)
+ tick_labels = [df.index[i].strftime('%H:%M') for i in tick_indices]
+ plt.xticks(tick_indices, tick_labels, rotation=45)
+
+ plt.tight_layout()
+ plt.savefig(output_path, dpi=300, bbox_inches='tight')
+ plt.close()
+ print(f'Saved PNG plot to: {output_path}')
+
+
+def create_plotly_plot(df: pd.DataFrame, output_path: str):
+ """Creates an interactive plotly plot and saves as HTML.
+
+ Args:
+ df: DataFrame with occupancy data.
+ output_path: Path to save the HTML file.
+ """
+ fig = go.Figure()
+
+ for column in df.columns:
+ fig.add_trace(
+ go.Scatter(
+ x=df.index,
+ y=df[column],
+ mode='lines',
+ name=column,
+ line=dict(width=2),
+ )
+ )
+
+ fig.update_layout(
+ title={
+ 'text': (
+ 'Occupancy Model Comparison - 24 Hour Period (5-Minute'
+ ' Intervals)'
+ ),
+ 'x': 0.5,
+ 'xanchor': 'center',
+ 'font': {'size': 16, 'weight': 'bold'},
+ },
+ xaxis_title='Time of Day (Pacific Time)',
+ yaxis_title='Average Occupancy (people)',
+ hovermode='x unified',
+ template='plotly_white',
+ width=1200,
+ height=600,
+ legend=dict(yanchor='top', y=0.99, xanchor='left', x=0.01),
+ )
+
+ fig.update_xaxes(
+ tickformat='%H:%M',
+ dtick=7200000, # 2 hours in milliseconds
+ )
+
+ fig.write_html(output_path)
+ print(f'Saved HTML plot to: {output_path}')
+
+
+def main():
+ """Main function to generate comparison charts."""
+ # Get the project root directory
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ project_root = os.path.join(script_dir, '..', '..', '..')
+
+ # Define output paths
+ png_output = os.path.join(
+ project_root, 'docs', 'assets', 'images', 'occupancy_comparison.png'
+ )
+ html_output = os.path.join(
+ project_root, 'docs', 'assets', 'plots', 'occupancy_comparison.html'
+ )
+
+ # Ensure output directories exist
+ os.makedirs(os.path.dirname(png_output), exist_ok=True)
+ os.makedirs(os.path.dirname(html_output), exist_ok=True)
+
+ print('Creating occupancy models...')
+ models = create_occupancy_models()
+
+ print('Generating occupancy data...')
+ df = generate_occupancy_data(models)
+
+ print('Creating matplotlib plot...')
+ create_matplotlib_plot(df, png_output)
+
+ print('Creating plotly plot...')
+ create_plotly_plot(df, html_output)
+
+ print('\\nComparison charts generated successfully!')
+ print(f'PNG: {png_output}')
+ print(f'HTML: {html_output}')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/smart_control/simulator/occupancy/conftest.py b/smart_control/simulator/occupancy/conftest.py
new file mode 100644
index 00000000..373ad4c4
--- /dev/null
+++ b/smart_control/simulator/occupancy/conftest.py
@@ -0,0 +1 @@
+"""Configuration for occupancy tests."""
diff --git a/smart_control/simulator/enhanced_occupancy.py b/smart_control/simulator/occupancy/enhanced_occupancy.py
similarity index 100%
rename from smart_control/simulator/enhanced_occupancy.py
rename to smart_control/simulator/occupancy/enhanced_occupancy.py
diff --git a/smart_control/simulator/enhanced_occupancy_test.py b/smart_control/simulator/occupancy/enhanced_occupancy_test.py
similarity index 97%
rename from smart_control/simulator/enhanced_occupancy_test.py
rename to smart_control/simulator/occupancy/enhanced_occupancy_test.py
index 517cd6b7..53379d99 100644
--- a/smart_control/simulator/enhanced_occupancy_test.py
+++ b/smart_control/simulator/occupancy/enhanced_occupancy_test.py
@@ -5,10 +5,10 @@
import numpy as np
import pandas as pd
-from smart_control.simulator.enhanced_occupancy import EnhancedOccupancy
-from smart_control.simulator.enhanced_occupancy import MinuteLevelZoneOccupant
-from smart_control.simulator.enhanced_occupancy import OccupancyStateEnum
-from smart_control.simulator.enhanced_occupancy import WorkerType
+from smart_control.simulator.occupancy.enhanced_occupancy import EnhancedOccupancy
+from smart_control.simulator.occupancy.enhanced_occupancy import MinuteLevelZoneOccupant
+from smart_control.simulator.occupancy.enhanced_occupancy import OccupancyStateEnum
+from smart_control.simulator.occupancy.enhanced_occupancy import WorkerType
DEBUG_PRINT = False
SEED = 511211
diff --git a/smart_control/simulator/randomized_arrival_departure_occupancy.py b/smart_control/simulator/occupancy/randomized_arrival_departure_occupancy.py
similarity index 100%
rename from smart_control/simulator/randomized_arrival_departure_occupancy.py
rename to smart_control/simulator/occupancy/randomized_arrival_departure_occupancy.py
diff --git a/smart_control/simulator/randomized_arrival_departure_occupancy_test.py b/smart_control/simulator/occupancy/randomized_arrival_departure_occupancy_test.py
similarity index 96%
rename from smart_control/simulator/randomized_arrival_departure_occupancy_test.py
rename to smart_control/simulator/occupancy/randomized_arrival_departure_occupancy_test.py
index ea691791..d541eefc 100644
--- a/smart_control/simulator/randomized_arrival_departure_occupancy_test.py
+++ b/smart_control/simulator/occupancy/randomized_arrival_departure_occupancy_test.py
@@ -5,9 +5,9 @@
import numpy as np
import pandas as pd
-from smart_control.simulator import randomized_arrival_departure_occupancy
-from smart_control.simulator.randomized_arrival_departure_occupancy import OccupancyStateEnum
-from smart_control.simulator.randomized_arrival_departure_occupancy import RandomizedArrivalDepartureOccupancy
+from smart_control.simulator.occupancy import randomized_arrival_departure_occupancy
+from smart_control.simulator.occupancy.randomized_arrival_departure_occupancy import OccupancyStateEnum
+from smart_control.simulator.occupancy.randomized_arrival_departure_occupancy import RandomizedArrivalDepartureOccupancy
# fmt: off
# pylint: disable=bad-continuation
diff --git a/smart_control/simulator/step_function_occupancy.py b/smart_control/simulator/occupancy/step_function_occupancy.py
similarity index 98%
rename from smart_control/simulator/step_function_occupancy.py
rename to smart_control/simulator/occupancy/step_function_occupancy.py
index 9de06f7c..9398b3b0 100644
--- a/smart_control/simulator/step_function_occupancy.py
+++ b/smart_control/simulator/occupancy/step_function_occupancy.py
@@ -73,9 +73,7 @@ def average_zone_occupancy(
nonwork_seconds = 0.0
# Get the timestamp for midnight of the first day.
- day = pd.Timestamp(
- year=start_time.year, month=start_time.month, day=start_time.day
- )
+ day = start_time.normalize()
current_time = start_time - day
# Accumulate working and non-working hours for all days.
diff --git a/smart_control/simulator/step_function_occupancy_test.py b/smart_control/simulator/occupancy/step_function_occupancy_test.py
similarity index 98%
rename from smart_control/simulator/step_function_occupancy_test.py
rename to smart_control/simulator/occupancy/step_function_occupancy_test.py
index 7ee743a7..c55410bc 100644
--- a/smart_control/simulator/step_function_occupancy_test.py
+++ b/smart_control/simulator/occupancy/step_function_occupancy_test.py
@@ -4,7 +4,7 @@
from absl.testing import parameterized
import pandas as pd
-from smart_control.simulator import step_function_occupancy
+from smart_control.simulator.occupancy import step_function_occupancy
class StepFunctionOccupancyModelTest(parameterized.TestCase):
diff --git a/smart_control/simulator/stochastic_occupancy.py b/smart_control/simulator/occupancy/stochastic_occupancy.py
similarity index 100%
rename from smart_control/simulator/stochastic_occupancy.py
rename to smart_control/simulator/occupancy/stochastic_occupancy.py
diff --git a/smart_control/simulator/stochastic_occupancy_test.py b/smart_control/simulator/occupancy/stochastic_occupancy_test.py
similarity index 96%
rename from smart_control/simulator/stochastic_occupancy_test.py
rename to smart_control/simulator/occupancy/stochastic_occupancy_test.py
index 4fd5863c..89dd6c66 100644
--- a/smart_control/simulator/stochastic_occupancy_test.py
+++ b/smart_control/simulator/occupancy/stochastic_occupancy_test.py
@@ -5,9 +5,9 @@
import numpy as np
import pandas as pd
-from smart_control.simulator.stochastic_occupancy import LIGHTSWITCHOccupancy
-from smart_control.simulator.stochastic_occupancy import OccupancyStateEnum
-from smart_control.simulator.stochastic_occupancy import ZoneOccupant
+from smart_control.simulator.occupancy.stochastic_occupancy import LIGHTSWITCHOccupancy
+from smart_control.simulator.occupancy.stochastic_occupancy import OccupancyStateEnum
+from smart_control.simulator.occupancy.stochastic_occupancy import ZoneOccupant
# fmt: off
# pylint: disable=line-too-long
diff --git a/smart_control/simulator/simulator_building_test_lib.py b/smart_control/simulator/simulator_building_test_lib.py
index 8e7279db..427779b1 100644
--- a/smart_control/simulator/simulator_building_test_lib.py
+++ b/smart_control/simulator/simulator_building_test_lib.py
@@ -11,8 +11,8 @@
from smart_control.simulator import setpoint_schedule
from smart_control.simulator import simulator as simulator_py
from smart_control.simulator import simulator_building as sb_py
-from smart_control.simulator import step_function_occupancy
from smart_control.simulator import weather_controller as weather_controller_py
+from smart_control.simulator.occupancy import step_function_occupancy
_ACTION_RESPONSE_TYPE = (
smart_control_building_pb2.SingleActionResponse.ActionResponseType
diff --git a/smart_control/simulator/simulator_flexible_floor_plan_test.py b/smart_control/simulator/simulator_flexible_floor_plan_test.py
index 32970ab2..31de8684 100644
--- a/smart_control/simulator/simulator_flexible_floor_plan_test.py
+++ b/smart_control/simulator/simulator_flexible_floor_plan_test.py
@@ -16,8 +16,8 @@
from smart_control.simulator import hvac_floorplan_based as floorplan_hvac_py
from smart_control.simulator import setpoint_schedule
from smart_control.simulator import simulator_flexible_floor_plan as simulator_py
-from smart_control.simulator import step_function_occupancy
from smart_control.simulator import weather_controller as weather_controller_py
+from smart_control.simulator.occupancy import step_function_occupancy
from smart_control.utils import conversion_utils
diff --git a/smart_control/simulator/simulator_test.py b/smart_control/simulator/simulator_test.py
index cd9227c0..e1e2f00f 100644
--- a/smart_control/simulator/simulator_test.py
+++ b/smart_control/simulator/simulator_test.py
@@ -13,8 +13,8 @@
from smart_control.simulator import hvac as hvac_py
from smart_control.simulator import setpoint_schedule
from smart_control.simulator import simulator as simulator_py
-from smart_control.simulator import step_function_occupancy
from smart_control.simulator import weather_controller as weather_controller_py
+from smart_control.simulator.occupancy import step_function_occupancy
from smart_control.utils import conversion_utils