File size: 4,448 Bytes
8dff354
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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
#!/usr/bin/env python3
import os, sys, subprocess, shutil, importlib
import gradio as gr

TARGET_DIR = "/home/user/app/external_core"

def run(cmd, **kw):
    subprocess.check_call(cmd, **kw)

def git_probe(url):
    try:
        subprocess.check_output(["git", "ls-remote", "--heads", url], stderr=subprocess.STDOUT)
        return True
    except subprocess.CalledProcessError:
        return False

def build_authed_url(repo_url, token):
    if repo_url.startswith("https://github.com/"):
        return repo_url.replace("https://github.com/", f"https://x-access-token:{token}@github.com/")
    if repo_url.startswith("https://"):
        return "https://x-access-token:{}@{}".format(token, repo_url.split("https://",1)[1])
    return repo_url

def ensure_private_core():
    REPO_URL   = os.getenv("CORE_REPO_URL")
    BRANCH     = os.getenv("CORE_BRANCH")
    TOKEN      = os.getenv("GIT_TOKEN")

    def fresh_clone(url):
        if os.path.isdir(TARGET_DIR):
            shutil.rmtree(TARGET_DIR)
        run(["git", "clone", "--depth", "1", "-b", BRANCH, url, TARGET_DIR])

    if git_probe(REPO_URL):
        fresh_clone(REPO_URL)
    else:
        if not TOKEN:
            raise RuntimeError("Repo requires auth but GIT_TOKEN is missing.")
        authed = build_authed_url(REPO_URL, TOKEN)
        if not git_probe(authed):
            raise RuntimeError("Git auth probe failed for the provided token/permissions.")
        fresh_clone(authed)

    return TARGET_DIR 

def import_callable_from_entry(entry, base_path):
    if base_path not in sys.path:
        sys.path.insert(0, base_path)
    module_name, func_name = entry.split(":", 1)
    mod = importlib.import_module(module_name)
    return getattr(mod, func_name)

CORE_DIR = ensure_private_core()  # returns TARGET_DIR
ENTRY = os.getenv("CORE_ENTRY", "kinetics_core:simulate")  
simulate_fn = import_callable_from_entry(ENTRY, CORE_DIR)

# -------- Gradio UI --------
with gr.Blocks(title="Kinetics Simulator — Solver") as demo:
    gr.Markdown("## Kinetics Simulator — Solver")

    # Centered reaction scheme at 30% width
    gr.HTML("""
    <style>
      /* Center the image container and scale to 30% of the app width */
      #rxn_img { display: flex; justify-content: center; }
      #rxn_img img { width: 30% !important; height: auto !important; margin: auto; }
    </style>
    """)
    gr.Image(value="./reaction.png", show_label=False, interactive=False, elem_id="rxn_img")

    rtol_state = gr.State(1e-8)
    atol_state = gr.State(1e-14)
    ceil_state = gr.State(1e30)

    with gr.Row():
        with gr.Column():
            S0   = gr.Number(value=5e-1, label="Initial S (M)")
            I10  = gr.Number(value=0.0, label="Initial I-1 (M)")
            I20  = gr.Number(value=0.0, label="Initial I-2 (M)")
            P0   = gr.Number(value=0.0, label="Initial P (M)")
            A    = gr.Number(value=2e-13, label="A (M)")
            HD   = gr.Number(value=1e-1, label="HD (M)")

            kHAA1   = gr.Number(value=1e9,  label="kHAA1 (M^-1 s^-1)")
            kHAD1   = gr.Number(value=1e7,  label="kHAD1 (M^-1 s^-1)")
            kHAA2   = gr.Number(value=4e9,  label="kHAA2 (M^-1 s^-1)")
            kHAD2   = gr.Number(value=2e7,  label="kHAD2 (M^-1 s^-1)")
            krearr1 = gr.Number(value=1e6,  label="krearr1 (s^-1)")
            krearr2 = gr.Number(value=1e4,  label="krearr2 (s^-1)")

            T     = gr.Number(value=1e5,  label="Duration (s)")
            Npts  = gr.Number(value=200,  label="Points (t_eval)")

            run_btn = gr.Button("Run Simulation", variant="primary")

        with gr.Column():
            plot = gr.Plot(label="Concentration evolution")
            stats = gr.JSON(label="Summary")

    def run_sim(S0, I10, I20, P0, A, HD,
                kHAA1, kHAD1, kHAA2, kHAD2, krearr1, krearr2,
                T, Npts, rtol, atol, ceil):
        return simulate_fn(
            S0, I10, I20, P0, A, HD,
            kHAA1, kHAD1, kHAA2, kHAD2, krearr1, krearr2,
            T, int(Npts),
            rtol=float(rtol), atol=float(atol), ceil=float(ceil)
        )

    run_btn.click(
        run_sim,
        inputs=[S0, I10, I20, P0, A, HD, kHAA1, kHAD1, kHAA2, kHAD2, krearr1, krearr2, T, Npts, rtol_state, atol_state, ceil_state],
        outputs=[plot, stats],
        api_name="simulate"
    )


if __name__ == "__main__":
    demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")))