procedure generation updated, UI changed
Browse files- app.py +444 -697
- biomaterials.py +124 -0
- cell_lines.py +178 -0
app.py
CHANGED
|
@@ -1,697 +1,444 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import streamlit as st
|
| 3 |
-
import pandas as pd
|
| 4 |
-
import joblib
|
| 5 |
-
import optuna
|
| 6 |
-
import numpy as np
|
| 7 |
-
from catboost import CatBoostClassifier
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
from google import
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
_original_number_input = st.number_input
|
| 16 |
-
|
| 17 |
-
def safe_number_input(label, **kwargs):
|
| 18 |
-
"""
|
| 19 |
-
Clamp `value` into [min_value, max_value] and warn if we had to adjust.
|
| 20 |
-
Then call the real st.number_input with the clamped default.
|
| 21 |
-
"""
|
| 22 |
-
min_value = kwargs.get("min_value", float("-inf"))
|
| 23 |
-
max_value = kwargs.get("max_value", float("inf"))
|
| 24 |
-
value = kwargs.get("value", min_value)
|
| 25 |
-
clamped = min(max(value, min_value), max_value)
|
| 26 |
-
if clamped != value:
|
| 27 |
-
st.warning(
|
| 28 |
-
f"⚠️ Default for “{label}” ({value}) was outside "
|
| 29 |
-
f"[{min_value}, {max_value}]; using {clamped} instead."
|
| 30 |
-
)
|
| 31 |
-
kwargs["value"] = clamped
|
| 32 |
-
return _original_number_input(label, **kwargs)
|
| 33 |
-
|
| 34 |
-
st.number_input = safe_number_input
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
st.
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
"
|
| 74 |
-
"
|
| 75 |
-
"
|
| 76 |
-
"
|
| 77 |
-
"
|
| 78 |
-
"
|
| 79 |
-
"
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
"
|
| 141 |
-
"
|
| 142 |
-
"
|
| 143 |
-
"
|
| 144 |
-
"
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
"
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
"
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
"
|
| 205 |
-
"
|
| 206 |
-
"
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
"
|
| 246 |
-
|
| 247 |
-
"
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
"
|
| 252 |
-
|
| 253 |
-
"
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
"
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
"
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
"
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
c1, c2, c3, c4, c5 = st.columns([2, 1, 1, 1, 0.3])
|
| 446 |
-
mat = c1.selectbox(
|
| 447 |
-
"", options,
|
| 448 |
-
index=options.index(row['mat']) if row['mat'] in options else 0,
|
| 449 |
-
key=f"bio_mat_{i}"
|
| 450 |
-
)
|
| 451 |
-
st.session_state.bio_rows[i]['mat'] = mat
|
| 452 |
-
|
| 453 |
-
mn = c2.number_input(
|
| 454 |
-
"Min", min_value=0.0, max_value=row['max'],
|
| 455 |
-
value=row['min'], step=row['step'], key=f"bio_min_{i}"
|
| 456 |
-
)
|
| 457 |
-
mx = c3.number_input(
|
| 458 |
-
"Max", min_value=row['step'], max_value=100.0,
|
| 459 |
-
value=max(row['max'], row['step']), step=row['step'],
|
| 460 |
-
key=f"bio_max_{i}"
|
| 461 |
-
)
|
| 462 |
-
st.session_state.bio_rows[i].update(min=mn, max=mx)
|
| 463 |
-
|
| 464 |
-
st.session_state.bio_rows[i]['step'] = c4.number_input(
|
| 465 |
-
"Step", min_value=0.0,
|
| 466 |
-
max_value=(mx - mn) if mx > mn else 0.1,
|
| 467 |
-
value=row['step'], step=0.1, key=f"bio_step_{i}"
|
| 468 |
-
)
|
| 469 |
-
|
| 470 |
-
if c5.button("❌", key=f"rem_{i}"):
|
| 471 |
-
st.session_state.bio_rows.pop(i)
|
| 472 |
-
st.rerun()
|
| 473 |
-
|
| 474 |
-
st.markdown("---")
|
| 475 |
-
|
| 476 |
-
st.subheader("Cell Line & Density (10^6 cells/ml)")
|
| 477 |
-
col1, col2, col3, col4 = st.columns([2,1,1,1])
|
| 478 |
-
cell_line = col1.selectbox("Cell Line", CELL_LINE_OPTIONS)
|
| 479 |
-
|
| 480 |
-
# ── Special handling for NoCellCultured (acellular 3D printing) ──
|
| 481 |
-
if cell_line == "NoCellCultured":
|
| 482 |
-
st.info("🧪 **Acellular 3D printing mode** – No cells will be included. Cell density is forced to 0.")
|
| 483 |
-
st.session_state.density_range.update({'min': 0.0, 'max': 0.0, 'step': 0.0})
|
| 484 |
-
|
| 485 |
-
col2.number_input("Min Density", value=0.0, disabled=True, key="cd_min")
|
| 486 |
-
col3.number_input("Max Density", value=0.0, disabled=True, key="cd_max")
|
| 487 |
-
col4.number_input("Step", value=0.0, disabled=True, key="cd_step")
|
| 488 |
-
else:
|
| 489 |
-
# Reset to sensible defaults if coming from NoCellCultured
|
| 490 |
-
if st.session_state.density_range['max'] == 0.0:
|
| 491 |
-
st.session_state.density_range.update({'min': 1.0, 'max': 20.0, 'step': 0.5})
|
| 492 |
-
|
| 493 |
-
dr = st.session_state.density_range
|
| 494 |
-
dmin = col2.number_input(
|
| 495 |
-
"Min Density", min_value=0.0, max_value=dr['max'],
|
| 496 |
-
value=dr['min'], step=dr['step'], key="cd_min"
|
| 497 |
-
)
|
| 498 |
-
dmax = col3.number_input(
|
| 499 |
-
"Max Density", min_value=dr['step'], max_value=1000.0,
|
| 500 |
-
value=max(dr['max'], dr['step']), step=dr['step'], key="cd_max"
|
| 501 |
-
)
|
| 502 |
-
dstep = col4.number_input(
|
| 503 |
-
"Step", min_value=0.0,
|
| 504 |
-
max_value=(dmax - dmin) if dmax > dmin else 0.1,
|
| 505 |
-
value=dr['step'], step=0.1, key="cd_step"
|
| 506 |
-
)
|
| 507 |
-
st.session_state.density_range.update({'min': dmin, 'max': dmax, 'step': dstep})
|
| 508 |
-
|
| 509 |
-
st.markdown("---")
|
| 510 |
-
|
| 511 |
-
st.markdown("---")
|
| 512 |
-
st.subheader("Crosslinking Settings")
|
| 513 |
-
|
| 514 |
-
col_cross1, col_cross2 = st.columns(2)
|
| 515 |
-
|
| 516 |
-
disable_physical = col_cross1.checkbox(
|
| 517 |
-
"Disable Physical Crosslinking",
|
| 518 |
-
value=False,
|
| 519 |
-
help="Check if you do not want physical/ionic crosslinking (e.g. CaCl₂ bath, temperature-induced)"
|
| 520 |
-
)
|
| 521 |
-
|
| 522 |
-
disable_photo = col_cross2.checkbox(
|
| 523 |
-
"Disable Photo Crosslinking",
|
| 524 |
-
value=False,
|
| 525 |
-
help="Check if you do not want UV/visible light crosslinking"
|
| 526 |
-
)
|
| 527 |
-
|
| 528 |
-
st.subheader("Printing Parameters (enter range)")
|
| 529 |
-
|
| 530 |
-
for name in PRINT_PARAM_NAMES:
|
| 531 |
-
# Special handling for crosslinking durations
|
| 532 |
-
if name == "Physical Crosslinking Duration (s)" and disable_physical:
|
| 533 |
-
st.session_state.pp_ranges[name].update({'min': 0.0, 'max': 0.0, 'step': 0.0})
|
| 534 |
-
c1, c2, c3, c4 = st.columns([2,1,1,1])
|
| 535 |
-
c1.write(name + " (DISABLED)")
|
| 536 |
-
c2.number_input("Min", value=0.0, disabled=True, key=f"pp_min_{name}")
|
| 537 |
-
c3.number_input("Max", value=0.0, disabled=True, key=f"pp_max_{name}")
|
| 538 |
-
c4.number_input("Step", value=0.0, disabled=True, key=f"pp_step_{name}")
|
| 539 |
-
continue
|
| 540 |
-
|
| 541 |
-
elif name == "Photo Crosslinking Duration (s)" and disable_photo:
|
| 542 |
-
st.session_state.pp_ranges[name].update({'min': 0.0, 'max': 0.0, 'step': 0.0})
|
| 543 |
-
c1, c2, c3, c4 = st.columns([2,1,1,1])
|
| 544 |
-
c1.write(name + " (DISABLED)")
|
| 545 |
-
c2.number_input("Min", value=0.0, disabled=True, key=f"pp_min_{name}")
|
| 546 |
-
c3.number_input("Max", value=0.0, disabled=True, key=f"pp_max_{name}")
|
| 547 |
-
c4.number_input("Step", value=0.0, disabled=True, key=f"pp_step_{name}")
|
| 548 |
-
continue
|
| 549 |
-
|
| 550 |
-
# Normal handling for all other parameters
|
| 551 |
-
pmin = st.session_state.pp_ranges[name]['min']
|
| 552 |
-
pmax = st.session_state.pp_ranges[name]['max']
|
| 553 |
-
pstep = st.session_state.pp_ranges[name]['step']
|
| 554 |
-
|
| 555 |
-
c1, c2, c3, c4 = st.columns([2,1,1,1])
|
| 556 |
-
c1.write(name)
|
| 557 |
-
|
| 558 |
-
pmin = c2.number_input(
|
| 559 |
-
"Min", min_value=0.0, max_value=pmax,
|
| 560 |
-
value=pmin, step=pstep, key=f"pp_min_{name}"
|
| 561 |
-
)
|
| 562 |
-
pmax = c3.number_input(
|
| 563 |
-
"Max", min_value=pstep, max_value=10000.0,
|
| 564 |
-
value=max(pmax, pstep), step=pstep, key=f"pp_max_{name}"
|
| 565 |
-
)
|
| 566 |
-
pstep = c4.number_input(
|
| 567 |
-
"Step", min_value=0.0,
|
| 568 |
-
max_value=(pmax - pmin) if pmax > pmin else 1.0,
|
| 569 |
-
value=pstep, step=max(1e-3, pstep/10),
|
| 570 |
-
key=f"pp_step_{name}"
|
| 571 |
-
)
|
| 572 |
-
st.session_state.pp_ranges[name].update(min=pmin, max=pmax, step=pstep)
|
| 573 |
-
st.markdown("---")
|
| 574 |
-
|
| 575 |
-
if st.button("🛠️ Optimize WSSQ"):
|
| 576 |
-
with st.spinner("Running Optuna…"):
|
| 577 |
-
def objective(trial):
|
| 578 |
-
bi_vals = {
|
| 579 |
-
r['mat']: trial.suggest_float(
|
| 580 |
-
f"bio__{r['mat']}", r['min'], r['max'], step=r['step']
|
| 581 |
-
)
|
| 582 |
-
for r in st.session_state.bio_rows
|
| 583 |
-
}
|
| 584 |
-
for m in BIOMATERIAL_OPTIONS:
|
| 585 |
-
bi_vals.setdefault(m, 0.0)
|
| 586 |
-
|
| 587 |
-
cd = 0.0 if cell_line=="NoCellCultured" else trial.suggest_float(
|
| 588 |
-
"cell_density", dr['min'], dr['max'], step=dr['step']
|
| 589 |
-
)
|
| 590 |
-
|
| 591 |
-
pp_vals = {
|
| 592 |
-
name: trial.suggest_float(
|
| 593 |
-
f"pp__{name}",
|
| 594 |
-
st.session_state.pp_ranges[name]['min'],
|
| 595 |
-
st.session_state.pp_ranges[name]['max'],
|
| 596 |
-
step=st.session_state.pp_ranges[name]['step']
|
| 597 |
-
)
|
| 598 |
-
for name in PRINT_PARAM_NAMES
|
| 599 |
-
}
|
| 600 |
-
|
| 601 |
-
feat = {**bi_vals, **pp_vals}
|
| 602 |
-
feat["Cell Density (cells/mL)"] = cd
|
| 603 |
-
feat.update(
|
| 604 |
-
encoder.transform(pd.DataFrame({"Cell Line":[cell_line]}))
|
| 605 |
-
.iloc[0].to_dict()
|
| 606 |
-
)
|
| 607 |
-
|
| 608 |
-
X = pd.DataFrame([feat])
|
| 609 |
-
Xp = X.reindex(columns=feature_order_print, fill_value=0.0)
|
| 610 |
-
Xc = X.reindex(columns=feature_order_cell, fill_value=0.0)
|
| 611 |
-
|
| 612 |
-
p_proba = model_print.predict_proba(scaler_print.transform(Xp))[0]
|
| 613 |
-
c_proba = model_cell .predict_proba(scaler_cell .transform(Xc))[0]
|
| 614 |
-
exp_p = float(np.dot(p_proba, model_print.classes_.astype(float)))
|
| 615 |
-
exp_c = float(np.dot(c_proba, model_cell .classes_.astype(float)))
|
| 616 |
-
|
| 617 |
-
np.random.seed(42)
|
| 618 |
-
return scaffold_quality_combined(exp_p, exp_c)
|
| 619 |
-
|
| 620 |
-
sampler = optuna.samplers.TPESampler(
|
| 621 |
-
seed=42,
|
| 622 |
-
n_startup_trials=30,
|
| 623 |
-
multivariate=True,
|
| 624 |
-
group=True,
|
| 625 |
-
consider_prior=True
|
| 626 |
-
)
|
| 627 |
-
|
| 628 |
-
study = optuna.create_study(
|
| 629 |
-
direction="maximize",
|
| 630 |
-
sampler=sampler,
|
| 631 |
-
pruner=optuna.pruners.MedianPruner()
|
| 632 |
-
)
|
| 633 |
-
study.optimize(objective, n_trials=300)
|
| 634 |
-
best = study.best_trial
|
| 635 |
-
|
| 636 |
-
st.success(f"🏆 Best WSSQ: **{best.value:.3f}**")
|
| 637 |
-
|
| 638 |
-
best_df = pd.Series(best.params, name="value") \
|
| 639 |
-
.rename_axis("parameter") \
|
| 640 |
-
.to_frame()
|
| 641 |
-
st.table(best_df)
|
| 642 |
-
|
| 643 |
-
st.markdown("## 🧪 Fabrication Procedure")
|
| 644 |
-
with st.spinner("Generating rigorous fabrication procedure…"):
|
| 645 |
-
|
| 646 |
-
formatted_params = "\n".join([
|
| 647 |
-
f"- {k.replace('bio__', 'Biomaterial: ').replace('pp__', 'Print Setting: ')}: {v:.2f}"
|
| 648 |
-
for k, v in best.params.items()
|
| 649 |
-
])
|
| 650 |
-
|
| 651 |
-
prompt = (
|
| 652 |
-
f"Please act as a senior tissue engineer with 15+ years of hands-on experience in 3D bioprinting for regenerative medicine. "
|
| 653 |
-
f"Write a **highly practical, bench-ready laboratory fabrication protocol** for fabricating a scaffold. "
|
| 654 |
-
f"Assume the reader is an experienced experimentalist who routinely works in a tissue engineering lab.\n\n"
|
| 655 |
-
|
| 656 |
-
f"**Use exactly these inputs to tailor every step:**\n"
|
| 657 |
-
f"Target Cell Line: {cell_line}\n"
|
| 658 |
-
f"Parameters:\n{formatted_params}\n\n"
|
| 659 |
-
|
| 660 |
-
f"**Critical requirements for the protocol (you MUST follow all of them):**\n"
|
| 661 |
-
f"• If Target Cell Line is 'NoCellCultured', this is **acellular 3D printing** (not bioprinting). Remove all references to cells, cell viability, cell density, and cell culturing. The final scaffold is cell-free. Change section 6 title to 'Post-Printing Incubation & Storage Instructions' and adapt its content accordingly.\n"
|
| 662 |
-
f"• If any suggested parameter is physically unrealistic (e.g. nozzle diameter 9 µm or syringe temp 2°C) or the nozzle diameter is very small relative to the cell diameter of the target cell line (when cells are used), adjust it slightly in the protocol and explicitly note the adjustment with justification.\n"
|
| 663 |
-
f"• Every quantity must be given in precise, measurable lab units (e.g., 2.5 mL, 1.2 % w/v, 10 mg/mL, 37 °C, 5 min, 150 rpm).\n"
|
| 664 |
-
f"• Include exact timings, temperatures, and workflow order to protect structural fidelity (and cell viability >85 % post-print when cells are used).\n"
|
| 665 |
-
f"• Anticipate and explicitly address common bioprinting pitfalls relevant to the given parameters (nozzle clogging, shear-induced cell death, premature gelation, filament fusion, air bubbles, etc.) and give precise mitigation steps.\n"
|
| 666 |
-
f"• Use only reagents and equipment that are standard in tissue engineering labs; if a specific brand/model is implied by the parameters, note a common equivalent.\n"
|
| 667 |
-
f"• Include simple quality-control checkpoints (visual inspection, live/dead staining timing when cells are used, etc.).\n\n"
|
| 668 |
-
|
| 669 |
-
f"Your response must be structured **exactly** with the following sections (no extra sections, no introductory text, no summary, no conclusions):\n"
|
| 670 |
-
f"1. Required Materials & Equipment\n"
|
| 671 |
-
f"2. Sterilization & Safety Precautions\n"
|
| 672 |
-
f"3. Bioink Preparation\n"
|
| 673 |
-
f"4. 3D Bioprinting Settings & Execution\n"
|
| 674 |
-
f"5. Post-processing & Crosslinking\n"
|
| 675 |
-
f"6. Cell Culturing & Incubation Instructions"
|
| 676 |
-
)
|
| 677 |
-
|
| 678 |
-
resp = client.models.generate_content(
|
| 679 |
-
model="gemini-2.5-flash-lite",
|
| 680 |
-
contents=prompt,
|
| 681 |
-
config=types.GenerateContentConfig(
|
| 682 |
-
system_instruction=(
|
| 683 |
-
"You are a senior tissue engineer and expert experimentalist specializing in translating optimized bioprinting parameters into reproducible, high-viability laboratory protocols. "
|
| 684 |
-
"Your protocols are used daily by PhD students and post-docs in regenerative medicine labs. "
|
| 685 |
-
"You always prioritize: (1) maximum cell viability and function, (2) structural fidelity of the printed construct, (3) workflow efficiency under sterile conditions, and (4) safety. "
|
| 686 |
-
"Write in clear, imperative, step-by-step language with numbered or bulleted sub-steps. "
|
| 687 |
-
"Never be vague — give exact volumes, times, temperatures, speeds, and concentrations. "
|
| 688 |
-
"Never add disclaimers or theoretical background unless explicitly asked."
|
| 689 |
-
),
|
| 690 |
-
temperature=0.1, # lowered for maximum determinism
|
| 691 |
-
top_p=0.85,
|
| 692 |
-
max_output_tokens=6144
|
| 693 |
-
)
|
| 694 |
-
)
|
| 695 |
-
|
| 696 |
-
procedure = resp.text
|
| 697 |
-
st.markdown(procedure)
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import streamlit as st
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import joblib
|
| 5 |
+
import optuna
|
| 6 |
+
import numpy as np
|
| 7 |
+
from catboost import CatBoostClassifier
|
| 8 |
+
|
| 9 |
+
from google import genai
|
| 10 |
+
from google.genai import types
|
| 11 |
+
|
| 12 |
+
from biomaterials import BIOMATERIAL_OPTIONS
|
| 13 |
+
from cell_lines import CELL_LINE_OPTIONS
|
| 14 |
+
|
| 15 |
+
_original_number_input = st.number_input
|
| 16 |
+
|
| 17 |
+
def safe_number_input(label, **kwargs):
|
| 18 |
+
"""
|
| 19 |
+
Clamp `value` into [min_value, max_value] and warn if we had to adjust.
|
| 20 |
+
Then call the real st.number_input with the clamped default.
|
| 21 |
+
"""
|
| 22 |
+
min_value = kwargs.get("min_value", float("-inf"))
|
| 23 |
+
max_value = kwargs.get("max_value", float("inf"))
|
| 24 |
+
value = kwargs.get("value", min_value)
|
| 25 |
+
clamped = min(max(value, min_value), max_value)
|
| 26 |
+
if clamped != value:
|
| 27 |
+
st.warning(
|
| 28 |
+
f"⚠️ Default for “{label}” ({value}) was outside "
|
| 29 |
+
f"[{min_value}, {max_value}]; using {clamped} instead."
|
| 30 |
+
)
|
| 31 |
+
kwargs["value"] = clamped
|
| 32 |
+
return _original_number_input(label, **kwargs)
|
| 33 |
+
|
| 34 |
+
st.number_input = safe_number_input
|
| 35 |
+
|
| 36 |
+
gemini_key = 'kldashgja;fjde;fajed;j'
|
| 37 |
+
# gemini_key = os.getenv("GEMINI_API_KEY")
|
| 38 |
+
if not gemini_key:
|
| 39 |
+
st.error("Gemini API key not found. Please set GEMINI_API_KEY in your Space settings.")
|
| 40 |
+
st.stop()
|
| 41 |
+
|
| 42 |
+
client = genai.Client(api_key=gemini_key)
|
| 43 |
+
|
| 44 |
+
def scaffold_quality_combined(printability, cell_response,
|
| 45 |
+
weight_printability=0.3, weight_cell_response=0.7):
|
| 46 |
+
"""
|
| 47 |
+
Calculates the Weighted Scaffold Synthesis Quality (WSSQ).
|
| 48 |
+
"""
|
| 49 |
+
if printability == 0:
|
| 50 |
+
return 0.0
|
| 51 |
+
|
| 52 |
+
# Normalization
|
| 53 |
+
norm_p = printability / 3.0
|
| 54 |
+
|
| 55 |
+
# If cell_response is 1 (minimum), avoid division by zero in harmonic mean
|
| 56 |
+
if cell_response <= 1:
|
| 57 |
+
return 100 * norm_p
|
| 58 |
+
|
| 59 |
+
norm_c = (cell_response - 1) / 4.0
|
| 60 |
+
|
| 61 |
+
# Weighted Harmonic Mean
|
| 62 |
+
hm = (weight_printability + weight_cell_response) / (
|
| 63 |
+
(weight_printability / norm_p) +
|
| 64 |
+
(weight_cell_response / norm_c)
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
# Weighted Multiplicative Component
|
| 68 |
+
mc = (norm_p**weight_printability) * (norm_c**weight_cell_response)
|
| 69 |
+
|
| 70 |
+
return 100 * ((hm + mc) / 2.0)
|
| 71 |
+
|
| 72 |
+
PRINT_PARAM_NAMES = [
|
| 73 |
+
"Physical Crosslinking Duration (s)",
|
| 74 |
+
"Photo Crosslinking Duration (s)",
|
| 75 |
+
"Extrusion Pressure (kPa)",
|
| 76 |
+
"Nozzle Movement Speed (mm/s)",
|
| 77 |
+
"Nozzle Diameter (µm)",
|
| 78 |
+
"Syringe Temperature (°C)",
|
| 79 |
+
"Substrate Temperature (°C)",
|
| 80 |
+
]
|
| 81 |
+
|
| 82 |
+
@st.cache_resource
|
| 83 |
+
def load_encoder():
|
| 84 |
+
return joblib.load('cell_line_encoder.joblib')
|
| 85 |
+
|
| 86 |
+
@st.cache_resource
|
| 87 |
+
def load_scalers():
|
| 88 |
+
return (
|
| 89 |
+
joblib.load('scaler_printability.joblib'),
|
| 90 |
+
joblib.load('scaler_cell_response.joblib')
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
@st.cache_resource
|
| 94 |
+
def load_models():
|
| 95 |
+
m_p = CatBoostClassifier(); m_p.load_model('catboost_printability.cbm')
|
| 96 |
+
m_c = CatBoostClassifier(); m_c.load_model('catboost_cell_response.cbm')
|
| 97 |
+
return m_p, m_c
|
| 98 |
+
|
| 99 |
+
encoder = load_encoder()
|
| 100 |
+
scaler_print, scaler_cell = load_scalers()
|
| 101 |
+
model_print, model_cell = load_models()
|
| 102 |
+
|
| 103 |
+
feature_order_print = list(scaler_print.feature_names_in_)
|
| 104 |
+
feature_order_cell = list(scaler_cell.feature_names_in_)
|
| 105 |
+
|
| 106 |
+
if 'bio_rows' not in st.session_state:
|
| 107 |
+
st.session_state.bio_rows = [{
|
| 108 |
+
'mat': BIOMATERIAL_OPTIONS[0],
|
| 109 |
+
'min': 0.0, 'max': 10.0, 'step': 0.1
|
| 110 |
+
}]
|
| 111 |
+
|
| 112 |
+
if 'density_range' not in st.session_state:
|
| 113 |
+
st.session_state.density_range = {'min': 0.0, 'max': 10.0, 'step': 0.1}
|
| 114 |
+
|
| 115 |
+
if 'pp_ranges' not in st.session_state:
|
| 116 |
+
st.session_state.pp_ranges = {
|
| 117 |
+
"Physical Crosslinking Duration (s)": {'min': 0.0, 'max': 300.0, 'step': 5.0},
|
| 118 |
+
"Photo Crosslinking Duration (s)": {'min': 0.0, 'max': 180.0, 'step': 5.0},
|
| 119 |
+
"Extrusion Pressure (kPa)": {'min': 5.0, 'max': 200.0, 'step': 5.0},
|
| 120 |
+
"Nozzle Movement Speed (mm/s)": {'min': 1.0, 'max': 20.0, 'step': 0.5},
|
| 121 |
+
"Nozzle Diameter (µm)": {'min': 100.0,'max': 1000.0, 'step': 50.0},
|
| 122 |
+
"Syringe Temperature (°C)": {'min': 4.0, 'max': 40.0, 'step': 1.0},
|
| 123 |
+
"Substrate Temperature (°C)": {'min': 4.0, 'max': 37.0, 'step': 1.0},
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
# --- Sidebar UI for Weights ---
|
| 127 |
+
st.sidebar.header("⚖️ Optimization Weights")
|
| 128 |
+
# User only controls Cell Response (0 to 100)
|
| 129 |
+
w_cell_pct = st.sidebar.slider("Cell Response Weight (%)", min_value=0, max_value=100, value=70, step=5)
|
| 130 |
+
# Printability is dynamically calculated and cannot be changed manually
|
| 131 |
+
w_print_pct = 100 - w_cell_pct
|
| 132 |
+
st.sidebar.number_input("Printability Weight (%)", value=w_print_pct, disabled=True, help="Auto-calculated to ensure sum is 100%")
|
| 133 |
+
|
| 134 |
+
# Convert back to 0.0 - 1.0 for the mathematical formula
|
| 135 |
+
w_cell = w_cell_pct / 100.0
|
| 136 |
+
w_print = w_print_pct / 100.0
|
| 137 |
+
|
| 138 |
+
st.title("🧬 MLATE: Machine Learning Applications in Tissue Engieering")
|
| 139 |
+
st.markdown(
|
| 140 |
+
"<p style='font-size:1.2em; color:grey;'>"
|
| 141 |
+
"A fully integrated Multi-Tissue, machine learning platform for prediction, optimization and generating procedures for fabricating 3D-(bio)printing scaffolds for tissue engineering. "
|
| 142 |
+
"For more details, please refer to and cite our paper: "
|
| 143 |
+
"<a href='https://doi.org/xxx' target='_blank'>https://doi.org/xxx</a>"
|
| 144 |
+
"</p>",
|
| 145 |
+
unsafe_allow_html=True
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
st.subheader("Biomaterials (enter range for each)")
|
| 149 |
+
if st.button("➕ Add Biomaterial"):
|
| 150 |
+
used = {r['mat'] for r in st.session_state.bio_rows}
|
| 151 |
+
available = [m for m in BIOMATERIAL_OPTIONS if m not in used]
|
| 152 |
+
if available:
|
| 153 |
+
st.session_state.bio_rows.append({
|
| 154 |
+
'mat': available[0], 'min': 0.0, 'max': 10.0, 'step': 0.1
|
| 155 |
+
})
|
| 156 |
+
st.rerun()
|
| 157 |
+
|
| 158 |
+
for i, row in enumerate(st.session_state.bio_rows):
|
| 159 |
+
used_except_current = {
|
| 160 |
+
r['mat'] for idx, r in enumerate(st.session_state.bio_rows) if idx != i
|
| 161 |
+
}
|
| 162 |
+
options = [m for m in BIOMATERIAL_OPTIONS if m not in used_except_current]
|
| 163 |
+
|
| 164 |
+
c1, c2, c3, c4, c5 = st.columns([2, 1, 1, 1, 0.3])
|
| 165 |
+
mat = c1.selectbox(
|
| 166 |
+
"", options,
|
| 167 |
+
index=options.index(row['mat']) if row['mat'] in options else 0,
|
| 168 |
+
key=f"bio_mat_{i}"
|
| 169 |
+
)
|
| 170 |
+
st.session_state.bio_rows[i]['mat'] = mat
|
| 171 |
+
|
| 172 |
+
mn = c2.number_input(
|
| 173 |
+
"Min", min_value=0.0, max_value=row['max'],
|
| 174 |
+
value=row['min'], step=row['step'], key=f"bio_min_{i}"
|
| 175 |
+
)
|
| 176 |
+
mx = c3.number_input(
|
| 177 |
+
"Max", min_value=row['step'], max_value=100.0,
|
| 178 |
+
value=max(row['max'], row['step']), step=row['step'],
|
| 179 |
+
key=f"bio_max_{i}"
|
| 180 |
+
)
|
| 181 |
+
st.session_state.bio_rows[i].update(min=mn, max=mx)
|
| 182 |
+
|
| 183 |
+
st.session_state.bio_rows[i]['step'] = c4.number_input(
|
| 184 |
+
"Step", min_value=0.0,
|
| 185 |
+
max_value=(mx - mn) if mx > mn else 0.1,
|
| 186 |
+
value=row['step'], step=0.1, key=f"bio_step_{i}"
|
| 187 |
+
)
|
| 188 |
+
|
| 189 |
+
if c5.button("❌", key=f"rem_{i}"):
|
| 190 |
+
st.session_state.bio_rows.pop(i)
|
| 191 |
+
st.rerun()
|
| 192 |
+
|
| 193 |
+
st.markdown("---")
|
| 194 |
+
|
| 195 |
+
st.subheader("Cell Line & Density (10^6 cells/ml)")
|
| 196 |
+
col1, col2, col3, col4 = st.columns([2,1,1,1])
|
| 197 |
+
|
| 198 |
+
cell_line = col1.selectbox("Cell Line", CELL_LINE_OPTIONS, key="cell_line_select")
|
| 199 |
+
|
| 200 |
+
if cell_line == "NoCellCultured":
|
| 201 |
+
st.info("🧪 **Acellular 3D printing mode** – No cells will be included. Cell density is forced to 0.")
|
| 202 |
+
st.session_state.density_range.update({'min': 0.0, 'max': 0.0, 'step': 0.0})
|
| 203 |
+
|
| 204 |
+
col2.number_input("Min Density", value=0.0, disabled=True, key="cd_min")
|
| 205 |
+
col3.number_input("Max Density", value=0.0, disabled=True, key="cd_max")
|
| 206 |
+
col4.number_input("Step", value=0.0, disabled=True, key="cd_step")
|
| 207 |
+
else:
|
| 208 |
+
if st.session_state.density_range.get('max', 0) <= 0.1:
|
| 209 |
+
st.session_state.density_range.update({'min': 1.0, 'max': 20.0, 'step': 0.5})
|
| 210 |
+
|
| 211 |
+
dr = st.session_state.density_range
|
| 212 |
+
dmin = col2.number_input(
|
| 213 |
+
"Min Density",
|
| 214 |
+
min_value=0.0,
|
| 215 |
+
max_value=dr['max'],
|
| 216 |
+
value=dr['min'],
|
| 217 |
+
step=dr['step'],
|
| 218 |
+
key="cd_min"
|
| 219 |
+
)
|
| 220 |
+
dmax = col3.number_input(
|
| 221 |
+
"Max Density",
|
| 222 |
+
min_value=dr['step'],
|
| 223 |
+
max_value=1000.0,
|
| 224 |
+
value=max(dr['max'], dr['step']),
|
| 225 |
+
step=dr['step'],
|
| 226 |
+
key="cd_max"
|
| 227 |
+
)
|
| 228 |
+
dstep = col4.number_input(
|
| 229 |
+
"Step",
|
| 230 |
+
min_value=0.0,
|
| 231 |
+
max_value=(dmax - dmin) if dmax > dmin else 0.1,
|
| 232 |
+
value=dr['step'],
|
| 233 |
+
step=0.1,
|
| 234 |
+
key="cd_step"
|
| 235 |
+
)
|
| 236 |
+
st.session_state.density_range.update({'min': dmin, 'max': dmax, 'step': dstep})
|
| 237 |
+
|
| 238 |
+
st.markdown("---")
|
| 239 |
+
|
| 240 |
+
st.subheader("Crosslinking Settings")
|
| 241 |
+
|
| 242 |
+
col_cross1, col_cross2 = st.columns(2)
|
| 243 |
+
|
| 244 |
+
disable_physical = col_cross1.checkbox(
|
| 245 |
+
"Disable Physical Crosslinking",
|
| 246 |
+
value=False,
|
| 247 |
+
help="Check if you do not want physical/ionic crosslinking (e.g. CaCl₂ bath, temperature-induced)"
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
disable_photo = col_cross2.checkbox(
|
| 251 |
+
"Disable Photo Crosslinking",
|
| 252 |
+
value=False,
|
| 253 |
+
help="Check if you do not want UV/visible light crosslinking"
|
| 254 |
+
)
|
| 255 |
+
|
| 256 |
+
st.subheader("Printing Parameters (enter range)")
|
| 257 |
+
|
| 258 |
+
for name in PRINT_PARAM_NAMES:
|
| 259 |
+
if name == "Physical Crosslinking Duration (s)" and disable_physical:
|
| 260 |
+
st.session_state.pp_ranges[name].update({'min': 0.0, 'max': 0.0, 'step': 0.0})
|
| 261 |
+
c1, c2, c3, c4 = st.columns([2,1,1,1])
|
| 262 |
+
c1.write(name + " (DISABLED)")
|
| 263 |
+
c2.number_input("Min", value=0.0, disabled=True, key=f"pp_min_{name}")
|
| 264 |
+
c3.number_input("Max", value=0.0, disabled=True, key=f"pp_max_{name}")
|
| 265 |
+
c4.number_input("Step", value=0.0, disabled=True, key=f"pp_step_{name}")
|
| 266 |
+
continue
|
| 267 |
+
|
| 268 |
+
elif name == "Photo Crosslinking Duration (s)" and disable_photo:
|
| 269 |
+
st.session_state.pp_ranges[name].update({'min': 0.0, 'max': 0.0, 'step': 0.0})
|
| 270 |
+
c1, c2, c3, c4 = st.columns([2,1,1,1])
|
| 271 |
+
c1.write(name + " (DISABLED)")
|
| 272 |
+
c2.number_input("Min", value=0.0, disabled=True, key=f"pp_min_{name}")
|
| 273 |
+
c3.number_input("Max", value=0.0, disabled=True, key=f"pp_max_{name}")
|
| 274 |
+
c4.number_input("Step", value=0.0, disabled=True, key=f"pp_step_{name}")
|
| 275 |
+
continue
|
| 276 |
+
|
| 277 |
+
pmin = st.session_state.pp_ranges[name]['min']
|
| 278 |
+
pmax = st.session_state.pp_ranges[name]['max']
|
| 279 |
+
pstep = st.session_state.pp_ranges[name]['step']
|
| 280 |
+
|
| 281 |
+
c1, c2, c3, c4 = st.columns([2,1,1,1])
|
| 282 |
+
c1.write(name)
|
| 283 |
+
|
| 284 |
+
pmin = c2.number_input(
|
| 285 |
+
"Min", min_value=0.0, max_value=pmax,
|
| 286 |
+
value=pmin, step=pstep, key=f"pp_min_{name}"
|
| 287 |
+
)
|
| 288 |
+
pmax = c3.number_input(
|
| 289 |
+
"Max", min_value=pstep, max_value=10000.0,
|
| 290 |
+
value=max(pmax, pstep), step=pstep, key=f"pp_max_{name}"
|
| 291 |
+
)
|
| 292 |
+
pstep = c4.number_input(
|
| 293 |
+
"Step", min_value=0.0,
|
| 294 |
+
max_value=(pmax - pmin) if pmax > pmin else 1.0,
|
| 295 |
+
value=pstep, step=max(1e-3, pstep/10),
|
| 296 |
+
key=f"pp_step_{name}"
|
| 297 |
+
)
|
| 298 |
+
st.session_state.pp_ranges[name].update(min=pmin, max=pmax, step=pstep)
|
| 299 |
+
st.markdown("---")
|
| 300 |
+
|
| 301 |
+
if st.button("🛠️ Optimize WSSQ"):
|
| 302 |
+
with st.spinner("Running Optuna…"):
|
| 303 |
+
def objective(trial):
|
| 304 |
+
bi_vals = {
|
| 305 |
+
r['mat']: trial.suggest_float(
|
| 306 |
+
f"bio__{r['mat']}", r['min'], r['max'], step=r['step']
|
| 307 |
+
)
|
| 308 |
+
for r in st.session_state.bio_rows
|
| 309 |
+
}
|
| 310 |
+
for m in BIOMATERIAL_OPTIONS:
|
| 311 |
+
bi_vals.setdefault(m, 0.0)
|
| 312 |
+
|
| 313 |
+
cd = 0.0 if cell_line=="NoCellCultured" else trial.suggest_float(
|
| 314 |
+
"cell_density", dr['min'], dr['max'], step=dr['step']
|
| 315 |
+
)
|
| 316 |
+
|
| 317 |
+
pp_vals = {
|
| 318 |
+
name: trial.suggest_float(
|
| 319 |
+
f"pp__{name}",
|
| 320 |
+
st.session_state.pp_ranges[name]['min'],
|
| 321 |
+
st.session_state.pp_ranges[name]['max'],
|
| 322 |
+
step=st.session_state.pp_ranges[name]['step']
|
| 323 |
+
)
|
| 324 |
+
for name in PRINT_PARAM_NAMES
|
| 325 |
+
}
|
| 326 |
+
|
| 327 |
+
feat = {**bi_vals, **pp_vals}
|
| 328 |
+
feat["Cell Density (cells/mL)"] = cd
|
| 329 |
+
feat.update(
|
| 330 |
+
encoder.transform(pd.DataFrame({"Cell Line":[cell_line]}))
|
| 331 |
+
.iloc[0].to_dict()
|
| 332 |
+
)
|
| 333 |
+
|
| 334 |
+
X = pd.DataFrame([feat])
|
| 335 |
+
Xp = X.reindex(columns=feature_order_print, fill_value=0.0)
|
| 336 |
+
Xc = X.reindex(columns=feature_order_cell, fill_value=0.0)
|
| 337 |
+
|
| 338 |
+
p_proba = model_print.predict_proba(scaler_print.transform(Xp))[0]
|
| 339 |
+
c_proba = model_cell .predict_proba(scaler_cell .transform(Xc))[0]
|
| 340 |
+
exp_p = float(np.dot(p_proba, model_print.classes_.astype(float)))
|
| 341 |
+
exp_c = float(np.dot(c_proba, model_cell .classes_.astype(float)))
|
| 342 |
+
|
| 343 |
+
np.random.seed(42)
|
| 344 |
+
# Use dynamic weights from the sidebar sliders
|
| 345 |
+
return scaffold_quality_combined(
|
| 346 |
+
exp_p,
|
| 347 |
+
exp_c,
|
| 348 |
+
weight_printability=w_print,
|
| 349 |
+
weight_cell_response=w_cell
|
| 350 |
+
)
|
| 351 |
+
|
| 352 |
+
sampler = optuna.samplers.TPESampler(
|
| 353 |
+
seed=42,
|
| 354 |
+
n_startup_trials=30,
|
| 355 |
+
multivariate=True,
|
| 356 |
+
group=True,
|
| 357 |
+
consider_prior=True
|
| 358 |
+
)
|
| 359 |
+
|
| 360 |
+
study = optuna.create_study(
|
| 361 |
+
direction="maximize",
|
| 362 |
+
sampler=sampler,
|
| 363 |
+
pruner=optuna.pruners.MedianPruner()
|
| 364 |
+
)
|
| 365 |
+
study.optimize(objective, n_trials=300)
|
| 366 |
+
|
| 367 |
+
# Store results in session state to persist after rerun
|
| 368 |
+
st.session_state.best_params = study.best_trial.params
|
| 369 |
+
st.session_state.best_value = study.best_trial.value
|
| 370 |
+
st.session_state.optimized_cell_line = cell_line
|
| 371 |
+
|
| 372 |
+
if 'best_params' in st.session_state:
|
| 373 |
+
st.success(f"🏆 Best WSSQ: **{st.session_state.best_value:.3f}**")
|
| 374 |
+
best_df = pd.Series(st.session_state.best_params, name="value") \
|
| 375 |
+
.rename_axis("parameter") \
|
| 376 |
+
.to_frame()
|
| 377 |
+
st.table(best_df)
|
| 378 |
+
|
| 379 |
+
st.markdown("---")
|
| 380 |
+
st.subheader("📝 Customize Fabrication Protocol")
|
| 381 |
+
user_inquiry = st.text_area(
|
| 382 |
+
"Add specific limitations, equipment, or extra requirements:",
|
| 383 |
+
placeholder="e.g., I only have a 25G nozzle available, or I need to use a specific UV intensity of 10mW/cm²...",
|
| 384 |
+
key="user_inquiry"
|
| 385 |
+
)
|
| 386 |
+
|
| 387 |
+
if st.button("🚀 Generate Fabrication Procedure"):
|
| 388 |
+
with st.spinner("Generating rigorous fabrication procedure…"):
|
| 389 |
+
|
| 390 |
+
formatted_params = "\n".join([
|
| 391 |
+
f"- {k.replace('bio__', 'Biomaterial: ').replace('pp__', 'Print Setting: ')}: {v:.2f}"
|
| 392 |
+
for k, v in st.session_state.best_params.items()
|
| 393 |
+
])
|
| 394 |
+
|
| 395 |
+
# Base prompt (remains unchanged)
|
| 396 |
+
base_prompt = (
|
| 397 |
+
f"Please act as a senior tissue engineer with 15+ years of hands-on experience in 3D bioprinting for regenerative medicine. "
|
| 398 |
+
f"Write a **highly practical, bench-ready laboratory fabrication protocol** for fabricating a scaffold. "
|
| 399 |
+
f"Assume the reader is an experienced experimentalist who routinely works in a tissue engineering lab.\n\n"
|
| 400 |
+
f"**Use exactly these inputs to tailor every step:**\n"
|
| 401 |
+
f"Target Cell Line: {st.session_state.optimized_cell_line}\n"
|
| 402 |
+
f"Parameters:\n{formatted_params}\n\n"
|
| 403 |
+
f"**Critical requirements for the protocol (you MUST follow all of them):**\n"
|
| 404 |
+
f"• If Target Cell Line is 'NoCellCultured', this is **acellular 3D printing** (not bioprinting). Remove all references to cells, cell viability, cell density, and cell culturing. The final scaffold is cell-free. Change section 6 title to 'Post-Printing Incubation & Storage Instructions' and adapt its content accordingly.\n"
|
| 405 |
+
f"• If any suggested parameter is physically unrealistic (e.g. nozzle diameter 9 µm or syringe temp 2°C) or the nozzle diameter is very small relative to the cell diameter of the target cell line (when cells are used), adjust it slightly in the protocol and explicitly note the adjustment with justification.\n"
|
| 406 |
+
f"• Every quantity must be given in precise, measurable lab units (e.g., 2.5 mL, 1.2 % w/v, 10 mg/mL, 37 °C, 5 min, 150 rpm).\n"
|
| 407 |
+
f"• Include exact timings, temperatures, and workflow order to protect structural fidelity (and cell viability >85 % post-print when cells are used).\n"
|
| 408 |
+
f"• Anticipate and explicitly address common bioprinting pitfalls relevant to the given parameters (nozzle clogging, shear-induced cell death, premature gelation, filament fusion, air bubbles, etc.) and give precise mitigation steps.\n"
|
| 409 |
+
f"• Use only reagents and equipment that are standard in tissue engineering labs; if a specific brand/model is implied by the parameters, note a common equivalent.\n"
|
| 410 |
+
f"• Include simple quality-control checkpoints (visual inspection, live/dead staining timing when cells are used, etc.).\n\n"
|
| 411 |
+
f"Your response must be structured **exactly** with the following sections (no extra sections, no introductory text, no summary, no conclusions):\n"
|
| 412 |
+
f"1. Required Materials & Equipment\n"
|
| 413 |
+
f"2. Sterilization & Safety Precautions\n"
|
| 414 |
+
f"3. Bioink Preparation\n"
|
| 415 |
+
f"4. 3D Bioprinting Settings & Execution\n"
|
| 416 |
+
f"5. Post-processing & Crosslinking\n"
|
| 417 |
+
f"6. Cell Culturing & Incubation Instructions\n"
|
| 418 |
+
)
|
| 419 |
+
|
| 420 |
+
# Append user inquiry if provided
|
| 421 |
+
final_prompt = base_prompt
|
| 422 |
+
if user_inquiry:
|
| 423 |
+
final_prompt += f"\n**Additional User Constraints & Inquiries (Integrate these into the protocol):**\n{user_inquiry}"
|
| 424 |
+
|
| 425 |
+
resp = client.models.generate_content(
|
| 426 |
+
model="gemini-2.5-flash-lite",
|
| 427 |
+
contents=final_prompt,
|
| 428 |
+
config=types.GenerateContentConfig(
|
| 429 |
+
system_instruction=(
|
| 430 |
+
"You are a senior tissue engineer and expert experimentalist specializing in translating optimized bioprinting parameters into reproducible, high-viability laboratory protocols. "
|
| 431 |
+
"Your protocols are used daily by PhD students and post-docs in regenerative medicine labs. "
|
| 432 |
+
"You always prioritize: (1) maximum cell viability and function, (2) structural fidelity of the printed construct, (3) workflow efficiency under sterile conditions, and (4) safety. "
|
| 433 |
+
"Write in clear, imperative, step-by-step language with numbered or bulleted sub-steps. "
|
| 434 |
+
"Never be vague — give exact volumes, times, temperatures, speeds, and concentrations. "
|
| 435 |
+
"Never add disclaimers or theoretical background unless explicitly asked."
|
| 436 |
+
),
|
| 437 |
+
temperature=0.1,
|
| 438 |
+
top_p=0.85,
|
| 439 |
+
max_output_tokens=6144
|
| 440 |
+
)
|
| 441 |
+
)
|
| 442 |
+
|
| 443 |
+
st.markdown("## 🧪 Fabrication Procedure")
|
| 444 |
+
st.markdown(resp.text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
biomaterials.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
BIOMATERIAL_OPTIONS = [
|
| 2 |
+
"Alginate (%w/v)",
|
| 3 |
+
"PVA-HA (%w/v)",
|
| 4 |
+
"CaSO4 (%w/v)",
|
| 5 |
+
"Na2HPO4 (%w/v)",
|
| 6 |
+
"Gelatin (%w/v)",
|
| 7 |
+
"GelMA (%w/v)",
|
| 8 |
+
"laponite (%w/v)",
|
| 9 |
+
"graphene oxide (%w/v)",
|
| 10 |
+
"hydroxyapatite (%w/v)",
|
| 11 |
+
"Hyaluronic_Acid (%w/v)",
|
| 12 |
+
"hyaluronan metacrylate (%w/v)",
|
| 13 |
+
"NorHA (%w/v)",
|
| 14 |
+
"Fibroin/Fibrinogen (%w/v)",
|
| 15 |
+
"Pluronic P-123 (%w/v)",
|
| 16 |
+
"Collagen (%w/v)",
|
| 17 |
+
"Chitosan (%w/v)",
|
| 18 |
+
"CS-AEMA (%w/v)",
|
| 19 |
+
"RGD (mM)",
|
| 20 |
+
"TCP (%w/v)",
|
| 21 |
+
"Gellan (%w/v)",
|
| 22 |
+
"bioactive glass (%w/v)",
|
| 23 |
+
"Nano/Methycellulose (%w/v)",
|
| 24 |
+
"PEGTA (%w/v)",
|
| 25 |
+
"PEGMA (%w/v)",
|
| 26 |
+
"PEGDA (%w/v)",
|
| 27 |
+
"Agarose (%w/v)",
|
| 28 |
+
" hyaluronic acid+ Ph moieties (%w/v)",
|
| 29 |
+
"matrigel (%w/v)",
|
| 30 |
+
"CaCl2(mM)",
|
| 31 |
+
"NaCl(mM)",
|
| 32 |
+
"BaCl2(mM)",
|
| 33 |
+
"SrCl2(mM)",
|
| 34 |
+
"CaCO3 (mM)",
|
| 35 |
+
"Genipin (%w/v)",
|
| 36 |
+
"PVA (%wt)",
|
| 37 |
+
"trans-glutaminase (%w/v)",
|
| 38 |
+
"alginate lyase (U/ml)",
|
| 39 |
+
"D-glucose (%w/v)",
|
| 40 |
+
"PLGA (%w/v)",
|
| 41 |
+
"vascular tissued-derived dECM (%w/v)",
|
| 42 |
+
"PEG-8-SH (mM)",
|
| 43 |
+
"Alginate dialdehyde (%w/v)",
|
| 44 |
+
"Alginate sulfate (%w/v)",
|
| 45 |
+
"RGD-modified alginate (%w/v)",
|
| 46 |
+
"poly(N-isopropylacrylamide) grafted hyaluronan (%w/v)",
|
| 47 |
+
"chondroitin sulfate methacrylate (%w/v)",
|
| 48 |
+
"PCL (%w/v)",
|
| 49 |
+
"alginate methacrylate (%w/v)",
|
| 50 |
+
"HRP (U/ml)",
|
| 51 |
+
"Pluronic F127 (%w/v)/Lutrol F127 (%w/v)",
|
| 52 |
+
"Irgacure 2959 (%w/v)",
|
| 53 |
+
"Eosin Y (%w/v)",
|
| 54 |
+
"Ruthenium (mM)",
|
| 55 |
+
"sodium persulfate (SPS) (mM)",
|
| 56 |
+
"HEPES (mM)",
|
| 57 |
+
"LAP (%w/v)",
|
| 58 |
+
"glutaraldehyde (%w/v)",
|
| 59 |
+
"PBS (M)",
|
| 60 |
+
"glycerol (%w/v)",
|
| 61 |
+
"cECM (%w/v)",
|
| 62 |
+
"gel-fu(%w/v)",
|
| 63 |
+
"Rose Bengal (%w/v)",
|
| 64 |
+
"Vitamin B2(%w/v)",
|
| 65 |
+
"VEGF(%w/v)",
|
| 66 |
+
"Polypyrrole:PSS(%w/v)",
|
| 67 |
+
"boratebioactiveglass(%w/v)",
|
| 68 |
+
"astaxanthin(%w/v)",
|
| 69 |
+
"PRP (%v/v)",
|
| 70 |
+
"methacrylated collagen (%w/v)",
|
| 71 |
+
"α-Toc (µM)",
|
| 72 |
+
"ascorbic acid (mM)",
|
| 73 |
+
"Liver dECM(%w/v)",
|
| 74 |
+
"galactosylated alginate (%w/v)",
|
| 75 |
+
"SC-PEG(%w/v)",
|
| 76 |
+
"SFMA-L(%w/v)",
|
| 77 |
+
"SFMA-M(%w/v)",
|
| 78 |
+
"SFMA-H(%w/v)",
|
| 79 |
+
"KdECMMA(%w/v)",
|
| 80 |
+
"BA silk fibronin (%w/v)",
|
| 81 |
+
"Carrageenan(%v)",
|
| 82 |
+
"Carbopol ETD 2020 NF (%w/v)",
|
| 83 |
+
"Carbopol Ultrez 10 NF(%w/v)",
|
| 84 |
+
"Carbopol NF-980(%w/v)",
|
| 85 |
+
"FBS (%v/v)",
|
| 86 |
+
"MeTro (%w/v)",
|
| 87 |
+
"Triethanolamine (%v/v)",
|
| 88 |
+
"PEG-Fibrinogen (%w/v)",
|
| 89 |
+
"polyethylene glycol dimethacrylate (%w/v)",
|
| 90 |
+
"aprotinin (µg/ml)",
|
| 91 |
+
"gold nanorod (mg/mL)",
|
| 92 |
+
"egg white (w/v)",
|
| 93 |
+
"1-Vinyl-2-Pyrrolidione (v/v)",
|
| 94 |
+
"carboxyl functionalized carbon nanotubes (%w/v)",
|
| 95 |
+
"polyHIPE (%w/v)",
|
| 96 |
+
"β-D galactose (mM)",
|
| 97 |
+
"hydrogen peroxide (H2O2) (%v/v)",
|
| 98 |
+
"lactic acid v/v",
|
| 99 |
+
"NorCol (%w/v)",
|
| 100 |
+
"DDT (%w/v)",
|
| 101 |
+
"ammonium persulfate (mM)",
|
| 102 |
+
"diTyr-RGD (mM)",
|
| 103 |
+
"PHEG-Tyr (%w/v)",
|
| 104 |
+
"MMP2-degradable peptide (%w/v)",
|
| 105 |
+
"KdECM (%w/v)",
|
| 106 |
+
"EDC (mg)",
|
| 107 |
+
"NHS (mg)",
|
| 108 |
+
"VA086 (%w/v)",
|
| 109 |
+
"PGS (%w/v)",
|
| 110 |
+
"thiolated HA (%w/v)",
|
| 111 |
+
"boron nitride nanotubes (%w/v)",
|
| 112 |
+
"PEDOT:PSS (ul)",
|
| 113 |
+
"KCl (mM)",
|
| 114 |
+
"skeletal muscle ECM methacrylate (%w/v)",
|
| 115 |
+
"PEO (%w/v)",
|
| 116 |
+
"Carbon dots (mg/ml)",
|
| 117 |
+
"Laminin (ug/ml)",
|
| 118 |
+
"DF-PEG (%w/v)",
|
| 119 |
+
"omenta ECM (%w/v)",
|
| 120 |
+
"thrombin (unit/ml)",
|
| 121 |
+
"Carbon nanotube (CNT) (w/v)",
|
| 122 |
+
"Phytagel(%v)",
|
| 123 |
+
"Laponite-XLG (%w/w)"
|
| 124 |
+
]
|
cell_lines.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
CELL_LINE_OPTIONS = [
|
| 2 |
+
"NoCellCultured",
|
| 3 |
+
"chondrocyteyte",
|
| 4 |
+
"HepG2",
|
| 5 |
+
"bMSCs",
|
| 6 |
+
"HUVECs",
|
| 7 |
+
"NIH3T3",
|
| 8 |
+
"MESCs",
|
| 9 |
+
"hiPSC-CMs /ATCCs",
|
| 10 |
+
"CPCs",
|
| 11 |
+
"L929",
|
| 12 |
+
"Myoblast cells",
|
| 13 |
+
"hiPSCs",
|
| 14 |
+
"HepaRG",
|
| 15 |
+
"hESCs",
|
| 16 |
+
"10T1/2",
|
| 17 |
+
"Cardiac progenitor cells",
|
| 18 |
+
"NSCLC PDX",
|
| 19 |
+
"RAMECs",
|
| 20 |
+
"hASCs",
|
| 21 |
+
"HAVIC",
|
| 22 |
+
"Primary mouse hepatocyte",
|
| 23 |
+
"PTECs",
|
| 24 |
+
"human nasoseptal chondrocytes",
|
| 25 |
+
"PDX",
|
| 26 |
+
"HPFs",
|
| 27 |
+
"U87-MG",
|
| 28 |
+
"ESCs",
|
| 29 |
+
"HASSMC",
|
| 30 |
+
"dermal fibroblasts",
|
| 31 |
+
"MC3T3-E1",
|
| 32 |
+
"Schwann cells",
|
| 33 |
+
"hiPSC-CMs and HS-27A",
|
| 34 |
+
"Saos-2 ",
|
| 35 |
+
"SU3",
|
| 36 |
+
"hTMSCs",
|
| 37 |
+
"HACs",
|
| 38 |
+
"HADMSCs",
|
| 39 |
+
"HeLa",
|
| 40 |
+
"human primary kidney cells",
|
| 41 |
+
"myoblasts",
|
| 42 |
+
"MSCs",
|
| 43 |
+
"human primary kidneycells",
|
| 44 |
+
"293FT",
|
| 45 |
+
"HEK 293FT",
|
| 46 |
+
"Wnt3a-293FT",
|
| 47 |
+
"RSC96/HUVECs",
|
| 48 |
+
"human adipogenic mesenchymal stem cells",
|
| 49 |
+
"HEPG2/ECs",
|
| 50 |
+
"HUVECs/MSCs",
|
| 51 |
+
"RHECs",
|
| 52 |
+
"Human non-small cell lung cancer line Calu-3 (Calu-3)",
|
| 53 |
+
"HL-1",
|
| 54 |
+
"mouse cardiac cells",
|
| 55 |
+
"IMR-90",
|
| 56 |
+
"EPCs",
|
| 57 |
+
"MRC5",
|
| 58 |
+
"rMSC",
|
| 59 |
+
"basil plant cell",
|
| 60 |
+
"hNCs",
|
| 61 |
+
"A549",
|
| 62 |
+
"human induced pluripotent stem cell-derived cardiomyocytes",
|
| 63 |
+
"bMSCs/hACs",
|
| 64 |
+
"EA.hy 926 cells",
|
| 65 |
+
"HepG2/C3A",
|
| 66 |
+
"human epithelial lung carcinoma cells",
|
| 67 |
+
"Human cardiac fibroblasts",
|
| 68 |
+
"hTERT-MSC",
|
| 69 |
+
"cardiomyocytes",
|
| 70 |
+
"Huh7",
|
| 71 |
+
"NRCMs",
|
| 72 |
+
"HCF",
|
| 73 |
+
"Wnt reporter-293FT",
|
| 74 |
+
"neonatal rat ventricular CFs",
|
| 75 |
+
"human coronary artery endothelial cells",
|
| 76 |
+
"hiPSC-CM / fibroblasts",
|
| 77 |
+
"primary mouse hepatocyte",
|
| 78 |
+
"NIH3T3/ HUVECs",
|
| 79 |
+
"murine macrophage-like cell line",
|
| 80 |
+
"Endothelial cells",
|
| 81 |
+
"Human aortic VIC",
|
| 82 |
+
"sADSC",
|
| 83 |
+
"HUVECs/H9C2",
|
| 84 |
+
"neonatal rat ventricular cardiomyocytes",
|
| 85 |
+
"MG-63",
|
| 86 |
+
"Neonatal mouse cardiomyocytes (NMVCMs)",
|
| 87 |
+
"human hepatic stellate cell line",
|
| 88 |
+
"HEK-293",
|
| 89 |
+
"aHSC",
|
| 90 |
+
"MFCs",
|
| 91 |
+
"fibroblasts",
|
| 92 |
+
"HNDF",
|
| 93 |
+
"cardiomyocyte/MSCs",
|
| 94 |
+
"ADSCs",
|
| 95 |
+
"HCASMCs",
|
| 96 |
+
"cardiomyocyte",
|
| 97 |
+
"hCPCs",
|
| 98 |
+
"Human CM /adult human fibroblasts ",
|
| 99 |
+
"primary rat hepatocyte",
|
| 100 |
+
"human cardiac progenitor cells",
|
| 101 |
+
"SMC",
|
| 102 |
+
"Human MSCs",
|
| 103 |
+
"ACPCs",
|
| 104 |
+
"Huh7/HepaRG",
|
| 105 |
+
"Human umbilical vein endothelial cells",
|
| 106 |
+
"ATDC5",
|
| 107 |
+
"hESC-derived HLCs",
|
| 108 |
+
"NIH 3T3",
|
| 109 |
+
"n neonatal mouse ventricular cardiomyocytes",
|
| 110 |
+
"CFs/CMs/HUVECs",
|
| 111 |
+
"MRC-5",
|
| 112 |
+
"VIC",
|
| 113 |
+
"eHep",
|
| 114 |
+
"hUVECs/NIH3T3",
|
| 115 |
+
"MC3T3",
|
| 116 |
+
"HLC",
|
| 117 |
+
"hepatoma",
|
| 118 |
+
"FB",
|
| 119 |
+
"A549 GFP+",
|
| 120 |
+
"HPAAF",
|
| 121 |
+
"PMHs",
|
| 122 |
+
"HUVSMCs",
|
| 123 |
+
"Human CPCs",
|
| 124 |
+
"Fibroblasts/THP-1",
|
| 125 |
+
"rat ventricular cardiomyocytes",
|
| 126 |
+
"iCMs/iCFs/iECs",
|
| 127 |
+
"HUVEC/HHSC",
|
| 128 |
+
"Human CPCs / MSCs",
|
| 129 |
+
"HepaRG/LX-2 ",
|
| 130 |
+
"iCMs/iCFs/iECs/iCMFs",
|
| 131 |
+
"LX-2 ",
|
| 132 |
+
"SMMC-7721",
|
| 133 |
+
"Hepatoblast- single cell/iESC/iMSC",
|
| 134 |
+
"iCMFs",
|
| 135 |
+
"Hepatoblast- spheroid/iESC/iMSC",
|
| 136 |
+
"hBM-MSCs",
|
| 137 |
+
"BMSCs",
|
| 138 |
+
"HUVECs and HHSCs",
|
| 139 |
+
"Intrahepatic cholangiocarcinoma (ICC)",
|
| 140 |
+
"VICs",
|
| 141 |
+
"CMs/CFs",
|
| 142 |
+
"human neonatal dermal fi broblasts",
|
| 143 |
+
"10T1/2 fibroblast-laden cells",
|
| 144 |
+
"human cardiac fibroblasts",
|
| 145 |
+
"neonatal rat ventricular CMs",
|
| 146 |
+
"HUVECs and hiPSC-CS",
|
| 147 |
+
"iPSC-derived CM",
|
| 148 |
+
"Human Umbilical Vein Endothelial Cells + iPSC-derived CM",
|
| 149 |
+
"rabbit bone marrow mesenchymal stem cells",
|
| 150 |
+
"Neonatal rat cardiomyocytes",
|
| 151 |
+
"NIH 3T3 mouse fibroblasts",
|
| 152 |
+
"Human CPCs & MSCs",
|
| 153 |
+
"NSCLC PDX/CAFs",
|
| 154 |
+
"hiPSCs-derived HLCs",
|
| 155 |
+
"U87",
|
| 156 |
+
"75% hepatoblast cells, 20% iEC and 5% iMSC",
|
| 157 |
+
"SMCs",
|
| 158 |
+
"A549/95-D cells",
|
| 159 |
+
"NCI-H441",
|
| 160 |
+
"pancreatic cancer cell",
|
| 161 |
+
"prostate cancer stem cell",
|
| 162 |
+
"human primary parathyroid cells ",
|
| 163 |
+
"primary human hepatocytes",
|
| 164 |
+
"CPCs / MSCs",
|
| 165 |
+
"CPCs ",
|
| 166 |
+
"cardiac fibroblasts",
|
| 167 |
+
"iPSCs-derived cardiomyocytes",
|
| 168 |
+
"cardiomyocytes/ fibroblasts",
|
| 169 |
+
"hiPSC-CM",
|
| 170 |
+
"iPSCs/HUVECs",
|
| 171 |
+
"iPSC-CMs",
|
| 172 |
+
"iPSCs",
|
| 173 |
+
"H1395",
|
| 174 |
+
"PC9",
|
| 175 |
+
"H1650",
|
| 176 |
+
"HULEC-5a",
|
| 177 |
+
"NCI-H1703"
|
| 178 |
+
]
|