Skip to content

Commit fa72b3b

Browse files
committed
Improved input structure and pytest
1 parent ee5d202 commit fa72b3b

8 files changed

Lines changed: 48 additions & 21 deletions

File tree

src/app/api/simulation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
from fastapi import APIRouter
55

66
from app.core.simulation.simulation_run import run_simulation
7-
from app.schemas.requests_generator_input import RqsGeneratorInput
7+
from app.schemas.full_simulation_input import SimulationPayload
88
from app.schemas.simulation_output import SimulationOutput
99

1010
router = APIRouter()
1111

1212
@router.post("/simulation")
13-
async def event_loop_simulation(input_data: RqsGeneratorInput) -> SimulationOutput:
13+
async def event_loop_simulation(input_data: SimulationPayload) -> SimulationOutput:
1414
"""Run the simulation and return aggregate KPIs."""
1515
rng = np.random.default_rng()
1616
return run_simulation(input_data, rng=rng)

src/app/core/event_samplers/gaussian_poisson.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
uniform_variable_generator,
1818
)
1919
from app.schemas.requests_generator_input import RqsGeneratorInput
20+
from app.schemas.simulation_settings_input import SimulationSettings
2021

2122

2223
def gaussian_poisson_sampling(
2324
input_data: RqsGeneratorInput,
25+
settings: SimulationSettings,
2426
*,
2527
rng: np.random.Generator | None = None,
2628
) -> Generator[float, None, None]:
@@ -35,11 +37,11 @@ def gaussian_poisson_sampling(
3537
Λ = U * (mean_req_per_minute_per_user / 60) [req/s].
3638
3. While inside the current window, draw gaps
3739
Δt ~ Exponential(Λ) using inverse-CDF.
38-
4. Stop once the virtual clock exceeds *simulation_time*.
40+
4. Stop once the virtual clock exceeds *total_simulation_time*.
3941
"""
4042
rng = rng or np.random.default_rng()
4143

42-
simulation_time = input_data.total_simulation_time
44+
simulation_time = settings.total_simulation_time
4345
user_sampling_window = input_data.user_sampling_window
4446

4547
# λ_u : mean concurrent users per window

src/app/core/event_samplers/poisson_poisson.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
uniform_variable_generator,
1515
)
1616
from app.schemas.requests_generator_input import RqsGeneratorInput
17+
from app.schemas.simulation_settings_input import SimulationSettings
1718

1819

1920
def poisson_poisson_sampling(
2021
input_data: RqsGeneratorInput,
22+
settings: SimulationSettings,
2123
*,
2224
rng: np.random.Generator | None = None,
2325
) -> Generator[float, None, None]:
@@ -32,11 +34,11 @@ def poisson_poisson_sampling(
3234
Λ = U * (mean_req_per_minute_per_user / 60) [req/s].
3335
3. While inside the current window, draw gaps
3436
Δt ~ Exponential(Λ) using inverse-CDF.
35-
4. Stop once the virtual clock exceeds *simulation_time*.
37+
4. Stop once the virtual clock exceeds *total_simulation_time*.
3638
"""
3739
rng = rng or np.random.default_rng()
3840

39-
simulation_time = input_data.total_simulation_time
41+
simulation_time = settings.total_simulation_time
4042
user_sampling_window = input_data.user_sampling_window
4143

4244
# λ_u : mean concurrent users per window

src/app/core/simulation/requests_generator.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
import numpy as np
1818

1919
from app.schemas.requests_generator_input import RqsGeneratorInput
20+
from app.schemas.simulation_settings_input import SimulationSettings
2021

2122

2223
def requests_generator(
2324
input_data: RqsGeneratorInput,
25+
settings: SimulationSettings,
2426
*,
2527
rng: np.random.Generator | None = None,
2628
) -> Generator[float, None, None]:
@@ -41,12 +43,14 @@ def requests_generator(
4143
#Gaussian-Poisson model
4244
return gaussian_poisson_sampling(
4345
input_data=input_data,
46+
settings=settings,
4447
rng=rng,
4548

4649
)
4750

4851
# Poisson + Poisson
4952
return poisson_poisson_sampling(
5053
input_data=input_data,
54+
settings=settings,
5155
rng=rng,
5256
)

src/app/core/simulation/simulation_run.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,35 @@
1414

1515
import numpy as np
1616

17-
from app.schemas.requests_generator_input import RqsGeneratorInput
17+
from app.schemas.full_simulation_input import SimulationPayload
18+
19+
1820

1921

2022

2123

2224
def run_simulation(
23-
input_data: RqsGeneratorInput,
25+
input_data: SimulationPayload,
2426
*,
2527
rng: np.random.Generator,
2628
) -> SimulationOutput:
2729
"""Simulation executor in Simpy"""
28-
gaps: Generator[float, None, None] = requests_generator(input_data, rng=rng)
29-
env = simpy.Environment()
30-
31-
simulation_time = input_data.total_simulation_time
30+
settings = input_data.settings
31+
simulation_time = settings.total_simulation_time
3232
# pydantic in the validation assign a value and mypy is not
3333
# complaining because a None cannot be compared in the loop
3434
# to a float
3535
assert simulation_time is not None
3636

37+
requests_generator_input = input_data.rqs_input
38+
39+
gaps: Generator[float, None, None] = requests_generator(
40+
requests_generator_input,
41+
settings,
42+
rng=rng)
43+
env = simpy.Environment()
44+
45+
3746
total_request_per_time_period = {
3847
"simulation_time": simulation_time,
3948
"total_requests": 0,
@@ -51,6 +60,6 @@ def arrival_process(
5160

5261
return SimulationOutput(
5362
total_requests=total_request_per_time_period,
54-
metric_2=str(input_data.avg_request_per_minute_per_user.mean),
55-
metric_n=str(input_data.avg_active_users.mean),
63+
metric_2=str(requests_generator_input.avg_request_per_minute_per_user.mean),
64+
metric_n=str(requests_generator_input.avg_active_users.mean),
5665
)

src/app/schemas/full_simulation_input.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from pydantic import BaseModel
44

55
from app.schemas.requests_generator_input import RqsGeneratorInput
6+
from app.schemas.simulation_settings_input import SimulationSettings
67
from app.schemas.system_topology_schema.full_system_topology_schema import TopologyGraph
78

89

@@ -11,3 +12,4 @@ class SimulationPayload(BaseModel):
1112

1213
rqs_input: RqsGeneratorInput
1314
topology_graph: TopologyGraph
15+
settings: SimulationSettings

src/app/schemas/requests_generator_input.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,6 @@ class RqsGeneratorInput(BaseModel):
1212

1313
avg_active_users: RVConfig
1414
avg_request_per_minute_per_user: RVConfig
15-
total_simulation_time: int = Field(
16-
default=TimeDefaults.SIMULATION_TIME,
17-
ge=TimeDefaults.MIN_SIMULATION_TIME,
18-
description=(
19-
f"Simulation time in seconds (>= {TimeDefaults.MIN_SIMULATION_TIME})."
20-
),
21-
)
2215

2316
user_sampling_window: int = Field(
2417
default=TimeDefaults.USER_SAMPLING_WINDOW,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""define a class with the global settings for the simulation"""
2+
3+
from pydantic import BaseModel, Field
4+
5+
from app.config.constants import TimeDefaults
6+
7+
8+
class SimulationSettings(BaseModel):
9+
"""Global parameters that apply to the whole run."""
10+
11+
total_simulation_time: int = Field(
12+
default=TimeDefaults.SIMULATION_TIME,
13+
ge=TimeDefaults.MIN_SIMULATION_TIME,
14+
description="Simulation horizon in seconds.",
15+
)

0 commit comments

Comments
 (0)