Update app.py
Browse files
app.py
CHANGED
@@ -230,13 +230,22 @@ def setup_figure(env):
|
|
230 |
|
231 |
return fig
|
232 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
233 |
# Streamlit app
|
234 |
-
st.title("
|
235 |
|
236 |
-
|
237 |
-
|
|
|
|
|
238 |
|
239 |
-
# Effect options
|
240 |
st.sidebar.header("Environmental Effects")
|
241 |
radiation = st.sidebar.checkbox("Radiation")
|
242 |
predation = st.sidebar.checkbox("Predation")
|
@@ -248,62 +257,120 @@ effects = {
|
|
248 |
"symbiosis": symbiosis
|
249 |
}
|
250 |
|
251 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
252 |
chart_placeholder = st.empty()
|
253 |
-
stop_button_placeholder = st.empty()
|
254 |
|
255 |
# Initialize session state
|
256 |
if 'running' not in st.session_state:
|
257 |
st.session_state.running = False
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
|
259 |
def toggle_simulation():
|
260 |
st.session_state.running = not st.session_state.running
|
|
|
|
|
|
|
|
|
|
|
|
|
261 |
|
262 |
start_stop_button = st.button("Start/Stop Simulation", on_click=toggle_simulation)
|
263 |
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
278 |
|
279 |
-
|
280 |
-
|
281 |
-
fig.data[i].x = data["x"]
|
282 |
-
fig.data[i].y = data["y"]
|
283 |
-
fig.data[i].marker.size = data["size"]
|
284 |
|
285 |
-
|
286 |
-
|
287 |
-
fig.data[len(cell_data)].y = total_population
|
288 |
|
289 |
-
|
290 |
-
for i, (cell_type, counts) in enumerate(population_history.items()):
|
291 |
-
fig.data[len(cell_data) + 1 + i].y = counts
|
292 |
|
293 |
-
# Update
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
fig.data[-1].y = list(organelle_counts.values())
|
299 |
-
|
300 |
-
chart_placeholder.plotly_chart(fig, use_container_width=True)
|
301 |
time.sleep(update_interval)
|
302 |
|
303 |
-
|
304 |
-
|
305 |
-
|
|
|
|
|
|
|
306 |
|
307 |
-
#
|
308 |
-
|
309 |
-
|
|
|
230 |
|
231 |
return fig
|
232 |
|
233 |
+
def format_number(num):
|
234 |
+
if num >= 1_000_000:
|
235 |
+
return f"{num/1_000_000:.1f}M"
|
236 |
+
elif num >= 1_000:
|
237 |
+
return f"{num/1_000:.1f}K"
|
238 |
+
else:
|
239 |
+
return str(num)
|
240 |
+
|
241 |
# Streamlit app
|
242 |
+
st.title("Continuous Cell Evolution Simulation")
|
243 |
|
244 |
+
# Sidebar for controls and live statistics
|
245 |
+
st.sidebar.header("Simulation Controls")
|
246 |
+
initial_cells = st.sidebar.slider("Initial number of cells", 10, 500, 200)
|
247 |
+
update_interval = st.sidebar.slider("Update interval (seconds)", 0.01, 1.0, 0.05)
|
248 |
|
|
|
249 |
st.sidebar.header("Environmental Effects")
|
250 |
radiation = st.sidebar.checkbox("Radiation")
|
251 |
predation = st.sidebar.checkbox("Predation")
|
|
|
257 |
"symbiosis": symbiosis
|
258 |
}
|
259 |
|
260 |
+
# Live statistics placeholders
|
261 |
+
st.sidebar.header("Live Statistics")
|
262 |
+
total_cells_text = st.sidebar.empty()
|
263 |
+
cell_type_breakdown = st.sidebar.empty()
|
264 |
+
dominant_type_text = st.sidebar.empty()
|
265 |
+
avg_energy_text = st.sidebar.empty()
|
266 |
+
total_merges_text = st.sidebar.empty()
|
267 |
+
|
268 |
+
# Event log
|
269 |
+
st.sidebar.header("Event Log")
|
270 |
+
event_log = deque(maxlen=10) # Keep the last 10 events
|
271 |
+
event_log_text = st.sidebar.empty()
|
272 |
+
|
273 |
+
# Create placeholders for the chart
|
274 |
chart_placeholder = st.empty()
|
|
|
275 |
|
276 |
# Initialize session state
|
277 |
if 'running' not in st.session_state:
|
278 |
st.session_state.running = False
|
279 |
+
if 'total_merges' not in st.session_state:
|
280 |
+
st.session_state.total_merges = 0
|
281 |
+
if 'env' not in st.session_state:
|
282 |
+
st.session_state.env = None
|
283 |
+
if 'fig' not in st.session_state:
|
284 |
+
st.session_state.fig = None
|
285 |
|
286 |
def toggle_simulation():
|
287 |
st.session_state.running = not st.session_state.running
|
288 |
+
if st.session_state.running and st.session_state.env is None:
|
289 |
+
st.session_state.env = Environment(100, 100, effects)
|
290 |
+
for _ in range(initial_cells):
|
291 |
+
cell = Cell(random.uniform(0, st.session_state.env.width), random.uniform(0, st.session_state.env.height))
|
292 |
+
st.session_state.env.add_cell(cell)
|
293 |
+
st.session_state.fig = setup_figure(st.session_state.env)
|
294 |
|
295 |
start_stop_button = st.button("Start/Stop Simulation", on_click=toggle_simulation)
|
296 |
|
297 |
+
def run_simulation():
|
298 |
+
while True:
|
299 |
+
if st.session_state.running:
|
300 |
+
for _ in range(4): # Update 4 times per frame to increase simulation speed
|
301 |
+
initial_cell_count = len(st.session_state.env.cells)
|
302 |
+
st.session_state.env.update()
|
303 |
+
final_cell_count = len(st.session_state.env.cells)
|
304 |
+
|
305 |
+
# Check for merges
|
306 |
+
if final_cell_count < initial_cell_count:
|
307 |
+
merges = initial_cell_count - final_cell_count
|
308 |
+
st.session_state.total_merges += merges
|
309 |
+
event_log.appendleft(f"Time {st.session_state.env.time}: {merges} cell merge(s) occurred!")
|
310 |
+
|
311 |
+
# Check for new cell types
|
312 |
+
for cell in st.session_state.env.cells:
|
313 |
+
if cell.cell_type not in st.session_state.env.population_history:
|
314 |
+
event_log.appendleft(f"Time {st.session_state.env.time}: New cell type '{cell.cell_type}' emerged!")
|
315 |
+
|
316 |
+
time.sleep(update_interval)
|
317 |
+
|
318 |
+
def update_chart():
|
319 |
+
while True:
|
320 |
+
if st.session_state.running and st.session_state.fig is not None:
|
321 |
+
with st.session_state.fig.batch_update():
|
322 |
+
cell_data, population_history, colors = st.session_state.env.get_visualization_data()
|
323 |
+
|
324 |
+
# Update cell distribution
|
325 |
+
for i, (cell_type, data) in enumerate(cell_data.items()):
|
326 |
+
st.session_state.fig.data[i].x = data["x"]
|
327 |
+
st.session_state.fig.data[i].y = data["y"]
|
328 |
+
st.session_state.fig.data[i].marker.size = data["size"]
|
329 |
+
|
330 |
+
# Update total population
|
331 |
+
total_population = [sum(counts) for counts in zip(*population_history.values())]
|
332 |
+
st.session_state.fig.data[len(cell_data)].y = total_population
|
333 |
+
|
334 |
+
# Update population by cell type
|
335 |
+
for i, (cell_type, counts) in enumerate(population_history.items()):
|
336 |
+
st.session_state.fig.data[len(cell_data) + 1 + i].y = counts
|
337 |
+
|
338 |
+
# Update organelle distribution
|
339 |
+
organelle_counts = {"nucleus": 0, "mitochondria": 0, "chloroplast": 0, "endoplasmic_reticulum": 0, "golgi_apparatus": 0}
|
340 |
+
for cell in st.session_state.env.cells:
|
341 |
+
for organelle in cell.organelles:
|
342 |
+
organelle_counts[organelle] += 1
|
343 |
+
st.session_state.fig.data[-1].y = list(organelle_counts.values())
|
344 |
+
|
345 |
+
# Update live statistics
|
346 |
+
total_cells = len(st.session_state.env.cells)
|
347 |
+
total_cells_text.text(f"Total Cells: {format_number(total_cells)}")
|
348 |
+
|
349 |
+
cell_type_counts = {cell_type: len([c for c in st.session_state.env.cells if c.cell_type == cell_type]) for cell_type in st.session_state.env.population_history.keys()}
|
350 |
+
cell_type_breakdown.text("Cell Type Breakdown:\n" + "\n".join([f"{cell_type}: {format_number(count)}" for cell_type, count in cell_type_counts.items()]))
|
351 |
|
352 |
+
dominant_type = max(cell_type_counts, key=cell_type_counts.get)
|
353 |
+
dominant_type_text.text(f"Dominant Type: {dominant_type}")
|
|
|
|
|
|
|
354 |
|
355 |
+
avg_energy = sum(cell.energy for cell in st.session_state.env.cells) / total_cells if total_cells > 0 else 0
|
356 |
+
avg_energy_text.text(f"Avg Energy: {avg_energy:.2f}")
|
|
|
357 |
|
358 |
+
total_merges_text.text(f"Total Merges: {st.session_state.total_merges}")
|
|
|
|
|
359 |
|
360 |
+
# Update event log
|
361 |
+
event_log_text.text("Recent Events:\n" + "\n".join(event_log))
|
362 |
+
|
363 |
+
chart_placeholder.plotly_chart(st.session_state.fig, use_container_width=True)
|
364 |
+
|
|
|
|
|
|
|
365 |
time.sleep(update_interval)
|
366 |
|
367 |
+
# Start the simulation and chart update threads
|
368 |
+
simulation_thread = threading.Thread(target=run_simulation)
|
369 |
+
chart_thread = threading.Thread(target=update_chart)
|
370 |
+
|
371 |
+
simulation_thread.start()
|
372 |
+
chart_thread.start()
|
373 |
|
374 |
+
# Keep the main thread alive
|
375 |
+
while True:
|
376 |
+
time.sleep(1)
|