Skip to content

Commit c81658c

Browse files
committed
simulation start with the vent generator
1 parent 5e38547 commit c81658c

File tree

5 files changed

+75
-33
lines changed

5 files changed

+75
-33
lines changed

src/app/api/simulation.py

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,16 @@
33
import numpy as np
44
from fastapi import APIRouter
55

6-
from app.core.requests_generator import requests_generator
6+
from app.core.simulation.simulator import run_simulation
77
from app.schemas.simulation_input import SimulationInput
88
from app.schemas.simulation_output import SimulationOutput
99

1010
router = APIRouter()
1111

1212
@router.post("/simulation")
13-
async def event_loop_simulation(
14-
generator_input: SimulationInput,
15-
) -> SimulationOutput:
16-
"""Endpoint to handle the simulation."""
17-
# Define the variables for the generator
18-
# Implement a robust type checking for
19-
# edge cases
20-
rng: np.random.Generator = np.random.default_rng()
21-
22-
# requests generator
23-
req_generated = requests_generator( # noqa: F841
24-
generator_input,
25-
rng=rng,
26-
)
27-
28-
# simple map to pass the quality assurance
29-
return SimulationOutput(
30-
metric_1=generator_input.avg_active_users.mean,
31-
metric_2=str(generator_input.avg_request_per_minute_per_user.mean),
32-
metric_n=str(generator_input.total_simulation_time),
33-
)
13+
async def event_loop_simulation(input_data: SimulationInput) -> SimulationOutput:
14+
"""Run the simulation and return aggregate KPIs."""
15+
rng = np.random.default_rng()
16+
return run_simulation(input_data, rng=rng)
3417

3518

src/app/core/event_samplers/gaussian_poisson.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@ def gaussian_poisson_sampling(
3636
Λ = U * (mean_req_per_minute_per_user / 60) [req/s].
3737
3. While inside the current window, draw gaps
3838
Δt ~ Exponential(Λ) using inverse-CDF.
39-
4. Stop once the virtual clock exceeds *simulation_time_second*.
39+
4. Stop once the virtual clock exceeds *simulation_time*.
4040
"""
4141
rng = rng or np.random.default_rng()
4242

43-
simulation_time_second = input_data.total_simulation_time
43+
simulation_time = input_data.total_simulation_time
4444
# pydantic in the validation assign a value and mypy is not
4545
# complaining because a None cannot be compared in the loop
4646
# to a float
47-
assert simulation_time_second is not None
47+
assert simulation_time is not None
4848

4949
# λ_u : mean concurrent users per window
5050
mean_concurrent_user = float(input_data.avg_active_users.mean)
@@ -65,7 +65,7 @@ def gaussian_poisson_sampling(
6565
window_end = 0.0 # end of the current user window
6666
lam = 0.0 # aggregate rate Λ (req/s)
6767

68-
while now < simulation_time_second:
68+
while now < simulation_time:
6969
# (Re)sample U at the start of each window
7070
if now >= window_end:
7171
window_end = now + float(sampling_window_s)
@@ -86,7 +86,7 @@ def gaussian_poisson_sampling(
8686
delta_t = -math.log(1.0 - u_raw) / lam
8787

8888
# End simulation if the next event exceeds the horizon
89-
if now + delta_t > simulation_time_second:
89+
if now + delta_t > simulation_time:
9090
break
9191

9292
# If the gap crosses the window boundary, jump to it

src/app/core/event_samplers/poisson_poisson.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ def poisson_poisson_sampling(
3333
Λ = U * (mean_req_per_minute_per_user / 60) [req/s].
3434
3. While inside the current window, draw gaps
3535
Δt ~ Exponential(Λ) using inverse-CDF.
36-
4. Stop once the virtual clock exceeds *simulation_time_second*.
36+
4. Stop once the virtual clock exceeds *simulation_time*.
3737
"""
3838
rng = rng or np.random.default_rng()
3939

40-
simulation_time_second = input_data.total_simulation_time
40+
simulation_time = input_data.total_simulation_time
4141
# pydantic in the validation assign a value and mypy is not
4242
# complaining because a None cannot be compared in the loop
4343
# to a float
44-
assert simulation_time_second is not None
44+
assert simulation_time is not None
4545

4646
# λ_u : mean concurrent users per window
4747
mean_concurrent_user = float(input_data.avg_active_users.mean)
@@ -57,7 +57,7 @@ def poisson_poisson_sampling(
5757
window_end = 0.0 # end of the current user window
5858
lam = 0.0 # aggregate rate Λ (req/s)
5959

60-
while now < simulation_time_second:
60+
while now < simulation_time:
6161
# (Re)sample U at the start of each window
6262
if now >= window_end:
6363
window_end = now + float(sampling_window_s)
@@ -74,7 +74,7 @@ def poisson_poisson_sampling(
7474
delta_t = -math.log(1.0 - u_raw) / lam
7575

7676
# End simulation if the next event exceeds the horizon
77-
if now + delta_t > simulation_time_second:
77+
if now + delta_t > simulation_time:
7878
break
7979

8080
# If the gap crosses the window boundary, jump to it
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""simulation of the server"""
2+
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING
6+
7+
import simpy
8+
9+
from app.core.requests_generator import requests_generator
10+
from app.schemas.simulation_output import SimulationOutput
11+
12+
if TYPE_CHECKING:
13+
from collections.abc import Generator, Iterator
14+
15+
import numpy as np
16+
17+
from app.schemas.simulation_input import SimulationInput
18+
19+
20+
21+
22+
def run_simulation(
23+
data: SimulationInput,
24+
*,
25+
rng: np.random.Generator,
26+
) -> SimulationOutput:
27+
"""
28+
Esegue la simulazione in SimPy usando i gap prodotti da requests_generator.
29+
Per ora calcola solo il numero totale di richieste arrivate.
30+
"""
31+
gaps: Iterator[float] = requests_generator(data, rng=rng)
32+
env = simpy.Environment()
33+
34+
simulation_time = data.total_simulation_time
35+
# pydantic in the validation assign a value and mypy is not
36+
# complaining because a None cannot be compared in the loop
37+
# to a float
38+
assert simulation_time is not None
39+
40+
total_request_per_time_period = {
41+
"simulation_time": simulation_time,
42+
"total_requests": 0,
43+
}
44+
45+
def arrival_process(
46+
env: simpy.Environment,
47+
) -> Generator[simpy.events.Event, None, None]:
48+
for gap in gaps:
49+
yield env.timeout(gap)
50+
total_request_per_time_period["total_requests"] += 1
51+
52+
env.process(arrival_process(env))
53+
env.run(until=simulation_time)
54+
55+
return SimulationOutput(
56+
total_requests=total_request_per_time_period,
57+
metric_2=str(data.avg_request_per_minute_per_user.mean),
58+
metric_n=str(data.avg_active_users.mean),
59+
)

src/app/schemas/simulation_output.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
class SimulationOutput(BaseModel):
77
"""Define the output of the simulation"""
88

9-
metric_1: str | int
9+
total_requests: dict[str, int | float]
1010
metric_2: str
1111
#......
1212
metric_n: str

0 commit comments

Comments
 (0)