iints-sdk-python35 0.0.18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iints/__init__.py +183 -0
- iints/analysis/__init__.py +12 -0
- iints/analysis/algorithm_xray.py +387 -0
- iints/analysis/baseline.py +92 -0
- iints/analysis/clinical_benchmark.py +198 -0
- iints/analysis/clinical_metrics.py +551 -0
- iints/analysis/clinical_tir_analyzer.py +136 -0
- iints/analysis/diabetes_metrics.py +43 -0
- iints/analysis/edge_efficiency.py +33 -0
- iints/analysis/edge_performance_monitor.py +315 -0
- iints/analysis/explainability.py +94 -0
- iints/analysis/explainable_ai.py +232 -0
- iints/analysis/hardware_benchmark.py +221 -0
- iints/analysis/metrics.py +117 -0
- iints/analysis/population_report.py +188 -0
- iints/analysis/reporting.py +345 -0
- iints/analysis/safety_index.py +311 -0
- iints/analysis/sensor_filtering.py +54 -0
- iints/analysis/validator.py +273 -0
- iints/api/__init__.py +0 -0
- iints/api/base_algorithm.py +307 -0
- iints/api/registry.py +103 -0
- iints/api/template_algorithm.py +195 -0
- iints/assets/iints_logo.png +0 -0
- iints/cli/__init__.py +0 -0
- iints/cli/cli.py +2598 -0
- iints/core/__init__.py +1 -0
- iints/core/algorithms/__init__.py +0 -0
- iints/core/algorithms/battle_runner.py +138 -0
- iints/core/algorithms/correction_bolus.py +95 -0
- iints/core/algorithms/discovery.py +92 -0
- iints/core/algorithms/fixed_basal_bolus.py +58 -0
- iints/core/algorithms/hybrid_algorithm.py +92 -0
- iints/core/algorithms/lstm_algorithm.py +138 -0
- iints/core/algorithms/mock_algorithms.py +162 -0
- iints/core/algorithms/pid_controller.py +88 -0
- iints/core/algorithms/standard_pump_algo.py +64 -0
- iints/core/device.py +0 -0
- iints/core/device_manager.py +64 -0
- iints/core/devices/__init__.py +3 -0
- iints/core/devices/models.py +160 -0
- iints/core/patient/__init__.py +9 -0
- iints/core/patient/bergman_model.py +341 -0
- iints/core/patient/models.py +285 -0
- iints/core/patient/patient_factory.py +117 -0
- iints/core/patient/profile.py +41 -0
- iints/core/safety/__init__.py +12 -0
- iints/core/safety/config.py +37 -0
- iints/core/safety/input_validator.py +95 -0
- iints/core/safety/supervisor.py +39 -0
- iints/core/simulation/__init__.py +0 -0
- iints/core/simulation/scenario_parser.py +61 -0
- iints/core/simulator.py +874 -0
- iints/core/supervisor.py +367 -0
- iints/data/__init__.py +53 -0
- iints/data/adapter.py +142 -0
- iints/data/column_mapper.py +398 -0
- iints/data/datasets.json +132 -0
- iints/data/demo/__init__.py +1 -0
- iints/data/demo/demo_cgm.csv +289 -0
- iints/data/importer.py +275 -0
- iints/data/ingestor.py +162 -0
- iints/data/nightscout.py +128 -0
- iints/data/quality_checker.py +550 -0
- iints/data/registry.py +166 -0
- iints/data/tidepool.py +38 -0
- iints/data/universal_parser.py +813 -0
- iints/data/virtual_patients/clinic_safe_baseline.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_hyper_challenge.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_hypo_prone.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_midnight.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_pizza.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_stress_meal.yaml +9 -0
- iints/data/virtual_patients/default_patient.yaml +11 -0
- iints/data/virtual_patients/patient_559_config.yaml +11 -0
- iints/emulation/__init__.py +80 -0
- iints/emulation/legacy_base.py +414 -0
- iints/emulation/medtronic_780g.py +337 -0
- iints/emulation/omnipod_5.py +367 -0
- iints/emulation/tandem_controliq.py +393 -0
- iints/highlevel.py +451 -0
- iints/learning/__init__.py +3 -0
- iints/learning/autonomous_optimizer.py +194 -0
- iints/learning/learning_system.py +122 -0
- iints/metrics.py +34 -0
- iints/population/__init__.py +11 -0
- iints/population/generator.py +131 -0
- iints/population/runner.py +327 -0
- iints/presets/__init__.py +28 -0
- iints/presets/presets.json +114 -0
- iints/research/__init__.py +30 -0
- iints/research/config.py +68 -0
- iints/research/dataset.py +319 -0
- iints/research/losses.py +73 -0
- iints/research/predictor.py +329 -0
- iints/scenarios/__init__.py +3 -0
- iints/scenarios/generator.py +92 -0
- iints/templates/__init__.py +0 -0
- iints/templates/default_algorithm.py +91 -0
- iints/templates/scenarios/__init__.py +0 -0
- iints/templates/scenarios/chaos_insulin_stacking.json +29 -0
- iints/templates/scenarios/chaos_runaway_ai.json +25 -0
- iints/templates/scenarios/example_scenario.json +35 -0
- iints/templates/scenarios/exercise_stress.json +30 -0
- iints/utils/__init__.py +3 -0
- iints/utils/plotting.py +50 -0
- iints/utils/run_io.py +152 -0
- iints/validation/__init__.py +133 -0
- iints/validation/schemas.py +94 -0
- iints/visualization/__init__.py +34 -0
- iints/visualization/cockpit.py +691 -0
- iints/visualization/uncertainty_cloud.py +612 -0
- iints_sdk_python35-0.0.18.dist-info/METADATA +225 -0
- iints_sdk_python35-0.0.18.dist-info/RECORD +118 -0
- iints_sdk_python35-0.0.18.dist-info/WHEEL +5 -0
- iints_sdk_python35-0.0.18.dist-info/entry_points.txt +10 -0
- iints_sdk_python35-0.0.18.dist-info/licenses/LICENSE +28 -0
- iints_sdk_python35-0.0.18.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Omnipod 5 Emulator - IINTS-AF
|
|
4
|
+
Emulates the Omnipod 5 with Horizon Algorithm.
|
|
5
|
+
|
|
6
|
+
Based on:
|
|
7
|
+
- Clinical studies ( ASSERT, ONSET)
|
|
8
|
+
- FDA 510(k) clearance documentation
|
|
9
|
+
- Technical specifications
|
|
10
|
+
|
|
11
|
+
Part of the #WeAreNotWaiting movement for transparent diabetes tech.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
from typing import Dict, List, Optional, Any
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
|
|
18
|
+
from .legacy_base import (
|
|
19
|
+
LegacyEmulator, PumpBehavior, PIDParameters, SafetyLimits,
|
|
20
|
+
SafetyLevel, EmulatorDecision
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class Omnipod5Behavior(PumpBehavior):
|
|
26
|
+
"""Omnipod 5 specific behavior profile"""
|
|
27
|
+
# Omnipod 5 specific parameters
|
|
28
|
+
adaptive_learning: bool = True
|
|
29
|
+
target_glucose: float = 110.0 # mg/dL (range 100-150)
|
|
30
|
+
max_bolus: float = 30.0 # Omnipod allows larger boluses
|
|
31
|
+
max_basal: float = 3.0 # units/hour (pod-specific)
|
|
32
|
+
delivery_frequency: int = 5 # minutes between deliveries
|
|
33
|
+
learning_window: int = 672 # hours (4 weeks) for adaptation
|
|
34
|
+
|
|
35
|
+
# Safety features
|
|
36
|
+
automatic_suspend: bool = True
|
|
37
|
+
low_glucose_suspend: bool = True
|
|
38
|
+
predictive_suspend: bool = True
|
|
39
|
+
|
|
40
|
+
def __init__(self):
|
|
41
|
+
super().__init__(
|
|
42
|
+
pump_name="Omnipod 5 with Horizon Algorithm",
|
|
43
|
+
manufacturer="Insulet Corporation",
|
|
44
|
+
safety_limits=SafetyLimits(
|
|
45
|
+
low_suspend_threshold=67.0, # Omnipod uses 67 mg/dL
|
|
46
|
+
high_suspend_threshold=250.0,
|
|
47
|
+
max_bolus=30.0,
|
|
48
|
+
max_basal_rate=3.0,
|
|
49
|
+
max_daily_total=80.0,
|
|
50
|
+
auto_off_duration=60.0,
|
|
51
|
+
low_suspend_duration=5.0,
|
|
52
|
+
target_glucose=110.0
|
|
53
|
+
),
|
|
54
|
+
pid_parameters=PIDParameters(
|
|
55
|
+
kp=0.025, # Adaptive PID tuning
|
|
56
|
+
ki=0.003,
|
|
57
|
+
kd=0.035,
|
|
58
|
+
target_glucose=110.0,
|
|
59
|
+
integral_limit=4.0
|
|
60
|
+
),
|
|
61
|
+
correction_factor=45.0,
|
|
62
|
+
carb_ratio=8.0, # Omnipod default
|
|
63
|
+
insulin_sensitivity_factor=45.0,
|
|
64
|
+
active_insulin_duration=5.0, # Omnipod uses 5-hour DIA
|
|
65
|
+
safety_level=SafetyLevel.MODERATE
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class Omnipod5Emulator(LegacyEmulator):
|
|
70
|
+
"""
|
|
71
|
+
Emulates Omnipod 5 with Horizon algorithm.
|
|
72
|
+
|
|
73
|
+
Omnipod 5 is a tubeless, patch pump with:
|
|
74
|
+
- Adaptive learning (adjusts based on user patterns)
|
|
75
|
+
- Automatic insulin delivery every 5 minutes
|
|
76
|
+
- Activity and sleep mode support
|
|
77
|
+
- Customizable target glucose (100-150 mg/dL)
|
|
78
|
+
|
|
79
|
+
Key characteristics:
|
|
80
|
+
- Tubeless design (no tubing)
|
|
81
|
+
- Adaptive algorithm learns user patterns
|
|
82
|
+
- Conservative low glucose threshold (67 mg/dL)
|
|
83
|
+
- Activity/Sleep modes with higher targets
|
|
84
|
+
|
|
85
|
+
Sources:
|
|
86
|
+
- ASSERT Trial Results
|
|
87
|
+
- ONSET Trial Results
|
|
88
|
+
- FDA 510(k) K203467
|
|
89
|
+
- Omnipod 5 User Guide
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def __init__(self):
|
|
93
|
+
"""Initialize the Omnipod 5 emulator"""
|
|
94
|
+
super().__init__()
|
|
95
|
+
self.behavior = Omnipod5Behavior()
|
|
96
|
+
self._learning_model = {
|
|
97
|
+
'user_sensitivity': 50.0,
|
|
98
|
+
'correction_factor': 50.0,
|
|
99
|
+
'carb_ratio': 10.0,
|
|
100
|
+
'basal_rate': 1.0,
|
|
101
|
+
'patterns_learned': 0
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
def _get_default_behavior(self) -> Omnipod5Behavior:
|
|
105
|
+
"""Get Omnipod 5 default behavior"""
|
|
106
|
+
return Omnipod5Behavior()
|
|
107
|
+
|
|
108
|
+
def get_sources(self) -> List[Dict[str, str]]:
|
|
109
|
+
"""Get sources for Omnipod 5 emulation logic"""
|
|
110
|
+
return [
|
|
111
|
+
{
|
|
112
|
+
'title': 'ASSERT Trial - Omnipod 5 Pivotal Study',
|
|
113
|
+
'type': 'clinical_study',
|
|
114
|
+
'year': '2021',
|
|
115
|
+
'url': 'https://www.omnipod.com/assert-trial'
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
'title': 'ONSET Trial - Omnipod 5 in Type 2',
|
|
119
|
+
'type': 'clinical_study',
|
|
120
|
+
'year': '2022',
|
|
121
|
+
'url': 'https://www.omnipod.com/onset-trial'
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
'title': 'FDA 510(k) K203467 - Omnipod 5 System',
|
|
125
|
+
'type': 'regulatory',
|
|
126
|
+
'year': '2021',
|
|
127
|
+
'url': 'https://www.accessdata.fda.gov/'
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
'title': 'Omnipod 5 User Guide',
|
|
131
|
+
'type': 'technical_manual',
|
|
132
|
+
'year': '2021',
|
|
133
|
+
'url': 'https://www.omnipod.com/'
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
def set_activity_mode(self, enabled: bool, mode_type: str = 'exercise'):
|
|
138
|
+
"""Enable activity or sleep mode"""
|
|
139
|
+
if mode_type == 'exercise':
|
|
140
|
+
self.behavior.target_glucose = 140.0 if enabled else 110.0
|
|
141
|
+
elif mode_type == 'sleep':
|
|
142
|
+
self.behavior.target_glucose = 130.0 if enabled else 110.0
|
|
143
|
+
|
|
144
|
+
if self.behavior.pid_parameters:
|
|
145
|
+
self.behavior.pid_parameters.target_glucose = self.behavior.target_glucose
|
|
146
|
+
|
|
147
|
+
print(f" {mode_type.title()} mode: {'ON' if enabled else 'OFF'} (target: {self.behavior.target_glucose} mg/dL)")
|
|
148
|
+
|
|
149
|
+
def emulate_decision(self,
|
|
150
|
+
glucose: float,
|
|
151
|
+
velocity: float,
|
|
152
|
+
insulin_on_board: float,
|
|
153
|
+
carbs: float,
|
|
154
|
+
current_time: float = 0) -> EmulatorDecision:
|
|
155
|
+
"""
|
|
156
|
+
Emulate Omnipod 5 decision-making for current conditions.
|
|
157
|
+
|
|
158
|
+
Omnipod 5 features:
|
|
159
|
+
1. Adaptive learning (adjusts based on user response)
|
|
160
|
+
2. Automatic delivery every 5 minutes
|
|
161
|
+
3. Activity and sleep modes
|
|
162
|
+
4. Conservative low glucose suspend
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
glucose: Current glucose (mg/dL)
|
|
166
|
+
velocity: Rate of change (mg/dL/min)
|
|
167
|
+
insulin_on_board: Current IOB (units)
|
|
168
|
+
carbs: Carbs consumed (grams)
|
|
169
|
+
current_time: Simulation time (minutes)
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
EmulatorDecision with insulin delivery details
|
|
173
|
+
"""
|
|
174
|
+
reasoning = []
|
|
175
|
+
safety_overrides = []
|
|
176
|
+
action = 'deliver'
|
|
177
|
+
insulin_delivered = 0.0
|
|
178
|
+
pid = self.behavior.pid_parameters
|
|
179
|
+
safety = self.behavior.safety_limits
|
|
180
|
+
|
|
181
|
+
# Omnipod uses slightly different low threshold
|
|
182
|
+
low_threshold = safety.low_suspend_threshold # 67 mg/dL
|
|
183
|
+
|
|
184
|
+
# Check safety constraints
|
|
185
|
+
should_suspend, suspend_reasons, adjustment = self._check_safety_constraints(
|
|
186
|
+
glucose, velocity, insulin_on_board, carbs, current_time
|
|
187
|
+
)
|
|
188
|
+
reasoning.extend(suspend_reasons)
|
|
189
|
+
|
|
190
|
+
# Omnipod-specific low threshold
|
|
191
|
+
if glucose < low_threshold:
|
|
192
|
+
should_suspend = True
|
|
193
|
+
reasoning.append(
|
|
194
|
+
f"Omnipod low suspend: glucose {glucose:.0f} < {low_threshold:.0f} mg/dL"
|
|
195
|
+
)
|
|
196
|
+
safety_overrides.append("Omnipod proprietary low threshold")
|
|
197
|
+
|
|
198
|
+
if should_suspend:
|
|
199
|
+
action = 'suspend'
|
|
200
|
+
insulin_delivered = 0.0
|
|
201
|
+
safety_overrides.extend(suspend_reasons)
|
|
202
|
+
else:
|
|
203
|
+
# Calculate correction with Omnipod's adaptive approach
|
|
204
|
+
error = glucose - pid.target_glucose
|
|
205
|
+
|
|
206
|
+
# Adaptive PID (simplified)
|
|
207
|
+
self.state['integral_term'] += error * 0.04
|
|
208
|
+
self.state['integral_term'] = max(-pid.integral_limit,
|
|
209
|
+
min(pid.integral_limit,
|
|
210
|
+
self.state['integral_term']))
|
|
211
|
+
|
|
212
|
+
# Meal bolus
|
|
213
|
+
meal_bolus = 0.0
|
|
214
|
+
if carbs > 0:
|
|
215
|
+
meal_bolus = carbs / self.behavior.carb_ratio
|
|
216
|
+
reasoning.append(
|
|
217
|
+
f"Meal bolus: {carbs:.0f}g → {meal_bolus:.2f} U"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# Calculate correction
|
|
221
|
+
pid_output = (
|
|
222
|
+
pid.kp * error +
|
|
223
|
+
pid.ki * self.state['integral_term'] +
|
|
224
|
+
pid.kd * velocity
|
|
225
|
+
) / self.behavior.correction_factor
|
|
226
|
+
|
|
227
|
+
# Apply adaptive learning (simplified)
|
|
228
|
+
if self._learning_model['patterns_learned'] > 10:
|
|
229
|
+
adaptation_factor = 1.0 + (
|
|
230
|
+
(50.0 - self._learning_model['user_sensitivity']) / 100
|
|
231
|
+
)
|
|
232
|
+
pid_output *= adaptation_factor
|
|
233
|
+
|
|
234
|
+
insulin_delivered = max(0, pid_output + meal_bolus) * adjustment
|
|
235
|
+
|
|
236
|
+
# Omnipod max delivery
|
|
237
|
+
insulin_delivered = min(insulin_delivered, safety.max_bolus)
|
|
238
|
+
|
|
239
|
+
if insulin_delivered > 0:
|
|
240
|
+
reasoning.append(
|
|
241
|
+
f"Omnipod correction: error={error:.0f} → {insulin_delivered:.2f} U"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# Simulate learning
|
|
245
|
+
self._learning_model['patterns_learned'] += 1
|
|
246
|
+
else:
|
|
247
|
+
reasoning.append("No delivery needed")
|
|
248
|
+
|
|
249
|
+
# Update cumulative insulin
|
|
250
|
+
self.state['cumulative_insulin'] += insulin_delivered
|
|
251
|
+
if self.state['cumulative_insulin'] > safety.max_daily_total:
|
|
252
|
+
safety_overrides.append("Daily total limit reached")
|
|
253
|
+
insulin_delivered = 0.0
|
|
254
|
+
action = 'suspend'
|
|
255
|
+
|
|
256
|
+
# Create decision
|
|
257
|
+
decision = EmulatorDecision(
|
|
258
|
+
insulin_delivered=insulin_delivered,
|
|
259
|
+
action=action,
|
|
260
|
+
reasoning=reasoning,
|
|
261
|
+
safety_overrides=safety_overrides,
|
|
262
|
+
predicted_glucose=glucose + velocity * 30,
|
|
263
|
+
confidence=0.87
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
self._decision_history.append(decision)
|
|
267
|
+
|
|
268
|
+
return decision
|
|
269
|
+
|
|
270
|
+
def get_algorithm_personality(self) -> Dict:
|
|
271
|
+
"""
|
|
272
|
+
Get the algorithm's "personality" characteristics.
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Dictionary with personality traits
|
|
276
|
+
"""
|
|
277
|
+
return {
|
|
278
|
+
'name': 'Omnipod 5 Horizon',
|
|
279
|
+
'type': 'Adaptive Hybrid Closed-Loop',
|
|
280
|
+
'personality': {
|
|
281
|
+
'aggressiveness': 'Adaptive (starts conservative)',
|
|
282
|
+
'hypo_aversion': 'High (67 mg/dL threshold)',
|
|
283
|
+
'response_speed': 'Moderate',
|
|
284
|
+
'correction_aggressiveness': 'Adaptive',
|
|
285
|
+
'meal_handling': 'Automatic delivery',
|
|
286
|
+
'predictive_features': 'Adaptive learning, Activity modes'
|
|
287
|
+
},
|
|
288
|
+
'key_differences': [
|
|
289
|
+
'Tubeless patch pump design',
|
|
290
|
+
'Adaptive algorithm learns user patterns',
|
|
291
|
+
'Conservative low threshold (67 mg/dL)',
|
|
292
|
+
'Activity & Sleep modes',
|
|
293
|
+
'Longer insulin duration (5 hours)'
|
|
294
|
+
],
|
|
295
|
+
'limitations': [
|
|
296
|
+
'Pod life limited to 72-80 hours',
|
|
297
|
+
'Requires different set for exercise',
|
|
298
|
+
'Learning period needed for best results'
|
|
299
|
+
]
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def demo_omnipod5():
|
|
304
|
+
"""Demonstrate Omnipod 5 emulator"""
|
|
305
|
+
print("=" * 70)
|
|
306
|
+
print("OMNIPOD 5 EMULATOR DEMONSTRATION")
|
|
307
|
+
print("=" * 70)
|
|
308
|
+
|
|
309
|
+
emulator = Omnipod5Emulator()
|
|
310
|
+
|
|
311
|
+
# Print behavior profile
|
|
312
|
+
print("\n Algorithm Personality:")
|
|
313
|
+
personality = emulator.get_algorithm_personality()
|
|
314
|
+
print(f" Name: {personality['name']}")
|
|
315
|
+
print(f" Type: {personality['type']}")
|
|
316
|
+
print(f" Hypo Aversion: {personality['personality']['hypo_aversion']}")
|
|
317
|
+
|
|
318
|
+
# Print sources
|
|
319
|
+
print("\n Sources:")
|
|
320
|
+
for source in emulator.get_sources():
|
|
321
|
+
print(f" - [{source['type']}] {source['title']} ({source['year']})")
|
|
322
|
+
|
|
323
|
+
# Simulate scenarios
|
|
324
|
+
print("\n🧪 Scenario Simulation:")
|
|
325
|
+
print("-" * 50)
|
|
326
|
+
|
|
327
|
+
scenarios = [
|
|
328
|
+
{'glucose': 170, 'velocity': 1.0, 'iob': 1.0, 'carbs': 0, 'desc': 'Elevated glucose'},
|
|
329
|
+
{'glucose': 110, 'velocity': 0.2, 'iob': 1.5, 'carbs': 0, 'desc': 'At target'},
|
|
330
|
+
{'glucose': 67, 'velocity': -0.5, 'iob': 1.0, 'carbs': 0, 'desc': 'At Omnipod low threshold'},
|
|
331
|
+
{'glucose': 250, 'velocity': 2.0, 'iob': 0.5, 'carbs': 50, 'desc': 'High with large meal'},
|
|
332
|
+
]
|
|
333
|
+
|
|
334
|
+
for i, scenario in enumerate(scenarios, 1):
|
|
335
|
+
decision = emulator.emulate_decision(
|
|
336
|
+
glucose=scenario['glucose'],
|
|
337
|
+
velocity=scenario['velocity'],
|
|
338
|
+
insulin_on_board=scenario['iob'],
|
|
339
|
+
carbs=scenario['carbs'],
|
|
340
|
+
current_time=i * 5
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
print(f"\n Scenario {i}: {scenario['desc']}")
|
|
344
|
+
print(f" Glucose: {scenario['glucose']} mg/dL, Velocity: {scenario['velocity']} mg/dL/min")
|
|
345
|
+
print(f" → Action: {decision.action}, Insulin: {decision.insulin_delivered:.2f} U")
|
|
346
|
+
print(f" Reasoning: {', '.join(decision.reasoning[:2])}")
|
|
347
|
+
|
|
348
|
+
# Test activity mode
|
|
349
|
+
print("\n Activity Mode Test:")
|
|
350
|
+
print("-" * 50)
|
|
351
|
+
emulator.set_activity_mode(True, 'exercise')
|
|
352
|
+
|
|
353
|
+
decision = emulator.emulate_decision(
|
|
354
|
+
glucose=160, velocity=0.5, insulin_on_board=1.0, carbs=0, current_time=0
|
|
355
|
+
)
|
|
356
|
+
print(f" Exercise mode: glucose=160 → {decision.insulin_delivered:.2f} U")
|
|
357
|
+
|
|
358
|
+
emulator.set_activity_mode(False)
|
|
359
|
+
|
|
360
|
+
print("\n" + "=" * 70)
|
|
361
|
+
print("OMNIPOD 5 EMULATOR DEMONSTRATION COMPLETE")
|
|
362
|
+
print("=" * 70)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
if __name__ == "__main__":
|
|
366
|
+
demo_omnipod5()
|
|
367
|
+
|